前提知识
什么是ret2text
ret2text
,即return to text
,意思是劫持函数返回地址为程序本身就有的代码(.text
),进而控制程序执行后门函数,拿到shell
。
strcpy
函数溢出
当源字符串的长度大于目标缓冲区时,
strcpy
函数会复制整个源字符串,导致栈溢出。strcpy
函数原型
char* strcpy(char* destination,const char* source); |
ret2text
题目类型
第一种是直接劫持返回地址为system("/bin/sh")
的地址。
第二种比第一种稍微复杂了一些,虽然有system
函数但是参数不是我们想要的 ,而在程序的其他地方也能找到/bin/sh
字符串。这时就需要利用一些gadget
给system
函数传参。而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
寄存器中。
例题
例题1:buu
jarvisoj_level0
拿到题目后先checksec level0
命令检查一下保护,PIE
和canary
都没开,开了NX
(堆栈不执行)保护。该程序为64位。
canary
保护:函数开始执行的时候会先往栈里插入canary
值,当函数真正返回的时候会验证canary
值是否合法,如果不合法就停止程序运行。可以防止栈溢出覆盖返回地址。

定义数组buf[128]
,read
函数读取,且0x200uLL
大于128,可以栈溢出。通过下图
计算偏移量,为0x80+8
该题是第一种类型,直接溢出并劫持返回地址为system
函数地址0x400596,即可。
最后exp
from pwn import * |
得到flag
例题2:buu
jarvisoj_level2_x64
拿到题目后先检查一下保护,PIE
和canary
都没开,开了NX
。
把题目放入ida
里,查看

定义了一个数组buf[128]
,有read
函数0x200uLL
明显大于128,可以栈溢出。计算偏移量,为0x80+8
有system
函数还是两个,但是参数都不是我们想要的。再查看一下字符串(shift+F12
)
很好,有/bin/sh
点进去查看地址,为0x600a90,随便选一个system
的地址为0x400603
我们要做的就是通过pop rdi;ret
把/bin/sh
传参给system
函数,通过ROPgadget --binary level2_x64 | grep "pop rdi"
命令得到pop rdi ; ret
地址,如下
最后exp
from pwn import * |
得到flag
。
例题3:buu
jarvisoj_level2
前面几步都同上,我就不再多说。
如下,system
的地址0x0804849e,偏移量0x88+4
|
得到flag
。