原理
ret2libc,即控制程序执行libc中的函数。通常是返回至某个函数的plt处或函数的具体位置(即函数对应的got表项的内容)。
一般情况下,我们会选择执行system(“/bin/sh”)。所以,我们需要知道system函数的地址。
下面由易到难给出三个ret2libc的例子:
例1
这里使用ctf wiki上的一个例题(点我下载)。
checksec检查保护
检查保护,发现开启了NX保护。
IDA分析
gets函数存在栈溢出漏洞。
寻找/bin/sh
利用ROPgadget查看是否有 /bin/sh 存在:
发现**/bin/sh**的地址是0x08048720。
寻找system
继续查找system函数:
发现system函数的地址是0x08048460。
构造思路
通过栈溢出覆盖返回地址到system函数地址,然后在后面的一个字节中写入虚假的返回地址,后面写入system函数的参数(即/bin/sh的地址)。
编写脚本
1 | from pwn import * |
这题的特点是:给出了 /bin/sh 字符串 和 system函数。
例2
这里使用ctf wiki上的一个例题(点我下载)。
本题和例1基本一样,不同之处在于没有 /bin/sh 字符串。
使用checksec查看保护
依旧开启了NX保护,栈上不可执行。
IDA分析
发现存在gets函数栈溢出漏洞。
寻找/bin/sh
寻找/bin/sh字符串,发现没有。
寻找system函数
system函数的地址:0x08048490。
写入 /bin/sh
找到gets函数地址,我们可以自己写入 /bin/sh。
gets函数的地址:0x08048460
在bss段找到全局变量buf2的地址:0x0804A080。
构造思路
现在,我们拥有了gets函数、system函数、buf2的地址。我们只需要利用gets函数将**/bin/sh写入buf2,然后调用system函数,让buf2作为system函数的参数**即可成功执行shell。
编写脚本
1 | from pwn import * |
本题的特点是:有system函数地址,没有 /bin/sh 字符串。
例3
这里使用ctf wiki上的一个例题(点我下载)。
此题的特点在于:既没有system,也没有 /bin/sh。
checksec查看保护
查看保护,依旧是NX保护。
IDA分析
依旧存在gets函数栈溢出漏洞。
我们需要做的是:
- 找到system函数的地址
- 找到/bin/sh的地址
寻找system函数地址
首先,需要知道以下两个知识点:
- system函数属于libc,而libc.so动态链接库中的函数之间相对偏移固定。
- 即使程序有ASLR保护,也只是针对地址中间位进行随机,最低的12位不变。
如果我们知道libc中某个函数的地址,就可以确定libc中其它函数地址。
如何得到libc中某个函数的地址呢?我们常使用got表泄露,即输出某个函数got表项的内容。
由于libc的延迟绑定机制,我们需要泄露已执行过的函数。
但是,这样手动操作过于麻烦,我们使用libc利用工具:LibcSearcher
构造思路
我们泄露__libc_start_main的地址,因为它是程序最初被执行的地方:
- 泄露__libc_start_main地址
- 通过LibSearcher获取libc版本(有了版本就知道相对偏移地址)
- 获取 system函数 的地址 和 /bin/sh 的地址
- 再次执行程序
- 触发栈溢出执行 system(‘/bin/sh’)
编写脚本
1 | from pwn import * |
本题既没给system,也没给/bin/sh。
需要我们通过已泄露的函数地址去libc中寻找system函数和/bin/sh地址。
然后通过栈溢出漏洞控制程序执行system函数拿下shell。
(目前新题都会给libc文件,不需要我们去找libc版本)
在线LibcSearcher: