【学习记录】Week14(一):信息泄露系统化方法论与 ORW 基础体系构建
写在前面:欢迎进入 Week14 的学习!在过去的几周里,我们深潜了 glibc 堆管理器(Week11-12)和 IO_FILE 机制(Week13),掌握了极其强大的内存破坏与控制流劫持手段。然而,无论多精妙的利用链,都建立在一个绝对前提之上:我们需要知道东西在哪(Libc 基址、堆基址、栈基址)。本周我们将跳出单一漏洞类型的局限,转向系统化的漏洞利用工程学。今天,第一篇将为你梳理信息泄露的系统化方法论,并以此为基础,引入现代 CTF 绝对的高频考点——ORW(Open-Read-Write)任意文件读取体系。
📑 目录
- 破局之基:为什么信息泄露是 PWN 的生命线?
- 🎯 信息泄露系统化 Checklist(实战速查表)
- 沙箱降临:为什么
execve逐渐失效? - ORW 基础架构:从系统调用到 ROP 链构造
- 总结与下篇预告
1. 破局之基:为什么信息泄露是 PWN 的生命线?
现代操作系统普遍开启了 ASLR(地址空间随机化)、PIE(程序代码段随机化)和 NX(不可执行栈)。在没有泄露的情况下,我们手里的漏洞(如栈溢出、UAF)就像是黑夜中拿着一张藏宝图,但既不知道自己在哪,也不知道宝箱在哪。
- 不知道 Libc 基址:找不到
system和/bin/sh,无法 ret2libc。 - 不知道堆基址:无法计算 Tcache 异或密钥(Safe-Linking),无法精准伪造堆块。
- 不知道栈基址:无法进行栈迁移,无法将 ROP 链写到正确位置。
因此,打 PWN 的第一步,永远是寻找泄露原语。掌握如何从各种残缺的内存环境中“榨取”地址,是成熟 PWN 选手的基本素养。
2. 🎯 信息泄露系统化 Checklist(实战速查表)
💡强烈建议将本节内容保存为你的个人 Checklist,打题时按图索骥。
2.1 泄露 Libc 地址
| 触发场景 / 漏洞类型 | 泄露目标 | 原理与操作 |
|---|---|---|
| Unsorted Bin 残留 | main_arena+88 | 释放一个大于 Fastbin/Tcache 大小的 chunk,其fd和bk指向 Unsorted Bin 链表头。通过 UAF 或 Show 功能读取。 |
| GOT 表读取 | libc 函数真实地址 | 程序若有puts/printf配合格式化字符串或任意读,直接读取 GOT 表中已解析的函数(如puts@got),查 Libc 数据库。 |
stdout低位覆盖 | libc 内部地址 | 溢出覆盖_IO_2_1_stdout_的_IO_write_base低位,触发puts打印出 libc 数据段残留的指针。 |
| Partial Overwrite | 无需泄露,直接爆破/偏移 | 在不知道 Libc 版本时,只覆盖返回地址末尾 1-2 字节,跳转到 libc 中的one_gadget或特定代码段(需配合环境搭建)。 |
2.2 泄露栈 地址
| 触发场景 / 漏洞类型 | 泄露目标 | 原理与操作 |
|---|---|---|
格式化字符串 (%p) | 栈上任意数据 | printf(fmt)的变参在栈上,通过%p或%N$p依次泄露栈上的返回地址、旧 RBP 等。 |
environ指针读取 | 栈基址 | libc 全局变量environ存放了环境变量数组的栈地址。通过任意读读取environ的值,即可算出当前栈帧位置。 |
| 栈残留数据 | 旧 RBP / 返回地址 | 程序再次调用 read/gets 时,若缓冲区未清空,残留的旧栈帧数据可能被随后的puts打印出来。 |
2.3 泄露堆 地址
| 触发场景 / 漏洞类型 | 泄露目标 | 原理与操作 |
|---|---|---|
| Tcache / Fastbin 残留 | 堆块地址 | 释放单个 chunk 进入 Tcache/Fastbin,其fd指针指向下一个堆块(或 NULL)。通过 UAF 读取。 |
| Safe-Linking 泄露 | 堆基址 >> 12 | glibc 2.32+ 中,Tcache 的fd被加密为(chunk_addr >> 12) ^ next_ptr。若链表仅有一个 chunk,fd即为堆基址的明文右移。 |
Largebinfd_nextsize | 堆块地址 | Largebin 中的 chunk 含有fd_nextsize和bk_nextsize指针,必定指向堆区,UAF 读取即可。 |
2.4 无 Libc 环境的特殊泄露
- DynELF 技术:通过
puts或write的任意读漏洞,不断泄露内存,暴力搜索 ELF 动态链接结构,远程还原 libc(耗时极长,但有效)。 - 延迟绑定机制攻击:通过覆盖
.got.plt的特定字节,强制触发_dl_runtime_resolve,泄露动态链接器内部地址。
3. 沙箱降临:为什么execve逐渐失效?
在掌握信息泄露后,我们通常的目标是执行system("/bin/sh")或execve("/bin/sh", NULL, NULL)。这对应着系统调用号59。
然而,在 2020 年以后的 CTF 赛事中,出题人越来越频繁地引入Seccomp (Secure Computing Mode)沙箱保护。Seccomp 通过 BPF(伯克利包过滤器)在内核态拦截系统调用。
常见的沙箱规则:
line 1: ALLOW syscalls: open, read, write, mmap, mprotect... line 2: KILL syscalls: execve, fork, clone...一旦execve被拦截,程序直接被内核 SIGKILL。我们精心构造的one_gadget或system全部失效。
出路何在?既然不能执行 shell,我们就自己写代码把服务器上的flag文件读出来打印到屏幕上。这就是ORW。
4. ORW 基础架构:从系统调用到 ROP 链构造
ORW 是 Open-Read-Write 的缩写,是一套标准的文件读取流程:
open("/flag", 0)-> 返回文件描述符fd(通常是 3)read(3, buf, 0x100)-> 将 flag 内容读到可写内存buf(如 bss 段或堆)write(1, buf, 0x100)-> 将buf内容写到标准输出 (fd=1)
4.1 构造 ORW 的前置条件
- 能够执行 ROP:栈溢出,或者堆漏洞结合
setcontext/IO_FILE 实现栈迁移。 - 知道 Libc 地址:用于寻找
syscall; ret等 gadget,以及/flag字符串(或自己写入)。 - 控制寄存器:能够控制
rdi, rsi, rdx, rax这四个关键寄存器。
4.2 ROP 链构造模板 (x64)
假设我们已经能控制栈并执行 ROP,且已知 Libc 基址。我们需要以下 Gadgets:
pop rdi; retpop rsi; retpop rdx; ret(如果没有,可使用__libc_csu_init中的万能 gadget)pop rax; ret(用于设置系统调用号)syscall; ret
完整 ROP 链伪代码:
// 假设 bss_addr 是一块可写内存,用于存放 "/flag" 字符串和读取的数据 // open("/flag", 0) -> sys_open = 2 pop rdi; ret; bss_addr; // rdi = "/flag" 字符串地址 pop rsi; ret; 0; // rsi = O_RDONLY pop rax; ret; 2; // rax = 2 (sys_open) syscall; ret; // read(3, bss_addr+0x100, 0x100) -> sys_read = 0 pop rdi; ret; 3; // rdi = fd (open返回值为3) pop rsi; ret; bss_addr + 0x100; // rsi = 缓冲区地址 pop rdx; ret; 0x100; // rdx = 读取长度 pop rax; ret; 0; // rax = 0 (sys_read) syscall; ret; // write(1, bss_addr+0x100, 0x100) -> sys_write = 1 pop rdi; ret; 1; // rdi = fd (stdout) pop rsi; ret; bss_addr + 0x100; // rsi = 缓冲区地址 pop rdx; ret; 0x100; // rdx = 写入长度 pop rax; ret; 1; // rax = 1 (sys_write) syscall; ret;漏洞触发: 栈溢出/堆劫持
栈迁移到可控内存
布置 ROP 链
open 打开 /flag
read 读入到 bss/heap
write 打印到屏幕
获取 Flag
4.3 常见难点与对策
- 没有
/flag字符串:ROP 链前面加上read(0, bss_addr, 8),自己从键盘输入/flag\x00写入 bss 段。 - 没有
pop rdx; ret:利用libc_csu_init中的mov rdx, r15; call [r12]进行复杂调用;或者利用read函数本身,因为read的第三个参数在调用前可能残留了合适的值。 - 没有
syscall:如果只禁用了execve而没有禁用open,可以直接调用 libc 中的open函数。
5. 总结与下篇预告
5.1 核心知识点总结
- 信息泄露是地基:牢记 Unsorted Bin 泄露 Libc、
environ泄露栈、Tcache 残留泄露堆的系统化方法论。 - 沙箱逼出 ORW:Seccomp 禁用
execve是现代出题趋势,必须掌握 ORW 体系。 - ORW 的本质:通过控制寄存器执行
open/read/write三个系统调用,核心在于对pop rdi/rsi/rdx/rax; syscall链的拼装。
5.2 下篇预告
在下一篇中,我们将深入探讨沙箱的分析与绕过技术。
- 如何使用
seccomp-tools工具逆向分析题目的沙箱规则。 - 进阶 ORW:如果
open也被禁用了怎么办?(利用openat或/proc/self/mem魔法)。 ret2mprotect进阶应用:在无libc环境下绕过 NX 执行 shellcode。
结语:信息泄露 Checklist 是你的“寻宝图”,ORW 是你的“破墙锤”。当你把这两者烂熟于心时,无论题目套了多少层保护,你都能找到一条通往 flag 的物理路径。
