导语:Microsoft的反恶意软件扫描接口(AMSI)在Windows 10中被引入,作为标准接口,它可以让AV引擎将签名应用于内存和磁盘上的缓冲区。
Microsoft的反恶意软件扫描接口(AMSI)在Windows 10中被引入,作为标准接口,它可以让AV引擎将签名应用于内存和磁盘上的缓冲区。这使得AV产品能够在脚本解释之前“hook”,这意味着任何经过混淆或加密的PS程序都经过了解密程序的解密。你可以在这里和这里阅读有关AMSI的更多信息。这篇文章将介绍一种通过劫持COM服务器来绕过AMSI的方式,并分析Microsoft是如何在build#16232 中修复它的,然后我又是如何绕过该修复的。
这个问题在5月3日提交给了微软,并且已经得到修复,并发布了Build#16232中的“深度防御”补丁。
下面是一个通过PowerShell进行的AMSI测试示例,当AMSI接受暴露的脚本块并将其传递给Defender进行分析时,内容如下:
正如你所看到的,AMSI接受了代码并将其传递给被调用的Invoke-Expression。由于该代码被认为是恶意的,因此被阻止执行。
那么问题来了:这个过程是如何工作的? 查看amsi.dll的导出,可以看到AMSI导出的各种函数调用:
amsi!DllGetClassObject和amsi!DllRegisterServer很快引发我的思考,因为这些都是COM的入口点,用于方便的实例化一个COM对象。幸运的是,COM服务器很容易劫持,因为中等完整性流程默认在查找HKCR / HKLM之前搜索当前用户注册表配置单元(HKCU)以用于COM服务器。
查看IDA,我们可以看到COM接口ID(IID)和ClassID(CLSID)传递给CoCreateInstance():
我们可以通过查看ProcMon的结果来验证这一点:
最终发生的事情是,AMSI的扫描功能似乎是通过自己的COM服务器来实现的,当COM服务器被实例化时会被暴露出来。当AMSI加载时,它实例化其自有的COM组件,并暴露了诸如amsi!AmsiOpenSession,amsi!AmsiScanBuffer,amsi!AmsiScanString和amsi!AmsiCloseSession之类的方法。如果我们强制COM实例化失败,AMSI将无法访问扫描恶意内容所需的方法。
由于COM服务器首先通过HKCU配置单元进行解析,因此普通用户可以劫持InProcServer32密钥并注册不存在的DLL(或者如果你也可以执行恶意的代码)。为了做到这一点,有两个注册表项需要处理:
当AMSI尝试实例化其COM组件时,它将查询其注册的CLSID并返回不存在的COM服务器。这导致加载失败,并阻止任何扫描方法被访问,最终使AMSI不可用。
你可以看到,导入上述注册表更改将导致“C: IDontExist”作为COM服务器返回:
现在,当我们尝试运行我们的“恶意”AMSI测试样本时,你将注意到它被允许执行,因为AMSI无法通过其COM接口访问任何扫描方法:
你可以在这里找到注册表更改:
https://gist.github.com/enigma0x3/00990303951942775ebb834d5502f1a6
现在我们可以理解这个bug了,那么我们来看看微软是如何在build#16232中修复它的。由于amsi.dll也是AMSI的COM服务器,因此将这两个DLL分开似乎是一个很好的办法。看到diff,AmsiInitialize函数突出显示,因为它可能包含了实际实例化AMSI的逻辑。
在左侧,是原来的AMSI DLL,在右边的是新更新的AMSI DLL。如你所见,Microsoft似乎删除了对CoCreateInstance()的调用,并将其替换为直接调用DllGetClassObject()。CoCreateInstance()可以定义为用于实例化使用CoGetClassObject()实现的COM对象的高级函数。在分辨完成(部分通过注册表CLSID查找)和定位COM服务器之后,服务器的导出函数“DllGetClassObject()”将被调用。通过直接调用amsi.dll的DllGetClassObject()函数替换CoCreateInstance,避免了注册表解析。由于AMSI不再在COM服务器的注册表中查询CLSID,因此我们无法再劫持它。
现在我们知道了修复过程,那么我们该如何去绕过它呢?在进行绕过之前,重要的是我们要明白,自2006年以来,这个特定的bug已被公布和讨论。基本上,脚本解释器(如PowerShell)从工作目录加载amsi.dll,而不是从安全路径(如System32)加载它。由于这个原因,我们可以将PowerShell.exe复制到我们可以写入的目录,并加载易受攻击的amsi.dll版本。基于这一点,我们可以利用bat劫持DLL,或者我们可以创建相同的注册表项来劫持AMSI的COM组件。由于这个易受攻击的AMSI版本仍然会调用CoCreateInstance(),我们可以再次劫持注册表搜索顺序。
首先,我们可以通过为powershell.exe和AMSI的CLSID创建一个ProcMon过滤器来验证修补后的amsi.dll版本不通过注册表查询COM服务器。当PowerShell启动时,你将注意到没有任何结果出现:
接下来,我们删除易受攻击的AMSI DLL并将PowerShell移动到同一目录。如你所见,现在正在查询注册表来定位AMSI的COM服务器:
使用易受攻击的AMSI DLL,我们现在可以执行COM服务器劫持:
检测方案:尽管在构建#16232补丁中进行了修复,但仍然可以通过使用旧的,易受攻击的AMSI DLL执行DLL劫持来执行此操作。为了检测这种攻击手法,监视对于在其正常目录之外执行的任何二进制文件(wscript,cscript,PowerShell)(通过命令行日志记录等)是比较理想的方法。由于绕过修复程序需要将二进制文件移动到用户可写位置,所以在非标准位置执行这些命令就可以抓住这一攻击行为。