手把手教你如何用 ROP 绕过数据执行保护 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

手把手教你如何用 ROP 绕过数据执行保护

xiaohui 技术 2023-02-17 12:00:00
94334
收藏

导语:在这篇文章中,我们将介绍应用程序的逆向过程,以发现缓冲区溢出漏洞,并开发用于绕过DEP的ROP小工具链(Gadget Chain)。

DEP(数据执行保护)是一种内存保护功能,允许系统将内存页标记为不可执行。ROP(面向返回的编程)是一种利用技术,允许攻击者在启用DEP等保护的情况下执行shellcode。在这篇文章中,我们将介绍应用程序的逆向过程,以发现缓冲区溢出漏洞,并开发用于绕过DEP的ROP小工具链(Gadget Chain)。

我们将在开发过程中使用以下工具:QuoteDB、TCPView(一个查看端口和线程的小工具,只要木马在内存中运行,一定会打开某个端口,只要黑客进入你的电脑,就有新的线程)、IDA Freeware、WinDbg(在windows平台下,强大的用户态和内核态调试工具)和rp++。

QuoteDB是一个设计上易受攻击的应用程序,创建它是为了实践逆向工程并利用它进行开发。如下图所示,该应用程序正在侦听端口3700上的网络连接:

1.jpg

我们已经使用TCPView确认程序确实在监听端口3700。

2.jpg

现在我们需要对应用程序进行逆向工程,看看它是如何处理网络连接的。accept函数用于允许指定端口上的传入连接,然后进程创建一个运行“handle_connection”例程的新线程,如下所示:

3.jpg

recv函数用于从连接的套接字接收数据:

4.jpg

我们已经开发了一个基本的Python脚本,它创建一个TCP套接字,并在端口3700上向远程服务器发送1000个“a”字符:

5.jpg

我们已将WinDbg附加到QuoteDB.exe进程,并列出了加载的模块,如下图所示。

6.jpg

我们可以使用“bp”命令在recv函数调用后放置断点,使用“bl”命令确认断点已成功设置:

7.jpg

recv函数返回后,EAX寄存器包含以十六进制接收的字节数:

8.jpg

缓冲区的前4个字节表示一个操作码,该操作码被移到EAX寄存器中,然后打印在命令行中:

9.jpg

下图显示了WinDbg中的printf调用,我们可以观察到第三个参数(=Opcode)由4个“A”字符组成:

10.jpg

该进程显示源IP地址、源端口、缓冲区长度和十进制的操作码:

11.jpg

应用程序从Opcode中减去0x384(十进制900),并将结果与4进行比较。这是一个带有5个示例的开关,也显示在下图中。

12.jpg

EAX寄存器大于4,执行流被重定向到默认情况,该情况调用“log_bad_request”函数:

13.jpg

上述函数包含缓冲区溢出漏洞,如下图所示,可执行文件在堆栈上分配0x818(2072)字节,用0初始化缓冲区,并在不检查边界的情况下将有效负载复制到此缓冲区:

14.jpg

发生溢出是因为要复制的字符数(0x4000)大于缓冲区的大小,并且可能会重写返回地址:

15.jpg

我们选择发送3000个“A”字符以利用该漏洞。如下所示,返回地址在堆栈上被重写,程序因此崩溃:

16.jpg

17.jpg

我们使用了“msf-pattern_create”命令来生成一个唯一的模式,该模式将为我们提供偏移量。

18-1536x180.jpg

应用程序在不同的地址崩溃,该地址用于使用“msf-pattern_offset”命令确定精确的偏移量:

19.jpg

20.jpg

我们修改了概念证明,以包括上述偏移量。在正确的地址崩溃后,ESP寄存器指向我们控制的缓冲区的最后一部分:

21.jpg

22.jpg

我们使用了narly WinDbg扩展来显示加载的模块及其内存保护,下图显示了该可执行文件是在启用ASLR和DEP保护的情况下编译的。

23.jpg

Windows Defender Exploit Guard可以用来启用/禁用ASLR。我们需要进入“Exploit protection settings”,选择“Program settings”页签,点击“Add Program to custom”,选择“Choose exact file path”选项:

24.jpg

25.jpg

我们想通过发送从“\x00”到“\xFF”的所有字节并确定它们如何写入堆栈来找出哪些字符被认为是“不适合”的:

26-1.jpg

如下图所示,没有不适合的字符,不过为了研究,我们将“\x00”视为不适合字符,因为它通常是不适合字符。正因为如此,漏洞开发过程稍微复杂一些,但它可能更容易适应其他应用程序。

27.jpg

我们使用rp++工具从“SysWOW64\kernel32.dll”模块中提取ROP小工具,因为ASLR是禁用的,所以我们可以选择任何提供必要ROP小工具的DLL,但是,我们将在以后的文章中看到应用程序泄漏特定DLL中的地址。我们已将小工具中的最大指令数设置为5:

28.jpg

29.jpg

由于DEP保护,堆栈不再是可执行的,我们需要找到执行shellcode的方法。我们可以使用VirtualAlloc、VirtualProtect和WriteProcessMemory等API来绕过DEP。VirtualAlloc函数用于保留、提交或更改进程地址空间中页面的状态。该函数有4个参数:

lpAddress
dwSize
flAllocationType
flProtect

我们的目的是将flAllocationType参数设置为0x1000(MEM_COMMIT),将flProtect设置为0x40(PAGE_EXECUTE_READWRITE)。我们需要在堆栈上创建以下框架:

VirtualAlloc address
Return address (Shellcode address)
lpAddress (Shellcode address)
dwSize (0x1)
flAllocationType (0x1000)
flProtect (0x40)

我们为每个元素分配了一个特定的值,需要在运行时使用正确的值对其进行修改。

30-1.jpg

如下图所示,可以在ESP寄存器的固定偏移处找到框架:

31.jpg

kernel32.dll模块的起始地址可以使用WinDbg来标识。所有ROP小工具的地址必须使用该值而不是“ROP.txt”文件中的加载地址来计算:

32.jpg

首先,我们需要找到一个保存ESP寄存器值的ROP小工具。我们确定了一个将ESP寄存器复制到ESI寄存器的寄存器:

33.jpg

我们修改了Python脚本,以包含kernel32地址和上述ROP小工具偏移量,如下所示:

34-1.jpg

我们已经成功地将执行流程重定向到我们的第一个ROP小工具,接着将其他ROP小工具链接在一起,因为ESP仍然指向我们的缓冲区:

35.jpg

36.jpg

现在我们需要找到从ESI寄存器中减去0x1C的方法。然而,由于缺少涉及使用ESI寄存器进行计算的ROP小工具,我们找到了一个将ESI寄存器复制到EAX中的ROP小工具。唯一的问题是ESI也被“POP ESI”指令修改,但是,它不会影响我们的利用:

37.jpg

38.jpg

在许多ROP小工具中发现的另一个寄存器是ECX。我们已经确定了一个ROP小工具,它从堆栈中弹出一个值到ECX寄存器中,另一个小工具将EAX和ECX寄存器加在一起。加上负值等于减去相同的正值:

39.jpg

40.jpg

通过在之前的EAX值上添加-0x1C (= ECX)值,EAX指向VirtualAlloc框架:

41.jpg

因为EAX在任何计算中都很有用,所以我们需要在执行任何其他操作之前找到保存它的方法。我们发现了一个ROP小工具,它将EAX寄存器复制到ECX中,ECX将用于修改框架中的值。事实上,EAX也被这个ROP小工具修改了,但这并不影响我们的利用:

42.jpg

我们修改后的概念证明如下图所示。“junk”值对堆栈对齐很有用,对应于“POP reg”和“retn4”指令。

43.jpg

再次运行Python脚本后,我们可以观察到ECX寄存器的值与之前的EAX寄存器相同,并指向VirtualAlloc框架:

44.jpg

IAT(导入地址表)包含指向由其他DLL导出的函数的指针。例如,kernel32.dll在VirtualAlloc的IAT中有一个条目,即使VirtualAlloc实际地址发生变化,该条目也保持不变:

45.jpg

我们使用了“POP EAX”指令将VirtualAlloc IAT复制到EAX寄存器中,需要对其进行解除引用才能获得VirtualAlloc地址,如下所示:

46.jpg

47.jpg

在更新Python脚本并再次运行之后,我们成功地获得了EAX中的VirtualAlloc地址:

48.jpg

因为ECX仍然指向VirtualAlloc框架,所以我们需要一个包含“MOV [ECX], EAX”的ROP小工具,以便使用VirtualAlloc地址更新第一个框架值:

49.jpg

50.jpg

我们需要找到一种方法来修改ECX寄存器以指向下一个框架值。“INC ECX”指令用于将1添加到ECX寄存器,我们已使用了其中的4个:

51.jpg

52.jpg

如下图所示,ECX指向下一个需要修改的元素:

53.jpg

第二个框架值对应于shellcode地址。第一个ROP小工具将ECX寄存器复制到EAX中。我们的想法是将shellcode放在有效载荷中ROP小工具之后,这将代表一个比当前更高的地址。我们已经从EAX寄存器中减去了一个负偏移量(-0x210),现在EAX指向一个可以用我们的shellcode填充的缓冲区:

54.jpg

55.jpg

56.jpg

57.jpg

使用以前的ROP小工具,我们更新了框架中的第二个值,现在看起来如下:

58.jpg

第三个骨架值(lpAddress)也应该等于shellcode地址。类似地,我们从EAX寄存器中减去了一个不同的偏移量(-0x20c),因为EAX增加了4。你可能会注意到,两次执行之间的堆栈地址是不同的,但偏移量保持不变:

59.jpg

第四个框架值(dwSize)应初始化为1,由于我们认为“\x00”是一个不适合字符,我们不能只将所需的值放在堆栈上,因为它包含NULL字节。我们已使用“NEG EAX”指令让-1 = 0xFFFFFFFF 无效,并获得所需值:

60.jpg

61.jpg

第五个框架值(flAllocationType)应设置为0x1000。我们需要找到两个和为0x1000的十六进制数,我们考虑number1=0x88888888。通过简单的数学运算,我们可以确定第二个数字必须是0x77778778,如下所示:

62.jpg

我们使用“POP reg”指令将这两个数字复制到EAX和ESI中,并使用另一个ROP小工具执行添加操作,如下图所示。

63.jpg

我们几乎完成的VirtualAlloc框架如下所示:

64.jpg

最后一个框架值(flProtect)应初始化为0x40。我们已经提供了获得所需结果的必要步骤:

65.jpg

66.jpg

最后,我们需要找到一种使用修改后的参数执行VirtualAlloc函数的方法。指向最后一个框架值的ECX寄存器被复制到EAX寄存器中,EAX寄存器需要减去0x14(框架中的6个元素)才能指向VirtualAlloc框架的第一个值。“xchg”指令用于交换EAX寄存器和ESP寄存器的内容,从而执行VirtualAlloc函数:

67.jpg

ROP小工具链的最后一部分如下所示:

68.jpg

执行VirtualAlloc API后,我们可以看到缓冲区现在是可执行的:

69-1.jpg

70.jpg

我们可以确定shellcode地址和最后一个ROP小工具之间的距离是0x9C字节。

71.jpg

我们添加了0x9C填充字节,然后添加了一个包含NOP指令的伪shellcode,以确认偏移量是否正确:

72-1536x365.jpg

我们已经使用msfvenom生成了一个逆向shell,并成功地执行了攻击。通过将多个执行VirtualAlloc调用的ROP小工具链接在一起,并将包含shellcode的内存页作为可执行文件来绕过。

73.jpg

74.jpg


本文翻译自:https://cybergeeks.tech/a-step-by-step-introduction-to-the-use-of-rop-gadgets-to-bypass-dep/如若转载,请注明原文地址
  • 分享至
取消

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

扫码支持

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

发表评论

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