导语:Eternal Synergy是一个SMBv1认证漏洞,它特别有趣,许多开发步骤纯粹是基于分组的,而不是本地shellcode执行。像其他SMB漏洞一样,MS17-010也解决了CVE-2017-0143这一问题。该漏洞利用可以在Windows 8上进行,但不能像任何较新的平台一样
最近我们发布了一系列博客文章,来分析ShadowBrokers于2017年4月发布的漏洞,特别是其中一些较少被研究的漏洞。本周我们来看一下Eternal Synergy,这是一个SMBv1认证漏洞。其实它特别有趣,因为许多开发步骤纯粹是基于分组的,而不是本地的shellcode执行。像其他SMB漏洞一样,MS17-010也解决了CVE-2017-0143这一问题。该漏洞利用可以在Windows 8上进行,但不能像任何较新的平台一样进行写入。
这篇文章分为四个主要部分。我们将深入探讨该漏洞,随后讨论如何将该漏洞武器化以创建在整个漏洞利用中用作构建块的读/写/执行的基元。接下来,我们将继续执行EternalSynergy,看看这些基元如何被用来提供一个完整的漏洞。最后,我们将简要讨论近期的一些缓解措施的提出对漏洞利用技术的影响。
漏洞:CVE-2017-0143
此漏洞的根本原因在于确定消息是否是事务(Transaction)的一部分时,不考虑SMB消息的命令类型。换句话说,只要在SMB头UID,PID,TID和OtherInfo字段匹配相应的事务(Transaction)字段,该消息将被认为是事务(Transaction)的一部分。
通常,该OtherInfo字段存储一个MID。但在SMB_COM_WRITE_ANDX消息的情况下,它会存储一个FID。这将产生一个潜在的消息类型混淆:给定一个现有的SMB_COM_WRITE_ANDX事务,MID等于事务FID的传入SMB消息将被包含在事务中。
PTRANSACTION SrvFindTransaction ( IN PCONNECTION Connection, IN PSMB_HEADER Header, IN USHORT Fid OPTIONAL ) { ... // // If this is a multipiece transaction SMB, the MIDs of all the pieces // must match. If it is a multipiece WriteAndX protocol the pieces // using the FID. // if (Header->Command == SMB_COM_WRITE_ANDX) {targetOtherInfo = Fid;} else {targetOtherInfo = SmbGetAlignedUshort( &Header->Mid );} ... // // Walk the transaction list, looking for one with the same // identity as the new transaction. // for ( listEntry = Connection->PagedConnection->TransactionList.Flink; listEntry != &Connection->PagedConnection->TransactionList; listEntry = listEntry->Flink ) { thisTransaction = CONTAINING_RECORD( listEntry, TRANSACTION, ConnectionListEntry ); if ( (thisTransaction->Tid == SmbGetAlignedUshort( &Header->Tid) ) && (thisTransaction->Pid == SmbGetAlignedUshort( &Header->Pid) ) && (thisTransaction->Uid == SmbGetAlignedUshort( &Header->Uid) ) && (thisTransaction->OtherInfo == targetOtherInfo) ) { ... // A transaction with the same identity has been found ... } ... }
开发
当SMB消息到达时,相应的处理程序将其内容复制到相应的事务(Transaction)缓冲区中,即InData。该SMB_COM_TRANSACTION_SECONDARY处理器假定InData地址指向缓冲区的开始。
if ( dataCount != 0 ) { RtlMoveMemory(transaction->InData + dataDisplacement, (PCHAR)header + dataOffset, dataCount ); }
然而,在SMB_COM_WRITE_ANDX的事务(Transaction)情况下,每当接收到该事务的SMB时,该InData地址被更新以指向现有数据的结束。
RtlCopyMemory(transaction->InData, writeAddress, writeLength ); // // Update the transaction data pointer to where the next // WriteAndX data buffer will go. //transaction->InData += writeLength;
利用数据包混淆,攻击者可以在SMB_COM_WRITE_ANDX事务中插入一个消息SMB_COM_TRANSACTION_SECONDARY。在这种情况下,InData将指向缓冲区的开头,因此SMB_COM_TRANSACTION_SECONDARY在复制传入的消息数据期间,处理程序可以溢出缓冲区。
接手事务(Transaction)
为利用过程中中使用的RWX基元构建块然后通过利用上一节中描述的消息混淆来接手事务结构。首先,通过SMB_COM_TRANSACTION客户端消息分配“控制”事务。
kd> dt srv!TRANSACTION 0xfffff8a00167f010 ... +0x080 InData :0xfffff8a0`0167f110+0x088 OutData : (null) ... +0x0a4 DataCount : 0x0 +0x0a8 TotalDataCount : 0x5100 ... +0x0ba Tid : 0x800 +0x0bc Pid : 0xab9e +0x0be Uid : 0x800 +0x0c0 OtherInfo : 0x4000
然后,SMB_COM_WRITE_ANDX发送一个消息,用于利用数据包混淆。因此,InData控制事务的指针被破坏以指向缓冲区的开始。在这里,它是通过0x200字节被关闭的。
kd> dt srv!TRANSACTION 0xfffff8a00167f010 ... +0x080 InData :0xfffff8a0`0167f310+0x088 OutData : (null) ... +0x0a4 DataCount : 0x200 +0x0a8 TotalDataCount : 0x5100 ... +0x0ba Tid : 0x800 +0x0bc Pid : 0xab9e +0x0be Uid : 0x800 +0x0c0 OtherInfo : 0x4000
接下来,将SMB_COM_TRANSACTION_SECONDARY消息发送到同一事务,并通过利用损坏的InData指针来修改相邻的“受害者”事务。我们再次访问来了解下面如何去计算目标写入地址。
if ( dataCount != 0 ) { RtlMoveMemory( transaction->InData + dataDisplacement, (PCHAR)header + dataOffset, dataCount ); }
传入的消息dataDisplacement足够大以到达相邻的事务。
kd> dv dataDisplacement dataDisplacement = 0x5020
具体来说,它将OtherInfo使用攻击者控制的值(在这种情况下为0)来覆盖事务的字段,以便将使用的所有将来的消息MID=0指向受害者事务。下面我们看到在覆盖发生之前的受害者事务。
kd> dt srv!TRANSACTION 0xfffff8a00167f310+0x5020-0xc0 ... +0x080 InData : 0xfffff8a0`0168436c +0x088 OutData : 0xfffff8a0`01684ffc ... +0x0ba Tid : 0x800 +0x0bc Pid : 0xab9f +0x0be Uid : 0x800 +0x0c0 OtherInfo : 0x8ccb
在接管受害者交易之后,漏洞利用可以预测性地在相同或其他事务中继续破坏领域,并通过向事务发送消息来可靠地触发它们。请注意,为了使这种技术有效,攻击者必须能够可预测地分配一对邻近的事务。
远程任意写入原语
任意写入原语可以使得攻击者修改受害者系统上的内存内容,并作为此漏洞中使用的其余技术的基础。为了破坏内存,它利用了上一节中描述的技术。具体来说,写入原语分为两个步骤:
在步骤1中,受害者事务InData缓冲区地址被覆盖,使其指向目标写入地址。
接下来在步骤2中,攻击者可以通过发送到受害者事务来覆盖任意内核内存。收到消息后,其内容将被复制到InData缓冲区; 然而,在这一情况下,缓冲区地址被破坏,所以内容被复制到攻击者控制的地址。下面是一个示例数据包,其中包含“Extra byte parameters”中的shellcode将被复制到受害计算机。
远程任意读取原语
任意读取原语可以使得攻击者从目标系统远程读取任何内存地址的内容。要使用这个原语,攻击者必须成功进行以下内容:
1、采取连接,并建立了写入原语,如上所述。
2、泄漏一个有效的TRANSACTION结构
正如下图中我们所看到的,我们有邻近于受害者#1交易用于写原语和受害者#2事务的控制事务处理。消息#1使用写入原语来破坏受害者#1 InData缓冲区地址,使其指向受害者#2基地址。这意味着针对受害人#1交易的任何消息将导致在消息的“数据移位”字段指定的偏移量下破坏受害者#2事务。受害者#2是泄漏的TRANSACTION结构,其基址可以通过其内容推断。
其余消息包含Transaction Secondary命令(0x26),并使用相同的TID,PID和UID。消息#2-5定位受害者#1交易(MID = 0),并执行受害人#2交易的特定字段的覆盖。下表总结了每条消息的修改:
作为示例,下面是发送以执行远程读取操作的消息。payload在“Extra byte parameters”中指定“Data Displacement”中的目标地址和“Data Count”字段中的大小。
消息#5是一个虚拟数据包,具有一个非零MID对象,受害者#2事务,发送到触发服务器响应。在响应消息期间,将损坏的DataOut指针的内存地址的内容复制出去并发送回SMB客户端。此类消息的示例如下所示:
代码执行
前面章节讨论的技术主要在内存中运行,利用过程中仍然需要一种方式来改变控制流并以可重复的方式调用执行。而这一利用需要通过破坏SMB消息处理程序的指针来实现此目的。
首先,它通过写入原语利用执行目标的地址覆盖0xe入口的srv!SrvTransaction2DispatchTable。这是一个dispatch表,其中包含SMB消息处理程序的指针。针对TRANS2_SESSION_SETUP子命令处理程序的此特定条目是非常方便的,因为它未被实现,因此不期望由“正常”SMB流量使用。有关如何将这个全局指针发现并泄漏回攻击者的详细信息将在下一节中介绍。
接下来,SMB_COM_TRANSACTION2设置的类型和子命令的消息TRANS2_SESSION_SETUP被发送到受害者,触发损坏的函数指针的执行。此消息的目标事务并不重要。下面可以看到一个示例数据包。
把所有的都放在一起
在本节中,我们将详细介绍这些漏洞,并了解上述构建的块如何组合以实现远程内核代码执行。
在这个阶段,一个TRANSACTION结构从受害者机器泄漏出来。这种结构可以以两种方式使用。首先,它包含EndpointSpinLock用作发现其他有用地址的基础的指针。第二,它被用作受害者#2事务,因为为了构建一个Read原语,攻击者需要一个有效TRANSACTION结构的细节。用于泄漏指针的方法类似于 Eternal Champion漏洞中描述的方法。
以下是SMB_COM_TRANSACTION包含泄露池内存的消息的内容。泄漏的TRANSACTION结构从偏移开始0xb0。我们可以看到,除其他事项外, 该事务包含 TID,PID,UID和OtherInfo。此外,诸如InData(offset 0x130)的指针允许攻击者确定事务的基本内存地址。
0000 ff 53 4d 42 a0 00 00 00 00 98 03 c0 00 00 00 00 .SMB............ 0010 00 00 00 00 00 00 00 00 00 08 37 ca 00 08 56 15 ..........7...V. 0020 12 00 00 00 04 00 00 00 c0 10 00 00 00 00 00 00 ................ 0030 48 00 00 00 04 00 00 00 58 01 00 00 48 00 00 00 H.......X...H... 0040 b8 10 00 00 00 59 01 00 fc 84 36 3a 10 77 98 5a .....Y....6:.w.Z 0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0060 00 01 02 03 46 72 61 67 00 00 00 00 00 00 00 00 ....Frag........ 0070 20 51 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Q.............. 0080 02 01 01 00 46 72 65 65 00 00 00 00 00 00 00 00 ....Free........ 0090 01 01 eb 03 4c 53 74 72 30 a1 07 00 83 fa ff ff ....LStr0....... 00a0 8c 0e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00b0 0c 02 8c 0e 00 00 00 00 d0 2d e6 00 83 fa ff ff .........-...... 00c0 90 bb e5 00 83 fa ff ff d0 2d 46 02 a0 f8 ff ff .........-F..... 00d0 d0 8d 41 02 a0 f8 ff ff 48 00 56 02 a0 f8 ff ff ..A.....H.V..... 00e0 48 c0 55 02 a0 f8 ff ff 00 00 00 00 00 00 00 00 H.U............. 00f0 00 00 02 00 00 00 00 00 68 b2 57 02 a0 f8 ff ff ........h.W..... 0100 6d 39 00 00 ff ff ff ff 00 00 00 00 00 00 00 00 m9.............. 0110 6c b2 57 02 a0 f8 ff ff 00 00 00 00 00 00 00 00 l.W............. 0120 6c b2 57 02 a0 f8 ff ff fc b2 57 02 a0 f8 ff ff l.W.......W..... 01306c b2 57 02 a0 f8 ff fffc bf 57 02 a0 f8 ff ff l.W.......W..... 0140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0150 00 0d 00 00 00 00 00 00 90 00 00 00 00 00 00 00 ................ 0160 00 00 00 00 01 01 00 00 00 0000 0837 ca00 08............7... 0170 5a 15 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Z............... 0180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0190 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
SMB_COM_TRANSACTION发送一系列消息以分配一对相邻的控制 – 受害者事务。具体来说,“groom”数据包包含SMB消息,用于创建数据包混淆,或换句话说,有资格成为控制事务。“Bride”数据包创建了一个作为破坏候选者的事务,即受害者事务。
漏洞利用了对R / W原语使用的邻居受害者事务的控制。
读取的原语是多次执行,以便发现srv!SrvTransaction2DispatchTable全局指针的位置,使用触发器shellcode执行。
读取的原语被多次使用以发现基础ntoskrnl.exe。上面发现的RWX内存被用作一个暂存页面,其中shellcode被写入和执行,返回值被存储。由于RWX部分存在此页面ntoskrnl.exe。值得注意的是ntoskrnl.exe,Windows 8.1及更高版本没有RWX部分。
kd> ?? 0xfffff802846f4000-0xfffff80284483000 unsigned int640x271000kd> !dh nt -s SECTION HEADER #3 RWEXEC name 1000 virtual size271000virtual address 0 size of raw data 0 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers E80000A0 flags Code Uninitialized Data Not Paged (no align specified)Execute Read Write
这是在受害者机器上复制和执行shellcode时的情况。首先,使用写入原语,将exploit shellcode复制到scratch页面。这个shellcode只是一个在池中分配内存的存根功能nt!ExAllocatePoolWithTag。然后,SMB_COM_TRANSACTION2发送一条消息来执行shellcode。返回值以临时页面上的固定偏移量保存,并使用读取原语泄漏回攻击者。我们可以看到stub功能如下:
; ; Retrieve the _KPRCB structure ; fffff802`846f400 65488b042520000000 mov rax,qword ptr gs:[20h] ; ; Access the PPNxPagedLookasideList.AllocateEx member ; fffff802`846f4009 4805b0080000 add rax,8B0h fffff802`846f400f 31c9 xor ecx,ecx ; ; Set NumberOfBytes (0xe4b) for size argument ; fffff802`846f4011 8b151e000000 mov edx,dword ptr [fffff802`846f4035] fffff802`846f4017 4883ec20 sub rsp,20h ; ; Call nt!ExAllocatePoolWithTag ; fffff802`846f401b ff10 call qword ptr [rax] fffff802`846f401d 4883c420 add rsp,20h ; ; Check for errors ; fffff802`846f4021 85c0 test eax,eax fffff802`846f4023 7407 je fffff802`846f402c ; ; Save the allocated memory address ; fffff802`846f4025 48890501000000 mov qword ptr [fffff802`846f402d],rax fffff802`846f402c c3 ret
最后,清除页面,将攻击者提供的shellcode写入池分配的页面,并发送一条消息以触发执行。
缓解措施对利用的影响
由于几个内核安全性的改进,利用此漏洞的技术已经不能直接适用于较新的平台了。特别是:
1、虚拟机管理程序强制的代码完整性(HVCI)可防止未签名的内核页面被执行,并防止攻击者复制和执行代码,即使存在RWX内存。
2、控制流程保护(CFG)可防止无效的间接函数调用,例如调用损坏的函数指针,此漏洞利用的技术可触发代码执行。