原文链接:https://outflank.nl/blog/2020/12/26/direct-syscalls-in-beacon-object-files/
原文作者:Cornelis de Plaa
在这篇文章中,我们将探讨在Cobalt Strike使用BOF进行直接系统调用(direct system calls)。具体来说:
- 说明如何在Cobalt Strike BOF中使用直接系统调用来规避典型的AV和EDR检测。
- 发布InlineWhispers:一个脚本,使在BOF代码中使用Direct Syscalls更加容易。
- 提供概念验证BOF代码,该代码可用于通过修补LSASS进程内存来启用WDigest凭据缓存和规避凭据保护。
PoC的源代码可以在这里找到:https://github.com/outflanknl/WdToggle
InlineWhispers的源代码可以在这里找到:https://github.com/outflanknl/InlineWhispers
Beacon Object Files
Cobalt Strike最近引入了一个新的代码执行概念,叫做 Beacon Object Files(以下简称为BOF)。这使Cobalt Strike可以在Beacon进程中执行一小段已编译的C代码。
这有什么好处?最重要的是,我们摆脱了名为fork&run
的概念。在BOF出来之前,fork&run
是在Cobalt Strike中运行作业的默认机制。这意味着为了执行大多数开发后功能,将启动一个牺牲进程(使用spawnto参数指定),随后将攻击的payload作为反射DLL注入该过程。从AV、EDR的角度来看,它具有可以检测到的各种特征,例如进程产生,进程注入和内存中的反射DLL。在许多现代环境中,fork&run
很容易演变成OPSEC(行动安全Operations security)的灾难。使用BOF,我们可以在Beacon当前过程的上下文中运行与位置无关的已编译代码,这更加隐蔽。
尽管BOF概念的引入对于在后渗透开发中绕过AV、EDR迈出了重大一步,但我们仍然需要面对AV、EDR产品HOOK系统API的问题。在2019年6月,我们发布了有关Direct Syscalls的博客文章,并展示了一个如何使用它绕过AV、EDR软件的示例。到目前为止,我们还没有看到Direct Syscalls在BOF的利用,因此我们决定编写自己的实现并在此博客文章中分享我们的经验。
Direct syscalls和BOF的实用性
现在很多红队都熟悉关于Direct Syscalls对绕过API HOOK的概念。在我们以前对Direct Syscalls的文章中,我们展示了如何在Visual Studio中利用Microsoft汇编程序(MASM)在C/C++项目中包含 Direct Syscalls。当我们构建一个包含汇编代码的Visual Studio项目时,它将使用汇编器和C编译器生成两个目标文件,并将所有片段链接在一起以形成一个可执行文件。
要创建BOF文件,我们使用C编译器来生成单个目标文件。如果要在BOF项目中包含汇编代码,则需要进行内联汇编以生成单个目标文件。不幸的是,Visual Studio不支持x64处理器的内联汇编,因此我们需要另一个C编译器,它支持x64处理器的内联汇编。
Mingw-w64和内联ASM
Mingw-w64 是GCC编译器的Windows版本,可用于创建32位和64位Windows应用程序。它可以在Windows,Linux或任何其他基于Unix的操作系统上运行。最重要的是,它甚至对于x64处理器也支持内联汇编。因此,现在我们需要了解如何在BOF源代码中包含汇编代码。
如果我们看一下Mingw-w64或GCC编译器的手册页,我们会注意到它支持使用-masm=dialect
语法进行汇编。
使用intel语言,我们能够像在Visual Studio中使用Microsoft Assembler一样通过相同的语言编写汇编代码。要将内联汇编包含在我们的代码中,我们可以简单地使用以下汇编器模板语法:
asm("nop \n "
"nop \n "
"nop")
- 起始asm关键字是
asm
或__asm__
- 指令必须用换行符分隔。
有关GCC汇编程序语法的更多信息,请参见以下指南:https://www.felixcloutier.com/documents/gcc-asm.html#assembler-template
从__asm__
到BOF
下面的示例显示使用内联汇编(inline-assembly)的NtCurrentTeb()
的自定义版本。这个例子可用于返回指向当前线程的Thread Environment Block (TEB)的指针,然后可将其用于解析为指向ProcessEnvironmentBlock(PEB)的指针:
为了使此汇编函数在我们的C代码中可用并声明其名称,返回类型和参数,我们使用EXTERN_C
关键字。该预处理程序宏指定该函数在其他位置定义,具有C链接并使用C语言调用约定。这种方法还可以用于在我们的代码中包括汇编系统调用函数。只需将以汇编形式编写的系统调用转换为汇编器模板语法,使用EXTERN_C关键字添加函数定义,然后将其保存在头文件中即可,该文件可以包含在我们的项目中。
尽管在头文件中实现函数是完全有效的,但这并不是最佳实践。然而,使用-o
选项编译目标文件只允许我们使用一个源文件,因此为了不使我们的主源文件带有汇编函数,我们将它们放在单独的头文件中。
要编译包含内联汇编的BOF源代码,我们使用以下编译器语法:
x86_64-w64-mingw32-gcc -o bof.o -c bof.c -masm=intel
WdToggle
为了演示整个概念,我们编写了概念验证代码,其中包括使用内联汇编的Direct Syscalls,并且可以编译为BOF文件。
此代码显示了如何通过在Lsass进程(wdigest.dll模块)中将g_fParameter_UseLogonCredential全局参数切换为1来启用WDigest凭据缓存。此外,可以通过在lsass进程中将g_IsCredGuardEnabled变量切换为0来绕过凭据保护(如果启用)。
这两个技巧都使我们能够在LSASS中使纯文本密码再次可见,因此可以使用Mimikatz抓取它们。使用 UseLogonCredential
补丁后,您只需要用户锁定和解锁其会话即可使明文密码凭据再次可用。
此PoC基于Hydra团队@_xpn_
和@N4kedTurtle
的以下精彩博客文章。这些博客是必读且包含所有必要的详细信息:
这两个博文都包含用于patch LSASS的PoC代码,因此从这个角度来看,我们的代码并不是什么新东西。我们的PoC以此工作为基础,并且仅演示了如何利用BOF中的Direct Syscalls来提供一种更安全的与LSASS流程进行交互并绕过Cobalt Strike的API hook的OPSEC安全方式。
补丁程序限制
此POC是内存补丁,因此重启后必须重新运行代码。此外,wdigest.dll模块中g_fParameter_UseLogonCredential
和g_IsCredGuardEnabled
全局变量的内存偏移量可能会在Windows版本和修订版之间发生变化。我们为代码中的不同内部版本提供了一些偏移,但是这些偏移会在将来的版本中更改。您可以添加自己的版本偏移量,可以使用Windows调试器工具找到该偏移量。(译者注:不同版本的Windows需要patch的偏移量不同)
防御
为了通过LSASS内存访问来检测凭证盗窃,我们可以使用Sysmon之类的工具来监视打开LSASS句柄的处理。我们可以监视访问LSASS进程的可疑进程,从而创建遥测来检测可能的凭证转储活动。
当然,还有更多选项可用于检测凭据盗用,例如,使用Windows Defender ATP之类的高级检测平台。但是,如果您没有使用这些新颖平台的预算,那么Sysmon是可以帮助填补空白的免费工具。
InlineWhispers
在发布直接系统调用博客文章几个月后,@Jackson_T发布了一个名为SysWhispers的出色工具。
“SysWhispers helps with evasion by generating header/ASM files implants can use to make direct system calls”.
“SysWhispers通过生成植入程序可用来进行直接系统调用的header/ASM文件来帮助规避AV”。
这是一个很棒的工具,它可以自动为任何系统调用生成标头/ ASM对,然后可以在定制的Red Teaming工具中使用它。
该工具生成的.asm输出文件可以在Microsoft Studio中使用Microsoft宏汇编器使用。 如果要在BOF项目中使用从SysWhispers输出生成的Direct Syscalls,则需要某种转换,以便它们与汇编器模板语法匹配。
我们的同事@_DaWouw 编写了一个Python脚本,可用于将SysWhispers生成的.asm输出文件转换为适合BOF项目的输出文件。
它转换输出以匹配汇编模板语法,因此可以从您的BOF代码中使用这些函数。我们可以手动输入在BOF中使用了哪些系统调用,以防止包括未使用的系统功能。该脚本可在Github页面上的InlineWhispers存储库中找到:
概要
在此博客中,我们展示了如何在Cobalt Strike BOF中使用Direct Syscalls
。要使用 Direct Syscalls
,我们需要使用汇编程序模板语法编写汇编程序,因此我们可以将汇编函数包括在内联汇编中。Visual Studio不支持x64处理器的内联汇编,但是幸运的是Mingw-w64支持。
为了演示在BOF中进行Direct Syscalls的用法,我们编写了概念验证代码,可用于启用WDigest凭据缓存。此外,我们编写了一个名为InlineWhispers的脚本,该脚本可用于将SysWhispers生成的.asm输出转换为适合BOF项目的内联汇编头文件。
我们希望该博文有助于理解如何在BOF项目中实施Direct Syscalls以提高OPSEC安全性。