1.什么是AMSI
AMSI全称(Antimalware Scan Interface),反恶意软件扫描接口
反恶意软件扫描接口是允许应用程序与反恶意软件产品集成的标准
例如,在可编写脚本的应用程序中,当脚本准备好提供给脚本引擎时,应用程序可以调用Windows AMSI API,请求在执行之前扫描内容。
AMSI有效的原因是,无论代码经过多么复杂的模糊处理和混淆,当脚本需要在脚本宿主中运行时,都必须是明文未经混淆的代码形式执行。比如powershell代码,无论经过多复杂的模糊处理或者编码(比如Base64),但是当需要执行powershell代码是必须要解码之后符合powershell代码规范才能执行。
2.AMSI架构
任何应用程序(消费者)都可以请求扫描内容
任何安全供应商(供应商)都可以注册以接收扫描请求
操作系统是中介程序amsi.dll,必须由任何受amsi保护的应用程序导入
3.受AMSI影响的产品
PowerShell (>2.0)
JavaScript
VBScript
VBA (office macro)
WMI
User Account Control (UAC)elevations
Excel 4.0 macros
Volume shadow copy operations
.NET in-memory assembly loads
4.AMSI函数
语法参数等详细信息可以查看微软官方文档https://learn.microsoft.com/en-us/windows/win32/api/amsi/
函数名 | 作用 |
---|---|
AmsiCloseSession | 关闭由 AmsiOpenSession 打开的会话。 |
AmsiInitialize | 初始化 AMSI API。 |
AmsiNotifyOperation | 向反恶意软件提供程序发送任意操作的通知。 |
AmsiOpenSession | 打开可在其中关联多个扫描请求的会话。 |
AmsiResultIsMalware | 确定扫描结果是否指示应阻止内容。 |
AmsiScanBuffer | 扫描缓冲区中的内容中寻找恶意软件。 |
AmsiScanString | 扫描字符串中的恶意软件。 |
AmsiUninitialize | 删除 AmsiInitialize最初打开的 AMSI API 实例。 |
5.禁用AMSI
断开AMSI链条中的任何一个环节
5.1.应用程序侧的Unhook
各种应用程序是通过AMSI这个接口被检测的,可以通过断开某个应用程序(比如powershell)到AMSI的路线来使这个应用程序(比如powershell)不被AMSI扫描,从而绕过AMSI。
取决于受AMSI保护的应用程序如何使用AMSI,了解应用程序的工作原理,使其在不调用AmsiScanBuffer的情况下执行代码。
5.1.1使用反射
首先了解powershell是如何调用AMSI的
powershell可以通过反射破坏Amsi的初始化相关对象(amsiInitFailed、amsiSession、amsiContext),使其不能正常初始化,从而不对当前进程进行扫描。
(这是2016年提出的概念脚本,现在AMSI会识别并拦截了)
1 | [Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiInitFailed","NonPublic,Static").SetValue($null,$true) |
改变上面的第一个脚本,使用base64绕过(此脚本也已经失效)
1 | [Ref].Assembly.GetType('System.Management.Automation.'+$([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('QQBtAHMAaQBVAHQAaQBsAHMA')))).GetField($([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('YQBtAHMAaQBJAG4AaQB0AEYAYQBpAGwAZQBkAA=='))),'NonPublic,Static').SetValue($null,$true) |
再次修改脚本,全部使用base64绕过(目前可用)
1 | function b64decode { |
最终绕过AMSI效果如下:
(同样有效的脚本)
1 | $w = 'System.Management.Automation.A';$c = 'si';$m = 'Utils' |
5.2.patch AMSI.DLL代码
AMSI的一个主要组件被实现为DLL,该DLL被加载到每个受AMSI保护的进程中,此DLL充当托管PowerShell代码和COM反恶意软件提供程序之间的连接器。因此通过修补AMSI.DLL的代码\数据部分,攻击者可以破坏AMSI链。
5.2.1patch AmsiScanBuffer函数
AmsiScanBuffer()函数扫描充满恶意软件内容的缓冲区,攻击者可以修补AmsiScanBuffer的任何部分(或其调用的其他代码片段),并根据其意愿使其返回AMSI_RESULT值。
(下面示例代码目前可用)
1 | $Win32 = @" |
为什么$Patch = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)?
这是因为在64位下HRESULT:0x80070057 (E_INVALIDARG)的字节码是0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3。而E_INVALIDARG代表传入的参数之一无效。当AmsiScanBuffer因为参数错误而导致返回代码是E_INVALIDARG时,实际扫描结果为 0 即AMSI_RESULT_CLEAN。
详细说明可以去查看这个链接:https://rastamouse.me/memory-patching-amsi-bypass/
5.2.2patch AMSI Context 结构体
AMSI Context 结构体在AmsiInitialize例程期间初始化,存储在AMSI保护的进程内存中,可以通过搜索“AMSI”签名在内存中找到AMSI Context 结构体或找到指向它的全局指针,覆盖此结构将导致AmsiScanBuffer失效。
5.3.COM Server劫持
AMSI提供程序通过在HKLM\Software\Classes\CLSID中创建CLSID条目并在HKLM\SSoftware\Microsoft\AMSI\providers中注册相同的CLSID来注册自己。当AMSI在主机进程中初始化时,它将枚举Providers注册表项中列出的每个CLSID,并通过导入InProcServer32子项中的DLL来初始化COM对象。
有关CLSID可查阅微软文档:https://learn.microsoft.com/en-us/windows/win32/com/clsid-key-hklm
IAntimalwareProvider是构成AMSI主要关键的接口。想要提供反恶意软件服务的每个AMSI提供程序都需要实现lAntimalwareProvider COM接口。
劫持AMSI提供程序COM服务器可能导致绕过AMSI,而且可通过注册表监控轻松检测。
5.4.patch AMSI提供程序
这种方法将导致AMSI初始化过程失败,从而破坏AMSI链。通过修补amsi.dll区域外的非受监控内存来完成
为了理解它,让我们深入到AMSI内部
AMSI Initialization
任何想要使用AMSI服务的提供商都必须调用Amsilnitialize函数,用信息填充HAMSICONTEXT。下面是简化的代码
我们会破坏一些不太直观的东西来保护提供程序本身,修补提供程序的DLL中DllGetClassObject函数的序言字节,并干扰AMSI的初始化过程。
AmsiUninitialize
每个使用AMSI的应用程序都有一个代码,用于取消初始化AMSI,那就是AmsiUninitialize函数。
在PowerShell中,我们可以使用反射来调用此代码。在AmsiUtils类中声明一个名为Uninitialize()的函数,为我们取消初始化AMSI。
5.4.1Patch Microsoft MpOav.dll
基于此原理可以得到一个bypass脚本,下面是一个示例patch Microsoft提供程序的DLL(MpOav.DLL),可以通过适当的查询注册表项来查找所有提供程序的DLL。
其它使用AMSI厂商的可以查看这个链接: https://github.com/subat0mik/whoamsi
(下面示例代码目前仍然有效)
1 | $APIs = @" |
5.4.2替换Add-Type Patch Microsoft MpOav.dll
Add-Type会将代码写入磁盘上的临时文件,然后使用csc.exe 将此代码编译为二进制文件,落地到磁盘上可能导致AV检测
解决方案:反射
Add-Type绕过脚本可以参考: http://redteam.cafe/red-team/powershell/using-reflection-for-amsi-bypass
(下面的示例代码是使用反射来代替Add-Type)
1 | function Get-ProcAddress { |
5.4.3扫描拦截
我们可以拦截AMSIs扫描过程而不是初始化(如经典AmsiScanBuffer patch代码,可以不需要接触amsi.dl就可以完成)
AmsiScanBuffer为每个注册的AMSI提供程序调用IAntimalwareProvider::Scan() 如果提供程序返回的结果不是AMSI_RESULT_NOT_DETECTED \ AMSI_RESULT_CLEAN,则扫描将停止并返回结果,而不调用其余提供程序,比如:AmsiScanBuffer、CAmsiBufferStream、CAmsiAntimalware::Scan。
所以需要找到提供商的扫描函数
调用AmsiInitialize将为我们生成一个新的HAMSICONTEXT,然后将它指向提供程序DLL中的相同扫描函数,我们可以修补每个提供商的 scan函数,所以它将返回而不填写AMSI_RESULT(将保持AMSI_RESULT_CLEAN)。
(下面的示例代码目前仍然可用)
1 | $Apis = @" |
5.5 更多AMSI bypass技术
5.5.1 使用PowerShell版本2
切换powershell版本:powershell -version 2
在脚本中:在脚本开头加入 #requires -version 2
这样如果可以使用2.0,脚本会以2.0执行,如果不能,会按照当前powershell版 本执行
5.5.3 amsi.dll劫持
LoadLibrary函数导入dll的时候没有使用绝对路径,因此程序会首先在当前目录下寻找dll,因此可以在 powershell.exe同目录下(C:\Windows\System32\WindowsPowerShell\v1.0)放一个伪造amsi.dll,就可以实现DLL劫持,而不会调用系统的amsi.dll(C:\Windows\System32\asmi.dll)
dll导入优先级如下:
1 | 进程对应的应用程序所在目录 |
5.5.4 宏代码绕过AMSI
1 | Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr |
6. 总结
powershell绕过方法不适合mimikatz,可以执行powershell版的mimikatz,但是会被杀毒软件(defender等)查杀。
由于AMSI.DLL和提供程序的DLL加载到潜在攻击者所在的相同内存空间,因此破坏操作更容易。
AMSI提供程序的内存以及AMSI.dll内存空间应受到保护
AMSI的Un-initialization可能会让我们找到通过干扰AMSI初始化过程来禁用AMSI的新方法,一种不同于当前干扰AMSI扫描过程的技术。
7. 附几个实用工具
1.使用AMSI分析脚本
将二进制文件作为输入将其拆分,直到它精确定位目标引擎将标记的字节,并将其打印到屏幕上。当试图识别工具/负载中的特定被标记代码时,这会很有帮助。
AmsiScanner实用安全分析:https://github.com/pracsec/AmsiScanner/
ThreatCheck:https://github.com/rasta-mouse/ThreatCheck
2.绕过AMSI
通过将文件分割为多个较小的文件来绕过AMSI,AMSI_Lines:https://github.com/x4sh3s/AMSI_Lines
https://github1s.com/kymb0/Stealth_shellcode_runners
https://github1s.com/boku7/injectAmsiBypass
3.注册AMSI 提供商来做权限维持:https://github.com/netbiosX/AMSI-Provider
4.脚本混淆器:https://github.com/danielbohannon/Invoke-Obfuscation
- 本文作者: ordar
- 本文链接: https://mrwq.github.io/AMSI绕过原理与实践/
- 版权声明: 本文作者: ordar123 转载请注明出处!