二进制安全 (PWN) 专项强化练习
“在内存的荒原上,每一比特都是博弈的筹码。” —— 本专题涵盖缓冲区溢出、格式化字符串及堆漏洞利用,侧重 Linux 环境下的 C/C++ 内存安全分析。
🪜 练习阶梯与评价标准
| 等级 | 难度目标 | 核心考察点 | 期望达成 |
|---|---|---|---|
| ● Level A | 内存基础破坏 | 栈溢出 (Ret2text)、Shellcode 注入 | 理解函数调用栈与返回地址覆写 |
| ● Level B | 现代保护绕过 | ROP 链构造、Ret2libc、格式化字符串漏洞 | 掌握绕过 NX、ASLR 等保护的方法 |
| ● Level C | 堆利用与内核 | Use-After-Free、Heap Overflow、Kernel PWN | 理解堆分配器 (glibc malloc) 机制 |
🎯 考点覆盖模型 (Knowledge Matrix)
| 知识模块 | 核心考点 | 关联习题 | 推荐等级 |
|---|---|---|---|
| 栈溢出 | 返回地址覆盖、JMP ESP 技巧 | 练习 1 | Level A |
| ROP 链 | Gadget 寻找、系统调用号配置 | 练习 2 | Level B |
| 格式化字符串 | 任意地址读/写、 Canary 泄露 | 练习 3 | Level B |
| 堆安全 | Bin 链表破坏、Tcache poisoning | 练习 4 | Level C |
| 安全机制 | NX/DEP、ASLR 绕过策略 | 练习 5 | Level B |
📂 核心习题库
Level A:基础巩固 (Foundations)
练习 1:经典的栈溢出模拟 (Buffer Overflow)
题目描述:给定一个 C 程序片段,输入缓冲区为 64 字节,目标是覆写返回地址以执行 get_shell() 函数。
Check Solution (Logic & C++ Simulation)
漏洞代码 (Vulnerable Snippet):
#include <iostream>
#include <cstring>
void get_shell() {
std::cout << "Success! Shell Spawned." << std::endl;
}
void vulnerable_function() {
char buffer[64];
// 使用危险的 gets (模拟)
std::cin >> buffer;
}
int main() {
vulnerable_function();
return 0;
}
利用思路:
- 偏移计算:Payload =
A* 64 +EBP(4/8字节) +get_shell_address。 - 栈布局分析:当
buffer溢出时,数据会向上覆盖栈帧中的Saved EBP和Return Address。
防御修复:
- 使用
fgets()或std::string限制输入长度。 - 开启
Stack Canary(Stack Cookie) 保护。
Level B:综合提升 (Intermediate)
练习 2:Ret2libc 绕过 NX 保护
题目描述:当栈不可执行 (NX Enabled) 时,无法直接执行 Shellcode。如何利用 libc 中的 system("/bin/sh") 实现 RCE?
Check Solution
利用逻辑:
- 泄露基址:利用
puts或printf泄露一个已知函数的 GOT 地址。 - 计算偏移:
libc_base = leaked_addr - offset_in_libc。 - 寻找 Gadget:寻找
pop rdi; ret等片段,将"/bin/sh"的地址放入RDI寄存器作为system的参数。 - 触发调用:返回地址覆盖为
system_addr。
练习 3:格式化字符串任意地址写
题目描述:程序存在 printf(user_input)。如何利用 %n 修改变量 count 的值为 100?
Check Solution
Payload 结构:
Payload: [count_address] + %96c + %k$n
- 其中
k是count_address在栈上的偏移位置。 %96c输出了 96 个字符,加上前面地址的 4 字节(32位下),共 100 字节。%n将已输出的字节数 (100) 写入到第k个参数对应的地址中。
Level C:竞赛挑战 (Advanced)
练习 4:Use-After-Free (UAF) 基础
题目描述:简述 UAF 漏洞的成因,并说明如何通过劫持虚函数表 (vtable) 实现代码执行。
Check Solution
核心原理解析:
- 成因:程序释放 (free) 了堆内存,但未将指向该内存的指针置为 NULL。随后程序再次访问 (use) 该指针。
- 劫持 vtable:
- 申请一个包含虚函数的 C++ 对象 A。
- 释放对象 A。
- 申请一个大小相同的数据块 B,并填入精心构造的伪造虚函数表地址。
- 调用对象 A 的虚函数,此时 CPU 会跳转到 B 中指定的恶意地址。
🏆 实验室规范
- 工具链:掌握
gdb-pwndbg、pwntools与checksec的协同使用。 - 底层视角:理解汇编指令 (
push,pop,call,ret) 对栈指针ESP/RSP的真实影响。 - 安全开发:PWN 的终点是安全。所有漏洞利用应伴随对
FORTIFY_SOURCE等现代防御机制的研究。