探索Cobalt Strike shellcode是由编译后的可执行.exe文件加载情况,这将需要使用调试器(x64dbg)和静态分析(Ghidra)来执行完整的分析。
可执行文件是编译后的exe,包含隐藏和混淆的Shellcode,使用一个简单的异或例程和一个4字节的项对shellcode进行解码,然后将其写入一个用VirtualAlloc创建的简单缓冲区。
本文将探索使用调试器获得解码的shellcode的方法,然后寻找使用Ghidra手动定位shellcode和相关解密密钥的方法,还将研究在X64dbg和Ghidra之间切换的方法,以及使用ChatGPT识别和分析Ghidra输出的方法。
点击下载样本(pw:infected)。
SHA256: 99986d438ec146bbb8b5faa63ce47264750a8fdf508a4d4250a8e1e3d58377fd
我们可以先把文件保存到一台分析机上然后用感染的密码解压缩。从这里我们还可以创建一个文件名较短的副本。
由于该文件是已编译的可执行文件,我们可以尝试使用调试器对其进行分析。在本文中为x64dbg。
我们可以继续使用x64dbg打开文件,一直点击直到到达入口点。
现在,我们可以继续在API上创建一些断点,这些断点通常(但并不总是)在恶意软件解包时使用。
我们可以通过运行bp VirtualAlloc和bp VirtualProtect来创建2个断点。
创建断点后,我们可以继续并允许恶意软件继续(F9)。恶意软件将继续运行并触发VirtualAlloc上的断点。
我们的主要目的是获取由VirtualAlloc创建的缓冲区,我们可以通过使用Execute Until Return来实现这一点。“Execute Until Return”将允许VirtualAlloc函数完成,但不允许发生任何进一步的操作。这意味着我们可以很容易地获得创建的缓冲区的地址。
在点击execute之后,返回。我们可以在RAX内部观察到新创建的缓冲区地址。
我们想继续监控这个缓冲区的可疑内容和解压缩的恶意软件时,可以通过右键点击RAX中包含的地址来开始监控过程。
现在我们可以选择Follow in Dump,这将打开左下角窗口中缓冲区的内容。
通过点击“Follow In Dump”,我们可以在左下角的窗口中观察到转储的内容。
我们可以在这里注意到缓冲区是空的,只包含00。
VirtualAlloc已经创建了一个空缓冲区,现在,我们可以通过创建一个硬件断点来监控这个缓冲区的变化。
硬件断点可以通过选择内存转储中的第一个字节以及RightClick->Breakpoint->Hardware,Access->Byte来创建。
这样我们可以允许恶意软件继续执行。可以看到硬件断点被触发,在缓冲区的第一部分中包含一个FC字节。前两篇文章中已经讲过FC是shellcode中非常常见的第一个字节。
此时,我们希望恶意软件继续填充缓冲区。
我们可以继续使用另一个Execute Until Return。这样缓冲区就会被填满,我们就可以监控里面的内容了。
下面我们可以看到填充后的缓冲区。可以看到第一个字节是0xFC,并且在初始字节中有一个wininet字符串,这可能表示shellcode。
现在我们有了一个合理的假设,即缓冲区包含shellcode,我们可以继续尝试使用X64dbg对其进行反汇编。如果我们反汇编代码并且没有明显的错误,那么很有可能正在查看shellcode。
我们可以通过在反汇编器中选择第一个FC字节和Follow in Disassembler来实现这一点。
X64dbg现在将尝试从缓冲区中反汇编字节。
下面,我们可以在顶部的反汇编窗口中观察到被反汇编的缓冲区。可以发现,似乎没有明显的错误,并且有有效的函数调用,循环和总体“正常”的指令。
使用SpeakEasy仿真器进行最终验证
由于非常怀疑缓冲区包含shellcode,所以我们可以继续使用Speakeasy来模拟它。
我们也可以用X64dbg实现同样的事情,但是对于shellcode来说,这是一个更复杂的过程。也可以用X64dbg实现同样的事情,但是对于shellcode,这也是一个复杂的过程。
要使用speakeasy模拟shellcode,我们首先需要保存它。
我们可以选择我们的第一个FC字节,右键单击然后Follow in Memory Map。
现在我们可以将内存缓冲区保存到一个文件中,将文件保存为memdump.bin。
用Speakeasy模拟未打包的Shellcode
现在将shellcode缓冲区保存到文件memdump.bin中。我们可以继续使用Speakeasy来模拟shellcode。
我们可以使用speakeasy-t memdump.bin-r-ax64命令来做到这一点:
speakeasy-运行speakeasy工具;
-t-我们要使用哪个文件;
-r-(Raw)-表示我们正在使用shellcode;
-ax64-表示我们的文件包含64位指令。我们知道这是因为我们使用的是x64dbg而不是x32dbg。
运行此命令后,将成功地模拟shellcode,并向我们提供有关其功能的大量信息。
Speakeasy输出显示了一个C2地址
可以看到对User-Agent:Mozilla/4.0(compatible;MSIE8.0;WindowsNT5.1;Trident/4.0;InfoPath.2;.NETCLR2.0.50727)\r\n的用户代理的引用。
如果有可用的代理日志,这个用户代理将是查找代理日志的好地方。
在Ghidra中查找Shellcode解密函数
在第一次触发硬件断点时,主要可执行文件可能位于解密函数的中间。我们可以使用这些信息在Ghidra中查找相同的解密函数。
在Ghidra中查找Shellcode解密函数;
用ChatGPT识别解密例程逻辑;
使用Ghidra识别解密密钥;
利用熵定位加密shell;
使用Cyberchef执行手动解码;
使用解密字节查找其他样本;
使用解密代码创建Yara Rule 。
参考及来源:https://embee-research.ghost.io/unpacking-malware-with-hardware-breakpoints-cobalt-strike/