针对内核中运行的恶意软件Uroburos的分析 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

针对内核中运行的恶意软件Uroburos的分析

41yf1sh 技术 2018-07-10 11:58:07
360381
收藏

导语:恶意软件Uroburos于2014年首次被发现,作为APT攻击中的一部分,具有较大程度的威胁。该恶意软件与其他同类恶意软件的不同之处在于,它是用Windows的64b(Rootkit)驱动程序,能够绕过系统的PatchGuard防护机制。

恶意软件Uroburos于2014年首次被发现,作为APT攻击中的一部分,具有较大程度的威胁。该恶意软件与其他同类恶意软件的不同之处在于,它是用Windows的64b(Rootkit)驱动程序,能够绕过系统的PatchGuard防护机制。此外,驱动程序未经签名,恶意软件可以利用第三方驱动程序中的漏洞来实现内核执行。此前,Andrzej Dereszowski和Matthieu Kaczmarek曾针对这一恶意软件进行了研究,各位可以参考阅读他们的研究成果: http://artemonsecurity.com/uroburos.pdf

几个月前,我们发现了一个新的Uroburos/Turla样本,该样本的发布时间为2017年。经过仔细分析和比较,我们发现其中的驱动程序基于2014年的版本做了改进,一些地方与原始版本相比具有较大的差异。本文主要对这个64位Rootkit的新增特性进行分析。

我们的分析过程将侧重于从内存转储(Memory Dump)中识别Rootkit(就像我们在寻找威胁时所做的工作),然后我们对该恶意软件的新通信协议进行研究。我们的目标是希望远程发现Rootkit的存在,而无需在服务器上进行身份验证。需要注意的是,该Rootkit仅针对于服务器。

本文分析的代码位于:https://www.virustotal.com/en/file/f28f406c2fcd5139d8838b52da703fc6ffb8e5c00261d86aec90c28a20cfaa5b/analysis

为了能够在服务器上找到威胁,我们使用了Comae DumpIt工具,并分析该工具生成的故障转储(Crushdump)。

确定核心威胁

恶意软件的驱动程序能够很好地隐藏在内核空间之中,我们发现它不存在于加载模块列表中,而且经确认,其他模块的完整性都保持良好。

为了辅助分析,我们使用ExaTrack开发的内部工具,借助该工具来检查内核的完整性,并发现潜在的异常问题。

Windows回调(Callback)系统允许在某些事件(如进程创建)期间调用任意函数,这是我们重点关注的组件。

通过认真分析,我们发现了一个异常的地方:

>>> ccb 
# Check CallBacks 
[*] Checking \Callback\TcpConnectionCallbackTemp : 0xfffffa8002f38360 
[*] Checking \Callback\TcpTimerStarvationCallbackTemp : 0xfffffa8004dfd640 
[*] Checking \Callback\LicensingData : 0xfffffa80024bc2f0 
[...] 
[*] PspLoadImageNotifyRoutine 
[*] PspCreateProcessNotifyRoutine 
Callback fffffa8004bc2874 -> SUSPICIOUS ***Unknown*** 48895c2408574881ec30010000488bfa

在创建进程时,会调用PspCreateProcessNotifyRoutine列表中的回调函数。如果将一些条目添加到其中,理论上就可以对新进程的数据及行为进行改动。在上一个命令中,该工具已经识别出一条可疑的条目,因为它转向了未分配给驱动程序的内存地址。

同时,回调函数中还存在第二个异常,这个地方不太明显,因为它不会对系统中所执行的操作产生太大影响,但会对其进行略微的改动。

[...] 
[*] IopNotifyShutdownQueueHead 
 Name : Null 
 Driver Object : fffffa80032753e0 
   Driver : \Driver\Null 
   Address: fffff88001890000 
   Driver : Null.SYS 
 Name : 000000a6 
 Driver Object : fffffa8003d2adb0 
   Driver : \Driver\usbhub 
   Address: fffff88000da6000 
   Driver : usbhub.sys 
[...] 
>>> cirp \Driver\Null 
Driver : \Driver\Null 
Address: fffff88001890000 
Driver : Null.SYS 
DriverUnload : fffff88001895100 c:\windows\system32\drivers\null.sys 
IRP_MJ_CREATE fffff88001895008 Null.SYS 
IRP_MJ_CREATE_NAMED_PIPE fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_CLOSE fffff88001895008 Null.SYS 
IRP_MJ_READ fffff88001895008 Null.SYS 
IRP_MJ_WRITE fffff88001895008 Null.SYS 
IRP_MJ_QUERY_INFORMATION fffff88001895008 Null.SYS 
IRP_MJ_SET_INFORMATION fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_QUERY_EA fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_SET_EA fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_FLUSH_BUFFERS fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_QUERY_VOLUME_INFORMATION fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_SET_VOLUME_INFORMATION fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_DIRECTORY_CONTROL fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_FILE_SYSTEM_CONTROL fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_DEVICE_CONTROL fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_INTERNAL_DEVICE_CONTROL fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_SHUTDOWN fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_LOCK_CONTROL fffff88001895008 Null.SYS 
IRP_MJ_CLEANUP fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_CREATE_MAILSLOT fffff80002abb1d4 ntoskrnl.exe 
IRP_MJ_QUERY_SECURITY fffff80002abb1d4 ntoskrnl.exe

在系统关闭时,要调用的模块列表中会引用“\Driver\Null”驱动程序。但实际上,这个驱动程序不应该放在此列表中。我们发现,其IRP表似乎没有被修改,它指向了ntoskrnl(对于IRP_MJ_SHUTDOWN接口)。我们认为这种行为是没有意义的。

接下来,我们继续分析。在Windows的输入输出(IO)处理程序中,有很多过滤系统。同时,在这些IO中,网络共分为几个部分,我们在这里将对其中的一个进行深入研究,也就是NetIO。

NetIO也提供了一个回调系统,允许对交换的网络数据进行操作,这些回调被称为“Callout”。但是,由于进行的是网络回调,所以其结构没有被记录,也没有出现在Windows符号文件之中。上述这些特性,使之成为了植入恶意软件的最佳之选。在转储中,我们可以找到5个回调,这些回调指向不属于任何驱动程序的代码。

>>> cnetio 
[*] NetIo Callouts (callbacks) : fffffa8004965000 (4790) 
Callback fffffa8004bd9580 -> SUSPICIOUS ***Unknown*** 488bc448895808488950105556574154 
Callback fffffa8004bca6b0 -> SUSPICIOUS ***Unknown*** 33c0c3cc40534883ec20488b89500100 
Callback fffffa8004bd9af8 -> SUSPICIOUS ***Unknown*** 4883ec286683f91474066683f916750f 
Callback fffffa8004bd9ca0 -> SUSPICIOUS ***Unknown*** 48895c24084889742410574883ec4048 
Callback fffffa8004bd9de0 -> SUSPICIOUS ***Unknown*** 4c8bdc49895b0849896b104989731857

稍后,我们将会仔细研究其中的一个函数。

在这里,我们最后要寻找试图隐藏在Windows中的已加载的驱动程序。

>>> pe 
[...] 
[OK] fffff88001899000 : \SystemRoot\System32\Drivers\Beep.SYS 
[OK] fffff88000da6000 : \SystemRoot\system32\DRIVERS\usbhub.sys 
[NO] fffffa8004bb8000 (Header overwritten) 
[OK] fffff88006a00000 : \SystemRoot\system32\DRIVERS\E1G6032E.sys 
[OK] fffff880017d2000 : \SystemRoot\System32\Drivers\Npfs.SYS
[...] 
>>> dq fffffa8004bb8000 100 
FFFFFA8004BB8000 0000000300000000 0000FFFF00000004 ....?...?...¦¦.. 
FFFFFA8004BB8010 00000000000000B8 0000000000000040 ........@....... 
FFFFFA8004BB8020 0000000000000000 0000000000000000 ................ 
FFFFFA8004BB8030 0000000000000000 000000D800000000 ................ 
FFFFFA8004BB8040 CD09B4000EBA1F0E 685421CD4C01B821 ??.?....!.?L.!Th 
FFFFFA8004BB8050 72676F7270207369 6F6E6E6163206D61 is program canno 
FFFFFA8004BB8060 6E75722065622074 20534F44206E6920 t be run in DOS
FFFFFA8004BB8070 0A0D0D2E65646F6D 0000000000000024 mode....$....... 
FFFFFA8004BB8080 095520395A3B417D 0955203909552039 }A;Z9 U.9 U.9 U. 
FFFFFA8004BB8090 095520A609542039 0955203C092E28A6 9 T.. U..(..< U. 
FFFFFA8004BB80A0 0955203B0928E61E 095520510938E61E ?.(.; U.?.8.Q U. 
FFFFFA8004BB80B0 09552038092FE61E 09552038092DE61E ?./.8 U.?.-.8 U. 
FFFFFA8004BB80C0 0955203968636952 0000000000000000 Rich9 U......... 
FFFFFA8004BB80D0 0000000000000000 0006866400000000 ............d.?. 
FFFFFA8004BB80E0 000000005900F3CF 202200F000000000 ...Y.........." 
FFFFFA8004BB80F0 00042E000008020B 000000000001BC00 ??....?...?..... 
>>> list fffffa8004bb8000 fffffa8004bbb000 
FFFFFA8004BB8000 rwx- 
FFFFFA8004BB9000 rwx- 
FFFFFA8004BBA000 rwx-

在这里,可以发现一个重要的异常。驱动程序存在于内存中,并且已经覆盖其MZ和PE头部,从而可以在原始内存搜索中隐藏其自身。它的地址与我们此前遇到的回调函数相对应,并且它以RWX权限进行映射。

目前发现的种种证据,都表明了该恶意软件是从内核运行的。现在,我们将分析它的一些代码(主要是网络通信部分),以进一步了解其工作原理。

驱动程序分析

入口点

在初始化期间,驱动程序将迅速瞄准“空”的设备。恶意软件检索指向该对象的指针,并将其注册在前文提到的“关闭”回调列表中。此外,它还注册了在进程创建期间调用的回调函数。

[...] 
  if ( (unsigned int)get_top_deviceObjet(L"\\Device\\Null", &device_obj_null) 
    && (result = get_top_deviceObjet(L"\\Device\\Beep", &device_obj_null), (_DWORD)result) ) 
  { 
    __asm { xchg rbx, qword ptr cs:isNullDeviceFailed } 
  } 
  else
  { 
    v5 = IoRegisterShutdownNotification(device_obj_null); 
    if ( v5 || (drvobj_null = device_obj_null->DriverObject, (v5 = sub_4E21C(byte_1188D)) != 0) ) 
[...] 
      PsSetCreateProcessNotifyRoutine(cbCreateProcess, 0i64); 
[...]

加密字符串

为了避免恶意软件被反病毒软件识别出来,所有与Uroburos相关的字符都会被加密。每个加密数据块大小为0x40字节,并使用前一个0x40字节进行异或(XOR)操作。

1.png

解密函数如下代码所示,由此,我们接下来的分析过程将会顺利很多。

Python>def decrypt(addr, clen): return ''.join(chr(b) for b in
[struct.unpack('B'*clen,idaapi.get_many_bytes(addr,64))[a] ^
struct.unpack('B'*clen,idaapi.get_many_bytes(addr-clen,clen))[a] for a in xrange(clen)])
 
Python>[decrypt( 0x53530 + (i*0x80) , 0x40).replace("\x00",'') for i in xrange(38)]
['system', 'isapi_http', 'isapi_log', 'isapi_dg', 'isapi_openssl', 'shell.{F21EDC09-85D3-
4eb9-915F-1AFA2FF28153}', 'Hd1', 'Hd2', 'RawDisk1', 'RawDisk2', 'wininet_activate',
'dmtev', 'Ultra3', 'Ultra3', 'services_control', 'fixdata.dat', '$NtUninstallQ817473$',
'fdisk.sys', 'fdisk_mon.exe', '400', '16', '{AAAA1111-2222-BBBB-CCCC-DDDD3333EEEE}',
'~WA434.tmp', '~WA4276.tmp', '.', '~WA356.tmp', 'rasmon.dll', 'rasman.dll', 'user',
'internat', 'NTUSER.DAT', 'ntuser.dat.LOG1', '.', 'mscrt.dll', 'msvcrt.dll', '0', '1',
'.']

在文末,提供了加密函数的YARA规则。

网络拦截

如上所示,网络回调已经安装。恶意软件通过函数FwpsCalloutRegister0(该函数允许添加网络过滤器)来注册,并且能控制驱动程序是否传输接收到的数据。

v20 = addCalloutAddress(
    &stru_14930,
    &a2,
    DeviceObject,
    (__int64)intercept_packet,
    (__int64)&ret_null,
    (__int64)a6,
    (__int64)&v47,
    (__int64)&v34,
    &a9,
&a10);

“intercept_packet”函数(位于内存转储中地址fffffa8004bd9580处)将对经过网络连接的数据进行分析。有趣的是,它并不会查看经过139端口的数据,只会查看其他端口收到的数据。

if ( v9 || LOWORD(a1->layerId) == 20 && a1->pIP_infos->src_port == 139 )
    return;
if ( LOWORD(a1->layerId) == 22 && a1->pIP_infos->src_port == 139 )
    return;
[...]
    fwpsCopyStreamDataToBuffer0(v8, datas_tcp_buffer, *(_QWORD *)(v8 + 48), &v31);
[...]
    buffer_type_2 = find_and_decode_datas(datas_tcp_buffer, v24, *((_DWORD *)v11 + 0x1FF) == 0, &a4a);

“find_and_decode_datas”函数负责测试不同的已接受通信协议。在这里我们将研究HTTP通信。目标是研究如何远程确定某台服务器是否已被Uroburos攻陷。

恶意软件会验证收到的消息是否为标准的HTTP请求,然后会在HTTP标头的一个参数中查找隐藏的信息。

if ( space_offset_1 != 3i64
    || ((v18 = *(_WORD *)Buf < (unsigned __int16)str_GET, *(_WORD *)Buf !=
(_WORD)str_GET)
    || (v19 = Buf[2], v18 = (unsigned __int8)v19 < BYTE2(str_GET), v19 != BYTE2(str_GET)) ? (v20 = -v18 - (v18 - 1)) : (v20 = 0), v20) )
    {
        if ( space_offset_1 != 4 || *(_DWORD *)Buf != str_POST )
            return 0i64;
    }
[...]
    if ( *(_DWORD *)start_word_2 != *(_DWORD *)"http://"
[...]
    if ( v33 != *(_DWORD *)"HTTP/" || (v35 = v32[4], v34 = v35 < aHttp_0[4], v35 !=
aHttp_0[4]) )
[...]
    || !(unsigned int)check_and_decode_buffer(&Buf[nextline], v14, response_tag, out_decoded_b64, v7) )
[...]

“check_and_decode_buffer”函数将会查找第一个“:”字符,并尝试在相应的HTTP参数中查找隐藏信息。

v15 = memchr(v10, ':', (unsigned int)(v14 - 1));
if ( !v15 || *((_BYTE *)v15 + 1) != ' ' )
    return (unsigned int)v5;

然后,通过一些校验和(Checksum)验证信息是否完整。

v10 = get_checksum(datas, 12);
result = (unsigned __int64)reverse_littleendian(v10) == *(_DWORD *)v4;

校验和函数使用修改后的“threefish256”算法。

crypto_it(v3, v6, (v5 - 1) >> 5, 32);
[...]
memcpy((void *)(v3 + *(_QWORD *)(v3 + 8) + 64), v6, v5);
*(_QWORD *)(v3 + 8) += v5;

该过程会运行3次散列函数,来计算出最终的散列值。最终获得的散列值,将会以4字节为单位进行截断,并以此作为校验和。我们猜测,针对每个目标,都有一个不同的密钥。同时还有一个在恶意软件及其运营者之间共享的密钥,该密钥用于计算哈希值,但不会在请求中发送。

要传递的信息位于每一行的末尾,其格式为7字节内容+1字节校验和,该校验和只是前7个字节的总和。随后,恶意软件会对这8个字节进行Base64编码。

后面,将会对HTTP请求数据进行如下修改:

2.png

有趣的是,“Code”元素在最终的查询中并不存在。实际上可以使用4个值,并且它们会在验证过程中由恶意软件强制执行。

当服务器与此类型的请求(在已经打开的端口上)进行通信时,Rootkit将负责响应(数据不会传递到用户空间)。

如果收到的消息符合特定格式,驱动程序就会发送可变大小的响应内容,并以随机字节进行填充。

if ( reply_datas[6] & 2 )
{
    v8 = 8 * (rand() % -32);
    v4 = v8;
    v9 = &v21[-v8];
    if ( v8 )
    {
        v10 = v8;
        do 
        { 
            *v9++ = rand(); 
            --v10; 
        } 
        while ( v10 ); 
    } 
} 
*(_BYTE *)(v7 + 0xBE0); 
sprintf(Dest, "HTTP/1.1 200 OK\r\nContent-Length: %u\r\nConnection: %s\r\n\r\n",(unsigned int)(v4 + 8));

只有前8个字节会对特定格式进行响应,其他所有数据都是随机的。针对该内容的完整性检查将会强制执行,并将结果存储在第8个字节中。这一校验和过程与上文中分析的过程类似。因此,我们可以基于此,开发一个PoC,来远程检查服务器是否已经被此版本的恶意软件贡献:

> request_builder.py 192.168.48.133 8080 
datas : 
0000000000000000 E8 F6 E8 4E 72 61 03 EA C8 B3 DD 8D 25 D0 26 12 ...Nra♥.....%.&↕ 
0000000000000010 B7 F9 50 E5 8C D2 01 62 A0 37 2F FB AD C8 91 DA ..P...☺b.7/..... 
0000000000000020 44 A5 53 C7 1D 76 0E 4D AC AF F7 18 F4 12 57 A2 D.S.↔v♫M...↑.↕W. 
0000000000000030 A0 75 3B 0F 50 C5 6C 55 31 4B A1 9F D0 2E F4 F4 .u;☼P.lU1K...... 
0000000000000040 30 39 93 13 1A DF B8 A2 B4 7C DB 88 55 DE 26 98 09.‼→....|..U.&. 
0000000000000050 98 04 29 6F AF 25 CF 9F FA F5 90 0D D8 23 E9 97 .♦)o.%.......#.. 
[*] checksum OK – Host is compromised

PoC请参考:http://www.exatrack.com/public/uroburos_poc.py

2014年与2017年版本的对比

与2014年的原始版本相比,存在一些相同和不同之处,接下来将简要列举我们所发现的一些内容。

1、文件名称和服务名称没有发生变化,从而导致任何IoC都可以轻松检测到。

2、驱动程序总会加载VirtualBox漏洞利用方法,因此在每次重新引导系统时都会进行内核漏洞利用。

3、新版本中删除了绕过PatchGuard机制的部分,这一改动会对内核修改产生一定限制。

Driver 2014: 
    if ( v2 ) 
        installService(v3);
    v4 = PG_bypass(); 
    if ( v4 )
        goto LABEL_16; 
    ObjectAttributes.Length = 48; 
    ObjectAttributes.RootDirectory = 0i64; 
 
Driver 2017: 
    if ( v2 ) 
        installService(cp_DriverObject); 
    ObjectAttributes.RootDirectory = 0i64; 
    ObjectAttributes.SecurityDescriptor = 0i64;

\Driver\Null驱动程序仍然使用,并且\Device\FWPMCALLOUT设备仍然会附加到该驱动程序之中。

>>> drv_stack \Driver\Null 
- Stack of device name : \Device\FWPMCALLOUT 
Driver Object : fffffa80032753e0 
    Driver : \Driver\Null 
    Address: fffff88001890000 
    Driver : Null.SYS 
 
- Stack of device name : \Device\Null 
Driver Object : fffffa80032753e0 
    Driver : \Driver\Null 
    Address: fffff88001890000 
    Driver : Null.SYS

在新版本恶意软件中,字符串加密这一部分与原始版本没有变化。我们可以看到,在IopNotifyShutdownQueueHead中注册“Null”驱动程序是一个非常有效的思路,但在该恶意软件中还没有看到其具体用途。恶意软件编写者可能会借助这一回调函数,在关闭时写入注册表项,从而保证持久性。

新版本对校验机制进行了改进,同时恶意软件使用了Threefish算法,并对消息的格式进行调整,这些举措的目的可能是为了改变恶意软件的特征,以逃避检测。

总体而言,新版本Rootkit仍然存在较大的风险,并且在原版本基础上进行了一些加强。尽管新版本中也有一些疏漏,例如没有修改文件和注册表项的名称,这可能表明该Rootkit只会在隔离的服务器上运行。

与用户空间组件相比,位于内核的恶意软件更难被检测,因此我们预测这一类型的恶意软件仍然会存在一段时间,威胁用户的安全。

YARA规则

rule Sig 
{ 
    strings: 
        $strings_crypt = { 4d 8b c1 41 ba 40 00 00 00 41 ?? ?? ?? 41 ?? ?? 49 83 c0 01 49 83 ea 01 75 ??} 
        $hash_part1 = { 49 c1 c3 0e 4e ?? ?? ?? 4c 33 dd 4c 03 c7 4c 03 c1 48 c1 c0 10 49 33 c0 4d 03 c3 48 03 e8 48 c1 c8 0c 48 33 c5 49 c1 cb 07 4d 33 d8 4c 03 c0 49 03 eb 49 c1 c3 17 4c 33 dd 48 c1 c8 18 49 33 c0 4d 03 c3 48 03 e8 49 c1 cb 1b 4d 33 d8 4c 03 df 4c 03 d9 48 c1 c0 05 48 33 c5 4a ?? ?? ?? ??} 
    condition: 
        1 of them 
}
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务