其他
任意写原语转 RCE:按 glibc/RELRO 选择…
已有任意写原语(堆、格式化字符串或越界写)时,按 glibc 版本、RELRO 和可用泄露选择覆写目标并触发代码执行。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 48/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 55/100 ×0.15
适用场景
任意写原语转 RCE:按 glibc/R通用
解决思路
- 先按 glibc 版本和 ELRO 选择覆写目标:GOT overwrite 在所有版本可用但需 Partial RELRO;__malloc_hook/__free_hook/__realloc_hook 仅在 glibc < 2.34 可用;_IO_FILE 直接 vtable 覆写在 <2.24 可用,2.24+ 有 vtable range check;_IO_str_jumps 在 2.24–2.27 可用,之后 patched;_IO_wfile_jumps 在 ≥2.28 可用;__exit_funcs、_dl_fini/link_map、modprobe_path、.fini_array、C++ vtable、setcontext、栈返回地址各有版本/保护要求。
- GOT overwrite 前置:目标二进制为 Partial RELRO,.got.plt 可写;Full RELRO 会完全阻止。
- GOT 常见覆写:printf@GOT -> system,触发下一次 printf(user_input) 且输入为 /bin/sh。
- GOT 常见覆写:free@GOT -> system,触发 free(ptr) 且 ptr 指向 '/bin/sh'。
- GOT 常见覆写:strlen@GOT -> system,触发 strlen(user_input)。
- GOT 常见覆写:atoi@GOT -> system,触发 atoi(user_input) 且输入为 'sh'。
- GOT 常见覆写:puts@GOT -> system,触发 puts(user_input)。
- GOT 常见覆写:exit@GOT -> main 或 gadget,创建循环以进行 multi-shot exploit。
- GOT 常见覆写:__stack_chk_fail@GOT -> ret gadget,消除 canary 检查。
- 格式化字符串 GOT 覆写:from pwn import fmtstr_payload; payload = fmtstr_payload(offset, {elf.got['printf']: libc.sym['system']})。
- 堆式 GOT 覆写:用 tcache poisoning 分配 chunk 到 GOT 地址,然后写入 system 地址。
- glibc < 2.34 的 __malloc_hook:将 __malloc_hook 覆写为 one_gadget 地址,触发任意 malloc 调用,包括 printf 大格式串内部 malloc;例如 io.sendline('%100000c')。
- glibc < 2.34 的 __free_hook:将 __free_hook 覆写为 system,然后释放包含 '/bin/sh' 的 chunk。
- one_gadget 约束不满足时的 realloc trick:realloc 会在调用 __realloc_hook 前压栈并调整栈;设 __realloc_hook = one_gadget,__malloc_hook = libc.sym['realloc'] + 2(或 +4、+6 等调整栈对齐)。
- _IO_FILE vtable 按版本利用:glibc <2.24 直接覆写 vtable 指向假表,在 __overflow 偏移放 system。
- _IO_FILE vtable 按版本利用:glibc 2.24–2.27 用 _IO_str_jumps,_IO_str_finish 调用 _s._free_buffer。
- _IO_FILE vtable 按版本利用:glibc ≥2.28 用 _IO_wfile_jumps,宽字符路径 _wide_data->_wide_vtable 不做范围检查。
- _IO_FILE vtable 按版本利用:glibc ≥2.35 用 House of Cat,走 _IO_wfile_seekoff -> _IO_switch_to_wget_mode -> 假 wide vtable 调用。
- FSOP 触发:覆写 _IO_list_all 指向伪造 FILE 与构造好的 vtable,通过 exit() 或 malloc abort 触发 _IO_flush_all_lockp -> _IO_OVERFLOW。
- __exit_funcs/__atexit:__exit_funcs 是 exit() 期间调用的函数指针链表,每条含 flavor 和函数指针;函数指针用 pointer guard 混淆,公式 stored = ROL(ptr ^ __pointer_chk_guard, 0x11)。
- __exit_funcs 利用需要 libc base 和 __pointer_chk_guard 值(位于 fs:[0x30] 或泄露得到)。
- __exit_funcs mangled 指针计算:def mangle(ptr, guard): return ((ptr ^ guard) << 0x11 | (ptr ^ guard) >> (64-0x11)) & 0xffffffffffffffff。
- __exit_funcs 写入 mangled one_gadget/system 到 __exit_funcs 条目,然后调用 exit() 或从 main 返回触发。
- 不知道 pointer guard 时:同时覆写函数指针和 TLS fs:[0x30] 的 pointer guard;设 pointer guard = 0,函数指针 = ROL(target, 0x11),解混淆 ROR(stored, 0x11) ^ 0 = target。
- glibc ≥2.34 的 TLS_dtor_list:由 exit 流程中的 __call_tls_dtors() 调用,条目格式 { void (*func)(void *), void *obj, void *next },func 同样用 PTR_DEMANGLE 混淆。
- TLS_dtor_list 位置:TLS 区域由 x86-64 的 fs 寄存器指向,tls_dtor_list 是 libc 中的线程局部变量,通常在 fs:[offset],offset 可通过 libc 符号或爆破找。
- TLS_dtor_list 利用步骤:泄露 TLS base(例如通过 canary 泄露 fs:[0x28]),计算 tls_dtor_list 地址。
- TLS_dtor_list 利用步骤:伪造条目 entry = p64(mangled_func_ptr) + p64(arg_value) + p64(0)。
- TLS_dtor_list 利用步骤:将条目写到堆,设置 tls_dtor_list 指向它,触发 exit() -> __call_tls_dtors() -> func(obj)。
- _dl_fini/link_map corruption:exit() 时 _dl_fini 遍历 link_map,并对每个库调用 DT_FINI_ARRAY 条目;逻辑为 if l_info[DT_FINI_ARRAY]: array = l_addr + l_info[DT_FINI_ARRAY]->d_un.d_ptr; 对 array 中每项 entry()。
- _dl_fini 利用:篡改 link_map 条目的 l_addr 使 FINI_ARRAY 指针偏移;或篡改 l_info[DT_FINI_ARRAY] 指向假数组。
- _dl_fini 利用:假数组放目标函数指针 system/one_gadget,触发 exit() -> _dl_fini -> 调用假析构;优势是 FINI_ARRAY 中函数指针不混淆。
- 内核 modprobe_path:用任意内核写将 modprobe_path 从 '/sbin/modprobe' 覆写为 '/tmp/x',例如 kernel_write(modprobe_path_addr, b'/tmp/x\x00')。
- 准备 /tmp/x:写入 '#!/bin/sh',追加 'cat /flag > /tmp/output',chmod +x /tmp/x。
- 触发 modprobe_path:创建未知格式文件 /tmp/trigger,内容为 \xff\xff\xff\xff,chmod +x /tmp/trigger,执行 /tmp/trigger;内核会以 root 调用 modprobe_path(/tmp/x)。
- .fini_array:覆写正常程序退出时调用的析构函数指针;通常为 [__do_global_dtors_aux, ...],可覆写第一个条目为目标(main 做循环,system 做 RCE)。
- .fini_array 两阶段:.fini_array[0] = main(回环),.fini_array[1] = exploit_func;第一次退出先调 .fini_array[1],再调 .fini_array[0] 回 main;在 main 循环中设置最终利用。
- .fini_array 限制:Full RELRO 二进制中 .fini_array 可能只读。
- C++ vtable 覆写:C++ 对象带虚函数时偏移 0 是 vptr,vptr -> vtable -> 函数指针数组;覆写 vptr 指向假 vtable 并控制函数指针。
- C++ 对象布局:+0x00: vptr -> [vtable_entry_0, vtable_entry_1, ...];+0x08: member data...。
- C++ vtable 利用:泄露对象地址和 vptr。
- C++ vtable 利用:在可控内存创建假 vtable,fake_vtable = p64(0) + p64(0) + p64(target_func) + p64(target_func),前两个 p64(0) 对应 offset -0x10/-0x08 的 RTTI 信息,后两个对应虚函数 0/1。
- C++ vtable 利用:覆写 vptr 指向 fake_vtable + 0x10(跳过 RTTI 前缀),触发对象虚函数调用。
- setcontext gadget:libc 的 setcontext 从 ucontext_t 加载寄存器,可作 pivot。
- glibc < 2.29:setcontext+53 从 [rdi + offsets] 加载寄存器;RDI 是第一个参数,指向可控 buffer,可设置 RSP、RIP 和所有其他寄存器,获得完全控制。
- glibc ≥ 2.29:setcontext+61 从 [rdx + offsets] 加载寄存器;必须控制 RDX 而不是 RDI;需要中间 gadget,如 mov rdx, [rdi+X]; ...; call/jmp [rdx+Y]。
- setcontext 常见 pre-2.34 组合:__free_hook = setcontext + 61,free(chunk) -> setcontext(chunk),chunk 内含假 ucontext;从 ucontext 设 RSP 到 ROP chain,RIP 到 ret,继续 ROP。
- setcontext post-2.34 组合:结合 _IO_FILE 利用,_IO_FILE vtable 调用把 fp 作第一参数,用 gadget 移到 rdx -> setcontext。
- 决策树:有任意写后,若 RELRO 为 None/Partial,优先 GOT overwrite,如 printf->system、free->system、atoi->system。
- 决策树:若 Full RELRO,GOT 只读,选择替代目标。
- 决策树:若 glibc <2.34,可选 __free_hook = system -> free('/bin/sh')(最简单),__malloc_hook = one_gadget -> malloc 触发,或 __realloc_hook + __malloc_hook realloc trick 调整栈对齐。
- 决策树:若 glibc ≥2.34,无 hooks;若知道 pointer guard (fs:[0x30]),选 __exit_funcs 或 TLS_dtor_list;若不知道,先覆写 pointer guard 为 0 再改 __exit_funcs。
- 决策树:若 glibc ≥2.34,可选 _IO_FILE + _IO_wfile_jumps(House of Apple 2 / Cat),需要 libc base + heap 地址 + 可控 FILE 结构。
- 决策树:若 glibc ≥2.34,可选 _dl_fini link_map corruption,需要 ld.so base。
- 决策树:若 glibc ≥2.34,可选 .fini_array(如果可写),需要 binary base(无 PIE 或已泄露 PIE base)。
- 决策树:任意版本可考虑栈返回地址(若知道栈地址)或 C++ vtable(若目标 C++ 对象有虚函数)。
- 决策树:若为内核写原语,modprobe_path 是最简单的 kernel→root;也可用 core_pattern(/proc/sys/kernel/core_pattern)或直接覆写 cred 结构。
- 决策树:若需要链式 read -> write -> execute,用 setcontext gadget:任意写 -> pivot RSP -> ROP chain;glibc <2.29 用 setcontext+53(RDI),glibc ≥2.29 用 setcontext+61(RDX,需 mov rdx, [rdi] gadget)。
适用边界 · 注意事项
- Full RELRO 时不要尝试 GOT overwrite,因为 .got.plt 只读。
- glibc ≥ 2.34 不要依赖 __malloc_hook、__free_hook、__realloc_hook,它们已被移除。
- 没有任意写原语、必要地址泄露或 pointer guard/TLS/堆地址等前置信息时,不要直接套用;先获取对应 primitive 或选择不需要混淆/泄露的目标。