概述
本文将主要针对PNG Dropper恶意软件进行分析,这一恶意软件是由Turla组织开发和使用的。PNG Dropper在2017年8月被Carbon Black研究人员首次发现。早在2017年,该恶意软件就被用来分发Snake。但是最近,NCC的研究人员发现了一个带有新型Payload的样本,我们在内部将其命名为RegRunnerSvc。
值得注意的是,这种威胁还有其他我们无法获得的组成部分。其中,有一个第一阶段的Dropper,将会投放并安装PNG Dropper(RegRunnerSvc)。我们非常有必要记录PNG Dropper的这种新用途。
PNG Dropper
关于PNG Dropper组件的详细信息,已经由Carbon Black研究团队进行了记录,但为了清晰起见,我们在这里将对其进行简要描述,并说明其工作原理。
Dropper的目的是加载并运行隐藏在众多PNG文件中的PE文件。上图展示了Dropper的资源。在这里,我们可以看到一个名为“PNG”的二进制数据的资源项。所有这些资源都有有效的PNG文件,可以使用任意支持这种文件格式的图像查看器打开,但在打开后,我们无法看到任何有意义的图像内容。举例来说,打开其中的一个文件,我们只能看到其中的几个彩色像素(如下图放大版本所示)。
PNG文件使用Microsoft的GDI+库加载。在下图中,我们看到其调用了LockBits,用于从PNG文件中读取像素数据。像素数据中的每个字节代表像素的RGB值。在每个RGB值中编码的是来自PE文件的字节。之所以没有形成一个有意义的图像,是因为恶意软件将数据隐藏在了看似合法的资源之中。
我们通过枚举每个PNG资源,提取其中的像素数据,并将它们组合在一起,最终获得到了完整的PE文件,正是内存中的完整PE文件。然后,Dropper会手动加载PE文件。对于导入和重定位的处理过程也大致一致,最后将执行整个PE文件的入口点,如下图所示。
RegRunnerSvc
PNG Dropper将从PNG资源中解码,随后运行RegRunnerSvc。RegRunnerSvc的目的是从注册表中提取加密的Payload,将其加载到内存中,然后运行。第一阶段的Dropper(我们目前还未获得)已经将它作为服务安装,同时执行了一些额外的设置操作。
下图展现了RegRunnerSvc的入口点。在这里,我们可以看到对StartServiceCtrlDispatcher的调用。在示例中,其服务名称为WerFaultSvc,显然是为了使其看上去类似于Windows错误报告服务的合法服务而特意选择的名称。该服务还可以作为恶意软件的持久性机制。
在进行服务的功能设置后,恶意软件开始在注册表中查找数据。通常,注册表值的路径将会作为(可能经过加密或混淆后的)字符串,存储到二进制文件中,但有趣的是,这一样本中并没有这样实现。实际上,恶意软件使用RegEnumKeyExA和RegEnumValueA函数遍历注册表项和值。遍历从HKEY_LOCAL_MACHINE开始,并采用深度优先的搜索方式,直至找到数据或结束遍历为止。另外还有一个有趣的实现细节(如下图所示),调用解密函数的唯一要求是数据的大小为0x200(512)字节。这一细节并不像它看起来的那么低效,如果第一阶段的Dropper没有执行其设置操作,那么解密函数就会迅速退出。很明显,对于恶意软件的作者来说,混淆比运行效率更重要。
注册表中的数据,包含加密Payload和解密它所需的数据。尽管其包含用于生成密钥的数据,但却不包含解密密钥。该数据自身使用Microsoft CNG库函数(NCrypt*)实现部分加密。第一阶段的Dropper将生成一个解密密钥,并将其存储在一个系统默认密钥存储提供程序中,在这一样本中为“Microsoft Software Key Storage Provider”。如果第一阶段Dropper未运行,那么密钥将不会保存在存储提供程序中,并且解密函数将会退出。如果存储提供程序实际包含密钥,那么会解密数据的第一个0x200(512)字节。该解密数据包含一个标头,其中包含在二进制Blob中定位其余数据所需的信息。标头中偏移量及完整描述如下:
0x00 加密数据,用于调用BCryptGenerateSymmetricKey()函数;
0x08 加密数据的大小
0x10 IV
0x18 IV的大小
0x20 AES加密数据
0x28 加密数据大小
现在,我们已经完成对标头的分析,接下来就可以进行第二部分的解密。主要Payload使用AES算法进行加密。首先,注册表中的一大块数据会被传递给BCryptGenerateSymmetricKey函数,随后创建AES解密密钥。在生成密钥并设置解密属性后,将会对Payload进行解密。再之后,对解密的Payload进行校验,以确认它是有效的PE文件。该校验主要检查MZ和PE魔术字节,并检查PE标头中的主机体系结构条目。如果校验通过,就会手动加载文件(导入和重定位),并调用入口点,如下图所示。
总结
在本文,我们对Turla组织PNG Dropper的新利用方法有了一定了解。该组织目前正在使用一个新的组件:RegRunnerSvc,该组件从注册表中提取加密后的PE文件,对其进行解密,并执行该文件。该恶意组织似乎从无文件恶意软件(例如Poweliks和Kovter)中获得了启发,尽量使恶意软件在二进制文件中留下尽可能少的信息,导致只留下了没有进行硬编码、包含加密数据的注册表密钥的名称。这也意味着,研究人员将无法提取有用的IoC来进行威胁分析工作。
值得庆幸的是,我们捕获了这一样本,至少可以使用Yara规则来对PNG Dropper进行检测。
作为我们研究的一部分,我们编写了一个工具,可以从PNG Dropper中提取Payload。目前,为方便大家的使用,我们已经公开发布了这一工具,可以在这里找到:
https://github.com/nccgroup/Cyber-Defence/tree/master/Scripts/turla_image_decoder
Yara规则
rule turla_png_dropper { meta: author = "Ben Humphrey" description = "Detects the PNG Dropper used by the Turla group" sha256 = "6ed939f59476fd31dc4d99e96136e928fbd88aec0d9c59846092c0e93a3c0e27" strings: $api0 = "GdiplusStartup" $api1 = "GdipAlloc" $api2 = "GdipCreateBitmapFromStreamICM" $api3 = "GdipBitmapLockBits" $api4 = "GdipGetImageWidth" $api5 = "GdipGetImageHeight" $api6 = "GdiplusShutdown" $code32 = { 8B 46 3C // mov eax, [esi+3Ch] B9 0B 01 00 00 // mov ecx, 10Bh 66 39 4C 30 18 // cmp [eax+esi+18h], cx 8B 44 30 28 // mov eax, [eax+esi+28h] 6A 00 // push 0 B9 AF BE AD DE // mov ecx, 0DEADBEAFh 51 // push ecx 51 // push ecx 03 C6 // add eax, esi 56 // push esi FF D0 // call eax } $code64 = { 48 63 43 3C // movsxd rax, dword ptr [rbx+3Ch] B9 0B 01 00 00 // mov ecx, 10Bh BA AF BE AD DE // mov edx, 0DEADBEAFh 66 39 4C 18 18 // cmp [rax+rbx+18h], cx 8B 44 18 28 // mov eax, [rax+rbx+28h] 45 33 C9 // xor r9d, r9d 44 8B C2 // mov r8d, edx 48 8B CB // mov rcx, rbx 48 03 C3 // add rax, rbx FF D0 // call rax } condition: (uint16(0) == 0x5A4D and uint16(uint32(0x3c)) == 0x4550) and all of ($api*) and 1 of ($code*) }
rule turla_png_reg_enum_payload { meta: author = "Ben Humphrey" description = "Payload that has most recently been dropped by the Turla PNG Dropper" shas256 = "fea27eb2e939e930c8617dcf64366d1649988f30555f6ee9cd09fe54e4bc22b3" strings: $crypt00 = "Microsoft Software Key Storage Provider" wide $crypt01 = "ChainingModeCBC" wide $crypt02 = "AES" wide condition: (uint16(0) == 0x5A4D and uint16(uint32(0x3c)) == 0x4550) and pe.imports("advapi32.dll", "StartServiceCtrlDispatcherA") and pe.imports("advapi32.dll", "RegEnumValueA") and pe.imports("advapi32.dll", "RegEnumKeyExA") and pe.imports("ncrypt.dll", "NCryptOpenStorageProvider") and pe.imports("ncrypt.dll", "NCryptEnumKeys") and pe.imports("ncrypt.dll", "NCryptOpenKey") and pe.imports("ncrypt.dll", "NCryptDecrypt") and pe.imports("ncrypt.dll", "BCryptGenerateSymmetricKey") and pe.imports("ncrypt.dll", "BCryptGetProperty") and pe.imports("ncrypt.dll", "BCryptDecrypt") and pe.imports("ncrypt.dll", "BCryptEncrypt") and all of them }
IoC
PNG Dropper:
6ed939f59476fd31dc4d99e96136e928fbd88aec0d9c59846092c0e93a3c0e27
在PNG Dropper中的Payload:
fea27eb2e939e930c8617dcf64366d1649988f30555f6ee9cd09fe54e4bc22b
服务:
WerFaultSvc