华硕供应链攻击事件技术分析
41yf1sh 嘶吼专业版
一、概述
3月,卡巴斯基发布了一份关于“ShadowHammer恶意活动”的供应链攻击报告,该报告中描述了攻击者如何通过ASUS Live Update Utility分发目标恶意软件,从而实现对华硕的攻击。本文主要对本次攻击中所使用的第一阶段Payload进行详细分析。
二、事件回放
2019年3月25日,卡巴斯基发布了一篇针对华硕供应链攻击的高级别报告。在报告中,卡巴斯基指出:“在2019年1月,我们发现了与ASUS Live Update Utility相关的复杂供应链攻击。该攻击发生在2018年6月至11月之间,根据我们的遥测,此次攻击影响了大量用户。”
攻击的目标是一个未知的用户池,这些用户的网络适配器MAC地址已经被识别出来。为了实现这一目标,攻击者在木马化的样本中硬编码了一份MAC地址列表,该列表用于识别此次大规模行动的实际目标。
在最初的报告中,包含了一些有用的信息,但技术细节在早期受到了限制,无法过多披露。为了了解有关该攻击的更多信息,我们决定进一步分析Payload。
三、历史活动
在卡巴斯基的文章中,引用了一个ZIP文件,该文件是ASUS Live Update Utility的副本。在这个ZIP文件中,有三个文件,两个MSI和一个名为Setup.exe的文件。通过在VirusTotal上查看这些文件的历史记录并检查文件本身,我们发现,攻击者已经将Shellcode插入到合法的Setup.exe中,并修改代码,从而实现重定向执行。
我们分析了VirusTotal的历史样本,以便更好地了解攻击者的行为。根据卡巴斯基的报告,这次攻击发生在2018年6月至11月,我们在VirusTotal上提交的样本中得到了验证。第一个恶意样本在2018年6月29日被提交,而最近的样本是在2018年11月17日提交。
我们对这些样本进行了深入地分析,发现其中至少部署了两种不同的后门变体。从6月到9月,攻击者使用未经编码的Payload以及修补后的WinMain来重定向执行。
从9月开始,一个更加隐蔽的后门被攻击者部署,其中包括一个混淆后的Shellcode Payload和解码器,通过函数_crtExitProcess执行。我们发现所有样本都使用相同的C&C通道,即asushotfix[.]com,该域名在2018年5月5日首次注册,其对应的IP地址是141.105.71[.]116,位于俄罗斯。
通过对这一IP地址进行查询,还发现了以下其他域名:
· host2[.]infoyoushouldknow[.]biz(2013年4月27日首次发现)
· nano2[.]baeflix[.]xyz(2016年3月24日首次发现)
· asushotfix[.]com(2018年5月22日首次发现)
· www[.]asushotfix[.]com(2018年7月13日首次发现)
· homeabcd[.]com(2018年9月5日首次发现)
· simplexoj[.]com(2018年9月11日首次发现)
目前暂不清楚这些域名发挥了什么样的作用,但很有可能它们也被用于此次针对华硕的攻击之中,或者同一攻击者发动的其他攻击。
在接下来的部分中,我们将深入分析卡巴斯基报告中引用的一个样本(MD5:55a7aa5f0e52ba4d78c145811c830107),其中包含经过混淆后的Payload。
四、加载Shellcode
在更高的一个级别来看,Setup.exe二进制文件似乎是一个合法的文件。该文件已经经过签名,元信息也与合法的文件相匹配,大多数代码符合其他合法安装文件的模式。但是,在将实际的合法Setup.exe与恶意Setup.exe进行比较后,我们发现,其中的代码已经被修补,执行流将从_crtCoreExitProcess转移到新函数。
我们将这个新函数重命名为drop_shellcode。其中,包含提取、解码和执行嵌入式Payload的代码。通过在ExitProcess之前,将要转移的内容放置在Setup.exe文件的末尾,就能够确保合法文件按照预期执行,从而降低被发现的可能性。
我们对Shellcode投放的函数进行分析,发现它首先通过VirtualAlloc调用在Setup.exe进程中分配的内存,然后将嵌入的Shellcode复制到已经分配的内存中:
有趣的是,第一步仅在解码之前,就将Payload的前16个字节复制到了内存之中。这些字节中,实际上包含Payload的大小,然后传递给第二个VirtualAlloc调用。随后,编写、解码和执行主Shellcode。
在本文中,不会对解码例程进行重点分析,Winnti之前已经使用过类似的代码。
五、分析Shellcode
根据我们截止到目前的分析,Shellcode会执行以下操作:
- 解析后需要调用的库函数:
(1) 遍历PEB中的结构,并检查k、l、点(.)字符,以匹配模块名称,可以找到第一个Kernel32的基址。
(2) 解析模块PE表,查找导出表。
(3) 使用函数对自定义函数进行哈希计算,并在每次迭代时导出该值以验证是否匹配。
(4) 以同样的方式,找到其他模块中的函数,使用LoadLibraryExW获取基址。该函数是位于Kernel32中的第一个函数。
-
通过调用IPHLPAPI.GetAdaptersAddresses获取主机的MAC地址。
-
将MAC地址进行MD5哈希运算。
-
将MD5哈希值与硬编码的列表进行比较。如果没有找到匹配项,则会将一个神秘的IDX文件投放到磁盘。
-
如果MAC地址匹配,则会使用代理感知(Proxy-aware)API调用,从特定URL下载第二阶段Payload。该过程会直接进入RWX内存中,并且会被调用。
下面是关于每个步骤的详细细节。
5.1 解析函数
Shellcode首先需要找到它想要使用的一些库函数,这主要分为两个步骤。首先,在从多个DLL中解析其他函数之前,它会首先从KERNEL32.DLL中寻找LoadLibraryExW和GetProcAddress,LoadLibraryExW的地址将在第二阶段使用。
第一步,需要获得Kernel32.dll的基址。为了找到基址,使用线程信息块(TIB)作为导航的结构,最终定位到InInitializationOrderModuleList,也就是包含在此过程中加载的模块的列表。
具体而言,需要到达的结构是:
TIB -> PEB -> Ldr -> InInitializationOrderModuleList
事实上,InInitializationOrderModuleList的类型是_LIST_ENTRY,这是一个双链表,其Flink随后会遍历模块的这一列表。在每个条目中,都包含一个BaseDllName字段,并且会在每个条目中检查该字段,以检验其是否与Kernel32.dll匹配。
但是,由于进行了混淆,过程中并不会检查名称是否为Kernel32.dll。相反,它们会在该字符串的适当位置,检查是否有k、l和点(.)的存在。事实上,只会检查每2字节Unicode字符中的第一个字节,在实践中这非常有效,但显然不是比较Unicode字符的常规方式。
整个过程可以在下面的注释代码中看到:
在找到Kernel32.dll条目之后,可以读取其DllBase字段,找到模块的基址。在这里,它与Shellcode中的函数一起使用,该函数接受模块基址和函数名的自定义哈希值。该函数解析内存中模块的PE标头,从而找到导出表。然后,将遍历每个导出项,并针对其名称运行一个简单的自定义函数(类似于哈希运算)。当找到匹配的哈希值时,证明目标函数已经位于导出表中,就无需在代码中再直接包含函数名称。函数的地址也将从导出表中保存,以便后续使用。
该导出表搜索如下所示,灰色标记的哈希值:
函数解析的第二步,是使用相同的Shellcode例程,在导出表中查找哈希运算后的函数名称。但是因为他要调用其他几个DLL,所以使用的是LoadLibraryExW,在第一步中就已经获得模块的基址。
下面是在代码中找到的所有其他函数名称对应的哈希值,并且针对其对应的模块和函数名称进行了标注。
这些函数地址被保存在其余代码的一个结构中,该结构可以通过寄存器基址访问。要查看调用的函数,可以使用以下偏移量:
0x4 kernel32.VirtualAlloc
0x8 kernel32.GetModuleFileNameW
0xC kernel32.WritePrivateProfileStringW
0x10 kernel32.GetSystemTimeAsFileTime
0x14 kernel32.FileTimeToSystemTime
0x18 kernel32.VirtualFree
0x1C ntdll.memcpy
0x20 ntdll.memcmp
0x24 ntdll.memset
0x28 ntdll.swprintf
0x2C ntdll.sprintf
0x30 ntdll.strncat
0x34 ntdll.MD5Init
0x38 ntdll.MD5Update
0x3C ntdll.MD5Final
0x40 IPHLPAPI.GetAdaptersAddresses
0x44 wininet.InternetOpenA
0x48 wininet.InternetOpenUrlA
0x4C wininet.InternetQueryDataAvailable
0x50 wininet.InternetReadFile
0x4 kernel32.VirtualFree
如果了解这些偏移量,并对它们进行定义,可能会使代码更具可读性。我们从这里开始:
然后:
如果大家认为这有所帮助,在这里提供了一些Python代码,可以自动产生这些哈希值,并找到与实际函数名称匹配的项目(适当进行了简化):
import numpy
# We expect, and require, that int_scalars overflow occurs, so ignore
numpy.warnings.filterwarnings('ignore')
find_hashes = [
0x431A42C9, 0x0C2CBC15A, ... function hashes ...
]
names = [ ... list of exported functions in target DLLs ... ]
hashes_2s_compliment = {}
for hash in find_hashes:
twoscomp = hash
if twoscomp >= 1<<31: twoscomp -= 1<<32
hashes_2s_compliment[twoscomp] = hash
mul_by = numpy.int32(0x21)
for name in names:
name_hash = numpy.int32(0)
for char in name:
name_hash = name_hash * mul_by
name_hash += numpy.int32(ord(char))
if name_hash in hashes_2s_compliment:
print('{}: {}'.format(hex(hashes_2s_compliment[name_hash]), name))
5.2 MAC地址
有了这些函数,Shellcode可以继续工作,继续进入到MAC验证的阶段。在这里,我们发现它通过调用Shellcode中的函数(我们将该函数称为get_macs_and_md5)来获取主机上MAC地址的MD5哈希值。该调用将进行两次。第一个,是获取MAC地址的数量,以帮助它分配适当的内存量,来存储所有MD5哈希值。第二次,是实际生成并存储MD5哈希值。
通过使用AF_UNSPEC调用GetAdaptersAddresses获取MAC地址,可以获取所有接口。
而实际的MD5调用如下:
然后根据硬编码到Shellcode中的一组哈希值,检查这些MD5哈希值,如下所示:
这展示了“MAC地址是否匹配”的分支条件,位于Shellcode中入口函数的末尾:
5.3 第二阶段Payload的下载和执行
如果存在MAC地址匹配,那么Shellcode将继续从网络中下载第二阶段。用于该阶段的URL被硬编码为一组常量值(低字节序),因此,当强制显示为ASCII时,如下所示:
其中,包含了URL:
https://asushotfix[.]com/logo2[.]jpg
我们使用代理感知功能,访问这一URL:
数据从URL直接下载到分配了读/写/执行的内存区域,最后调用第二阶段的代码:
在分析的过程中,回调的URL不再提供第二阶段Payload。我们可能会在未来几周内提供更多信息。
六、ShadowHammer的检测
作为防御方,可以搜索几个指标,包括文件哈希、投放的文件,以及基于网络的IoC。
6.1 SHA-256
· bca9583263f92c55ba191140668d8299ef6b760a1e940bddb0a7580ce68fef82(2018年6月)
· 6aedfef62e7a8ab7b8ab3ff57708a55afa1a2a6765f86d581bc99c738a68fc74(2018年7月)
· ac0711afee5a157d084251f3443a40965fc63c57955e3a241df866cfc7315223(2018年7月)
· e78e8d384312b887c01229a69b24cf201e94997d975312abf6486b3363405e9d(2018年9月)
· 736bda643291c6d2785ebd0c7be1c31568e7fa2cfcabff3bd76e67039b71d0a8(2018年9月)
· 9bac5ef9afbfd4cd71634852a46555f0d0720b8c6f0b94e19b1778940edf58f6(2018年9月)
· 9a72f971944fcb7a143017bc5c6c2db913bbb59f923110198ebd5a78809ea5fc(2018年10月)
· 357632ee16707502ddb74497748af0ec1dec841a5460162cb036cfbf3901ac6f(2018年10月)
· 9842b08e0391f3fe11b3e73ca8fa97f0a20f90b09c83086ad0846d81c8819713(2018年11月)
6.2 投放的文件
对于MAC地址不匹配的系统,将在Setup.exe的向上两级目录中创建一个idx文件,例如:
· C:\Program Files (x86)\ASUS\ASUS Live Update\Temp\6\Setup.exe · C:\Program Files (x86)\ASUS\ASUS Live Update\idx.ini
6.3 网络
· host2[.]infoyoushouldknow[.]biz
· nano2[.]baeflix[.]xyz
· asushotfix[.]com
· www[.]asushotfix[.]com
· homeabcd[.]com
· simplexoj[.]com
· 141.105.71[.]116
· hxxps://asushotfix[.]com/logo[.]jpg
· hxxps://asushotfix[.]com/logo2[.]jpg
6.4 PDB指标
2018年6月样本:D:\C++\AsusShellCode\Release\AsusShellCode.pdb
七、总结
ShadowHammer攻击是供应链攻击的一个典型案例,攻击者滥用可信的更新实用程序,以有针对性的方式在全球范围内分发恶意软件。正如卡巴斯基的分析中所提到的,该攻击与BARIUM恶意组织所执行的攻击具有相似之处,这表明其行动规模和复杂程度与过去一致,或者有所升级。
从防御的角度来看,从发现这起袭击所花费的大量时间可以看出,在攻击的第一阶段往往是难以察觉的。但是,随着有关第二阶段Payload的更多信息被公开,很可能会发现一些噪音指标。
为了具有实时监控和回顾监测的能力,我们强烈建议组织使用EDR代理部署终端监控与响应,从而提供抵御此类威胁所需的可见性和控制能力。
八、参考文章
[1] https://securelist.com/operation-shadowhammer/89992/
[2] https://www.virustotal.com/#/file/9a72f971944fcb7a143017bc5c6c2db913bbb59f923110198ebd5a78809ea5fc/detection
[3] https://www.vkremez.com/2019/03/lets-learn-dissecting-operation.html