DoublePulsar 分析

admin 2024年3月10日00:11:00评论23 views字数 7150阅读23分50秒阅读模式

困了想睡觉,先发一版~

一、简介 

  今天发现希谭实验室-ABC123发的方程式工具包GUI工具包,话说回来这份泄露的工具其实里面有很多地方都是非常值得学习,无论是从漏洞利用到后门框架等等。所以打算写一波分析文

    DoublePulsar是由美国国家安全局(NSA)方程组开发的后门植入工具,于2017 年初被影子经纪人泄露。该工具在短短几周内感染了超过 200,000 台Microsoft Windows计算机,并在 2017 年 5 月的WannaCry 勒索软件攻击中与EternalBlue一起使用。 Symantec于 2016 年 3 月首次在野外发现了 DoublePulsar 的变种。

    DoublePulsar是一种恶意软件,它利用了Windows操作系统的漏洞,特别是针对SMB和RDP协议的漏洞。它通过在系统内核中注入代码来实现对系统的控制,从而允许攻击者执行恶意操作。DoublePulsar的一个显著特点是它可以绕过Windows的PatchGuard保护机制,这是Windows操作系统用于保护核心部分不被修改的重要机制之一。由于DoublePulsar的高度隐蔽性和对PatchGuard的绕过能力,它被认为是一种非常危险的后门工具。

    PatchGuard 是微软 Windows 操作系统中的一种内核保护机制,旨在防止恶意软件对内核进行未经授权的修改。它通过监视内核数据结构和代码,以检测和阻止未经授权的内核模块加载和内核代码修改。PatchGuard 的主要目标是增强系统的稳定性和安全性,防止恶意软件绕过操作系统的安全性措施。PatchGuard 会定期检查内核的完整性,如果发现异常则会触发系统崩溃或者拒绝加载未经授权的模块,从而保护系统免受恶意软件的攻击。

二、复现

    首先使用MS17-010 攻击模块进行漏洞利用,成功加载DoublePulsar后门。(利用模块为自己利用框架上的模块,Target SP未加载属于当时不熟悉Python,后续考虑优化)

DoublePulsar 分析

图:MS-17-010

    并且使用DoublePusar攻击模块进行RCE操作。弹calc还有上线美少妇就不展示了。

DoublePulsar 分析

图: DoublePusar成功利用

二、永恒之蓝分析

2.1:漏洞存在文件信息:

    漏洞类型:内存池溢出漏洞

    分析存在驱动:srv.sys

    漏洞驱动版本:6.1.7601.17514

    漏洞所在函数:SrvOs2FeaListToNt (需符合表)

    漏洞成因:在 SrvOs2FeaListToNt 主要是将 FEA LIST 转换为 NTFEA LIST 。内部调用的SrvOs2FeaListSizeToNt由于使用错误类型进行强制转换导致获得错误的内存长度,无法进行安全长度校验从而导致OOB,从而可以构造超出长度的内存数据覆盖MDL指针从而实现任意内存写入最后导致远程代码执行。

2.2:漏洞代码分析:

    通过bindiff进行分析,发现漏洞点的差异,错误使用WORD类型。

DoublePulsar 分析

图:补丁对比

通过查看国外大佬逆向的漏洞函数代码发现并且核实了这一点。

DoublePulsar 分析

图:漏洞产生点

    通过分析漏洞调用栈分析SrvOs2FeaListToNt代码,将 OS/2 1.2 格式的 FEALIST 转换为 NT 格式的全量 EA 列表。在转换过程中,需要进行内存分配和数据结构转换。


NTSTATUSSrvOs2FeaListToNt ( IN PFEALIST FeaList, OUT PFILE_FULL_EA_INFORMATION *NtFullEa, OUT PULONG BufferLength, OUT PUSHORT EaErrorOffset )
/*++Routine Description: 将一个 OS/2 1.2 的 FEALIST 转换为 NT 格式。从非分页池分配内存来存储 NT 格式的全量 EA 列表。调用方负责在使用完后释放此内存。 警告!调用方有责任确保 FeaList->cbList 的值在分配给 FeaList 的缓冲区内。这可以防止恶意重定向器在服务器上引发访问冲突。Arguments: FeaList - 指向要转换的 OS/2 1.2 FEALIST 的指针。 NtFullEa - 指向指向 NT 格式全量 EA 列表的指针。将分配一个缓冲区,并由 *NtFullEa 指向。 BufferLength - 分配的缓冲区长度。Return Value: NTSTATUS - STATUS_SUCCESS 或 STATUS_INSUFF_SERVER_RESOURCES。--*/
{ PFEA lastFeaStartLocation; PFEA fea = NULL; PFEA lastFea = NULL; PFILE_FULL_EA_INFORMATION ntFullEa = NULL; PFILE_FULL_EA_INFORMATION lastNtFullEa = NULL;
PAGED_CODE( );
// 找出将 OS/2 1.2 FEALIST 转换为 NT 格式后的大小。这是为了确定分配用于接收 NT EA 的缓冲区的大小。
*BufferLength = SrvOs2FeaListSizeToNt( FeaList );
// 从非分页池中分配一个缓冲区来存储 NT 列表。
*NtFullEa = ALLOCATE_NONPAGED_POOL( *BufferLength, BlockTypeDataBuffer );
if ( *NtFullEa == NULL ) { INTERNAL_ERROR( ERROR_LEVEL_EXPECTED, "SrvOs2FeaListToNt: 无法从非分页池中分配 %d 字节。", *BufferLength, NULL );
return STATUS_INSUFF_SERVER_RESOURCES; }
// 找到最后一个 FEA 可以开始的位置。
lastFeaStartLocation = (PFEA)( (PCHAR)FeaList + SmbGetUlong( &FeaList->cbList ) - sizeof(FEA) - 1 );
// 遍历 FEA 列表,将 OS/2 1.2 格式转换为 NT 格式。
for ( fea = FeaList->list, ntFullEa = *NtFullEa, lastNtFullEa = ntFullEa; fea <= lastFeaStartLocation; fea = (PFEA)( (PCHAR)fea + sizeof(FEA) + fea->cbName + 1 + SmbGetUshort( &fea->cbValue ) ) ) {
// 检查无效标志位。如果设置,返回错误。
if ( (fea->fEA & ~FEA_NEEDEA) != 0 ) { *EaErrorOffset = (USHORT)( (PCHAR)fea - (ULONG)FeaList ); return STATUS_INVALID_PARAMETER; }
lastNtFullEa = ntFullEa; lastFea = fea; ntFullEa = SrvOs2FeaToNt( ntFullEa, fea ); }
// 确保 FEALIST 大小参数正确。如果我们结束于不是最后一个 FEA 之后的位置,则大小参数错误。返回引起错误的 EA 的偏移量。
if ( (PCHAR)fea != (PCHAR)FeaList + SmbGetUlong( &FeaList->cbList ) ) { *EaErrorOffset = (USHORT)( (PCHAR)lastFea - (PCHAR)FeaList ); DEALLOCATE_NONPAGED_POOL( *NtFullEa ); return STATUS_UNSUCCESSFUL; }
// 设置最后一个完整 EA 的 NextEntryOffset 字段为 0,表示列表结束。
lastNtFullEa->NextEntryOffset = 0;
return STATUS_SUCCESS;

    

那么错误类型使用导致什么问题?

    以下做一个小演示。


    DWORD:长度4个字节 32位       而WORD:长度2个字节16位 这个会有什么影响。我们写一个C语言复现一下错误类型的问题。

    发现DWORD错误类型,仍然适用dowrd的值,但是长度变成了0xffff(65535)。

DoublePulsar 分析

图:错误类型

    现在已经知道smb在SrvOs2FeaListSizeToNt进行长度获取,但是使用错误类型导致长度非真实长度,导致按照要求进行长度校验,并且复制并且在SrvOs2FeaToNt进行非分页内存拷贝。

    在拷贝溢出后会覆盖到srvnet.sys(Windows内核驱动的内存不采用独立划分)的非分页内存,永恒之蓝通过覆盖该驱动的SRVNET_BUFFER_HDR对应的MDL指针,实现任意内存写操作。

struct SRVNET_BUFFER_HDR {    LIST_ENTRY list;    USHORT flag; // 2 least significant bit MUST be clear. if 0x1 is set, pmdl pointers are access. if 0x2 is set, go to lookaside.    char unknown0[6];    char *pNetRawBuffer;  // MUST point to valid address (check if this request is "xfdSMB")    DWORD netRawBufferSize; // offset: 0x20    DWORD ioStatusInfo;    DWORD thisNonPagedPoolSize;  // will be 0x82e8 for netRawBufferSize 0x8100    DWORD pad2;    char *thisNonPagedPoolAddr; // 0x30  points to SRVNET_BUFFER    PMDL pmdl1; // point at offset 0x90 关键MDL指针    DWORD nByteProcessed; // 0x40    char unknown4[4];    QWORD smbMsgSize; // MUST be modified to size of all recv data    PMDL pmdl2; // 0x50:  if want to free corrupted buffer, need to set to valid address    QWORD pSrvNetWskStruct;  假WSL地址    DWORD unknown6; // 0x60    char unknown7[12];    char unknown8[0x20];};struct SRVNET_BUFFER {    char transportHeader[80]; // 0x50    char buffer[reqSize+padding];  // 0x8100 (for pool size 0x82f0), 0x10100 (for pool size 0x11000)    SRVNET_BUFFER_HDR hdr; //some header size 0x90    //MDL mdl1; // target};

    查看漏洞利用脚本代码,可以看到对应利用流程。https://gist.github.com/worawit/bd04bad3cd231474763b873df081c09a?permalink_comment_id=2311451

    大概流程就先控制MDL,间接性控制srvnet驱动,后通过srvnet,进行任意内存写入。看图构造恶意结构体的对应数据。

    fakeSrvNetBufferNsa 是一个用于构造伪造的 SRVNET_BUFFER 结构的数据块。这个结构用于利用漏洞进行内存泄漏和任意写入。这个数据块包含了一些字段的值,这些字段的值将被用来覆盖 SRVNET_BUFFER 结构中的相应字段。攻击者利用这些字段的不当值来控制程序行为,最终实现代码执行。具体来说:

  • fakeSrvNetBufferNsa 中的数据被设计为符合 SRVNET_BUFFER 结构的布局,以便在漏洞利用过程中替换原始 SRVNET_BUFFER 结构的内容。

  • 这些数据中包含了一些特定值,如指向函数指针的地址、MDL 结构的描述、以及用于触发特定行为的值。这些值的设定是为了在利用漏洞时能够控制程序的执行流程。

  • 通过精心构造的 fakeSrvNetBufferNsa 数据块,攻击者可以在程序中引发内存泄漏、任意写入等行为,最终实现对程序的控制和攻击。

DoublePulsar 分析

图:fakeSrvNetBufferNsa构造

并且分析fake_recv_struct构造,通过控制SrvNetWskReceiveComplete()和SrvNetCommonReceiveHandler的指针,指向HAL,因为HAL地址是固定的(CVE-2020-0796是因为无法攻克地址随机化和RWX问题,导致很多Exp不稳定利用)

DoublePulsar 分析

图:构造代码

    注意发送完后,其实最终执行在srvnet的会话取消。

DoublePulsar 分析

图:最终执行

二、DoublePulsar分析

    以上内容有点跑题了,开始正式分析DoublePulsar。其实DoublePulsar巧妙在于可以保证会话持久因为频繁进行漏洞利用会导致BSOD问题。

    简单分析一下

DoublePulsar 分析

图:流程图

    我简单做个图,首先进行一些比较基础向的SMB Header会话建立。然后都会建立一个ping测试,它类似于ICMP协议,主要是看后门是否存活。然后一起带有一个key用于对下面的shellcode进行加密。fuzzbunch的dopu里面的dll注入是基于APC注入的一段shellcode实现的,所以我们把RunShellCode和DLL Inject归结在一起。这里并不对DLL注入进行分析。

    通过分析导出Shellcode,可以使用ida或者ghidra,首先分析头部,开发者利用x64和x86区别的特性,利用opcode区别x86 会多出一个inc。 因为eax被执行inc,ZF也会被清0,JZ跳转会失效会执行下一跳Call调用。代码会继续执行流程。x64没有执行inc会跳转会被执行,会跳转。

DoublePulsar 分析

    代码差异

DoublePulsar 分析

图:头部差异

   接着分析Shellcode,具体流程查找ntoskrnl.exe基地址并且通过Hash定位ZwQuerySystemInformation和一些内存池申请释放API的地址(ExAllocatePool, ExFreePool )。

DoublePulsar 分析

图:基础地址定位

    接下来,Shellcode通过调用ZwQuerySystemInformation,将SystemInformationClass设置为0x0B(请求系统模块信息结构的未记录选项)来获取已加载的驱动程序列表,使用路径哈希比较在列表中搜索Srv.sys。

DoublePulsar 分析

图:ZwQuerySystemInformation

DoublePulsar 分析

    图:定位srv.sys

       遍历 .sys到 PE 部分,直到到达 .data 部分.data 部分内部通常是全局读/写内存,这里存储的是 SrvTransaction2DispatchTable,一个处理不同 SMB 任务的函数指针数组。

        shellcode 存储名为 SrvTransactionNotImplemented() 的调度的函数指针(以便它可以从钩子代码中调用它)。然后它用钩子覆盖 SrvTransaction2DispatchTable 中的这个成员。

    就是这样。后门完成。现在它只是返回自己的调用堆栈并做一些小的清理工作。


三、DoublePulsar通讯原理

    这个后门它是通过SMB Trans2进行协议会话。我们前面复现例子举例,比如我们先进行Ping测试。我们ping时会设置一个随机的MID(MultiplexID ),发送到目标服务器,然后触发后门。后门一旦返回消息后,会将返回的Mid与原Mid进行减法计算。最终会得到一个状态码。 然后怎么触发后门呢? 他是通过Timeout字段进行计算取后两位。

SMB_Header{UCHAR  Protocol[4];UCHAR  Command;  //命令码SMB_ERROR Status;//错误信息UCHAR  Flags;    //USHORT Flags2;USHORT PIDHigh;  UCHAR  SecurityFeatures[8];USHORT Reserved;USHORT TID;   //tree idUSHORT PIDLow;/进程IDUSHORT UID;  //用户IDUSHORT MID;  //multiplex ID}

比如0x723 它是23。等于ping
    下面是状态码 和操作码
    状态代码(通过 MultiplexID delta)是:

        0x10 = 成功

        0x20 = 无效参数

        0x30 = 分配失败

    操作码列表如下:

        0x23 = ping   

        0xc8 = 执行Shellcode

        0x77 = 杀死

    然后查看参数数据。

DoublePulsar 分析

    然后用来加密shellcode key通过SecurityFeatures字段里面的数据进行计算得出,传输时通过XOR加密。

DoublePulsar 分析

DoublePulsar 分析

图:Shellcode

构造发送数据

DoublePulsar 分析

图:构造发送数据

(声明:曾在圈子社区发布过此文,后续经过修改细化重新发布。)

原文始发于微信公众号(猫头鹰安全团队):DoublePulsar 分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月10日00:11:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   DoublePulsar 分析http://cn-sec.com/archives/2563333.html

发表评论

匿名网友 填写信息