18届软件攻防赛现场赛awdp
字数 1375 2025-08-29 08:29:58
18届软件攻防赛现场赛AWDP题目"encoder"漏洞分析与利用教学
题目概述
这是一个来自第18届软件攻防赛现场赛AWDP环节的PWN题目,名为"encoder"。题目涉及二进制安全领域,主要考察参赛者对内存管理漏洞的理解和利用能力。
漏洞分析
1. 结构体定义
题目中定义了一个关键结构体:
struct su { // sizeof=0x2000000000
int size; // 0x0
char data[16]; // 0x4
// padding byte // 0x14
// padding byte // 0x15
// padding byte // 0x16
// padding byte // 0x17
void *ptr; // 0x18
};
2. 原始漏洞
在upload函数中存在一个关键漏洞:
- 无论size是否大于0x10,read操作的目标地址都是结构体内的堆地址(ptr)
- 按照逻辑,当size≤0x10时,数据应该写入结构体的data字段而非ptr指向的堆内存
3. 修复方案
修复方法很简单:确保当size≤0x10时,数据写入结构体的data字段而非ptr指向的内存。
漏洞利用分析
1. UAF (Use-After-Free)漏洞
在decode函数中存在UAF漏洞:
- 解密数据后可以触发一次free操作
- 如果解密后的size≤0x10,可以避免地址指针被ptr覆盖
- 这会导致UAF情况,因为可以继续使用已被释放的内存
2. 结合两个漏洞的攻击链
- UAF利用:通过decode函数造成UAF情况
- 堆覆盖:利用upload函数的漏洞,当size≤0x10时覆盖已释放chunk的前16字节
- Double Free:结合UAF和堆覆盖,构造double free漏洞
- 地址泄露:通过让两个结构体的堆指针指向同一chunk,free后可以泄露堆地址和libc地址
- 最终利用:修改tcache的fd为__free_hook,实现任意代码执行
漏洞利用代码(EXP)
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
from pwn import *
# 调试设置
# context(os = 'linux', arch = 'amd64', log_level = 'debug')
# context.terminal = ['tmux', 'splitw', '-h']
# 关键函数地址
menu = 0x00000000000013C9
encode0 = 0x0000000000018F9
decode0 = 0x0000000000002084
file_name = './pwn'
# 断点设置
b_string ="b main\n"
b_slice = [menu]
pie = 1
for i in b_slice:
if type(i) == int and pie:
b_string += f"b *$rebase({i})\n"
elif type(i) == int :
b_string += f"b *{hex(i)}\n"
else :
if type(i) == str:
b_string += f"b *"+i+f"\n"
# 选择攻击模式
# 1 => attach
# 2 => debug
# 3 => remote
choice = 1
if choice == 1:
p = process(file_name)
# gdb.attach(p,b_string)
print(f"Break_point:\n"+b_string)
elif choice == 2:
p = gdb.debug(file_name,b_string)
攻击步骤详解
-
触发UAF:
- 通过decode函数解密数据,设置size≤0x10
- 这将导致ptr被释放但结构体仍保留对它的引用
-
堆内存覆盖:
- 利用upload函数的漏洞,当size≤0x10时写入数据
- 由于漏洞存在,数据会写入ptr指向的已释放内存
- 覆盖chunk的fd指针等关键数据
-
构造Double Free:
- 通过精心设计的内存操作,使同一内存块被多次释放
- 这破坏了堆管理器的正常状态
-
地址泄露:
- 通过控制堆布局,使两个结构体的ptr指向同一内存块
- 释放其中一个后,可以读取到堆管理器的元数据
- 从而泄露堆地址和libc地址
-
劫持控制流:
- 修改tcache的fd指针指向__free_hook
- 覆盖__free_hook为system或其他目标函数
- 触发free时即可执行任意代码
防御建议
-
修复upload函数:
- 严格区分size≤0x10和size>0x10的情况
- 确保小数据写入data字段而非ptr指向的内存
-
加强内存管理:
- 在free后立即将指针置NULL
- 使用内存管理包装器跟踪所有分配和释放操作
-
输入验证:
- 对所有输入数据进行严格验证
- 特别是size参数的范围检查
-
使用安全机制:
- 启用堆保护机制如FORTIFY_SOURCE
- 考虑使用现代内存分配器如scudo或jemalloc
总结
这道题目展示了如何通过结合多个看似小的漏洞(UAF和错误的内存访问)来构造强大的攻击链。它强调了在安全编程中需要严格管理内存生命周期,验证所有输入,以及防御深度的重要性。对于二进制安全学习者来说,理解这类漏洞的成因和利用方式对于提高代码安全性和漏洞挖掘能力都至关重要。