其他
ELF 二进制漏洞利用时识别并绕过 ASLR、PIE、NX…
当你需要对 ELF 二进制做漏洞利用,并已获得或可构造读、写、溢出等原语,需要判断启用了哪些保护并选择绕过路径时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 48/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 55/100 ×0.15
适用场景
ELF 二进制漏洞利用时识别并绕过 AS通用
解决思路
- 用 `checksec ./binary` 识别 Arch、RELRO、Stack、NX、PIE、FORTIFY。
- 用 `cat /proc/sys/kernel/randomize_va_space` 检查 ASLR:0=off,1=partial,2=full。
- 用 `checksec` 或 `readelf -h` 的 Type: DYN 判断 PIE。
- 用 `checksec` 或 `readelf -l` 判断无 RWE segment 作为 NX 指示;`gcc -z noexecstack` 默认开启。
- 用 `checksec` 或查找 `__stack_chk_fail@plt` 判断 Canary。
- 用 `readelf -l` 查看 GNU_RELRO segment 且 `.got.plt` 可写判断 Partial RELRO;`gcc -Wl,-z,relro`。
- 用 `readelf -l` 加 `.got` section 只读判断 Full RELRO;`gcc -Wl,-z,relro,-z,now`。
- 通过存在 `__printf_chk`、`__memcpy_chk` 等判断 FORTIFY;`gcc -D_FORTIFY_SOURCE=2`。
- 信息泄漏:利用任意 read primitive(format string、OOB read、UAF)泄漏 libc/stack/heap 地址,再计算 base。
- Partial overwrite:用有限写 primitive 覆盖最后 1-2 字节,因为页偏移固定。
- 32 位暴力破解:可重连/重试时尝试约 256–4096 次(8-12 bits 熵)。
- Return-to-PLT:栈溢出时跳 PLT;若 no PIE,PLT 地址相对 binary base 固定。
- ret2dlresolve:栈溢出 + write primitive,伪造动态解析结构解析任意函数,无需 libc base。
- 格式字符串泄漏:用 `%N$p` 读取 stack/libc/heap 地址。
- Stack reading:在 fork server 中通过 crash oracle 逐字节读取栈。
- 评估 ASLR 熵决定暴力破解可行性:x86-64 Linux stack 22 bits,mmap/libc 28 bits,heap brk 13 bits,PIE binary 28 bits。
- 信息泄漏:从栈读返回地址,PIE base = leaked_addr - known_offset。
- Partial overwrite:写 1 或 2 字节;页偏移最后 12 bits 固定。
- 格式字符串泄漏:用 `%N$p`,N 指向 .text 返回地址。
- 相对寻址:若已知二进制布局和相对偏移,只需一次泄漏。
- Partial overwrite 细节:PIE binary 加载如 0x555555554000,函数 offset 0x1234 -> 0x555555555234;覆盖返回地址最后 2 字节 0x?234 -> 0x?XXX;未知 bits 12-15 有 16 种,每次成功率 1/16。
- ROP:串联以 `ret` 结尾的现有代码 gadgets。
- ret2libc:直接调用 libc 函数 `system`、`execve`。
- ret2csu:使用 `__libc_csu_init` gadgets 进行受控函数调用。
- ret2dlresolve:伪造动态链接器结构解析任意函数。
- SROP:用 sigreturn 从伪造 signal frame 设置所有寄存器。
- mprotect ROP:串联 `mprotect(addr, size, PROT_RWX)` 使页面可执行,再跳 shellcode。
- JIT spray:在 JIT 环境(V8 等)通过 JIT 编译器创建可执行代码。
- mprotect chain 示例:`rop = b'A' * offset; rop += p64(pop_rdi) + p64(stack_page); rop += p64(pop_rsi) + p64(0x1000); rop += p64(pop_rdx) + p64(7); rop += p64(mprotect_addr); rop += p64(shellcode_addr)`。
- No RELRO:GOT 完全可写,直接 GOT overwrite。
- Partial RELRO:`.got.plt` 可写(lazy binding),GOT overwrite 仍有效。
- Full RELRO:所有 GOT 加载时解析且 GOT 只读,不能写 GOT,改其他结构。
- Full RELRO 目标 `__malloc_hook`:glibc < 2.34 时用 one_gadget 覆盖。
- Full RELRO 目标 `__free_hook`:glibc < 2.34 时覆盖为 `system`,触发 `free('/bin/sh')`。
- Full RELRO 目标 `_IO_FILE vtable`:任意 glibc 用 FSOP / vtable hijack。
- Full RELRO 目标 `__exit_funcs`:任意 glibc 覆盖 exit handler list。
- Full RELRO 目标 `TLS_dtor_list`:glibc ≥ 2.34 时覆盖线程局部析构列表(需要 pointer guard)。
- Full RELRO 目标 `.fini_array`:如果可写,覆盖析构函数指针。
- Full RELRO 目标栈返回地址:直接栈写覆盖返回地址进行 ROP。
- 格式字符串泄漏:`printf(user_input)` 时用 `%N$p` 从栈读 canary。
- 暴力破解:fork() server 中 canary 在子进程持续,逐字节尝试 256 × (canary_size-1) 次。
- Stack reading:部分覆盖/信息泄漏时覆盖 canary 的 null 字节,通过输出泄漏。
- Thread canary overwrite:溢出到达 TLS;canary 位于 `fs:[0x28]`,溢出越过 buffer 到 TLS 用已知值覆盖 canary。
- Canary-relative overwrite:布局中溢出发生在 canary 后、返回地址前时,跳过 canary 只覆盖返回地址(罕见布局)。
- Heap-based:漏洞在堆而非栈时,canary 只保护栈,可绕过。
- `__stack_chk_fail` GOT overwrite:Partial RELRO 时覆盖 `__stack_chk_fail@GOT` 指向无害函数,使 canary 检查通过。
- 识别 canary 格式:x86 为 0x00XXXXXX(4 字节,前导 null byte);x86-64 为 0x00XXXXXXXXXXXXXX(8 字节,前导 null byte);前导 `\x00` 防止字符串操作误读 canary。
- `__printf_chk` 限制:禁止带位置参数的 `%n`(`%N$n`);绕过用非位置 `%n` 或 `%hn` 链。
- `__memcpy_chk` 限制:检查目标缓冲区大小;绕过改用堆溢出而不是栈。
- `__strcpy_chk` 限制:同上,改用堆或其他未 fortify 路径。
- `__read_chk` 限制:读取大小按缓冲区检查。
- FORTIFY_SOURCE 下格式字符串:`%1$n` 被 `__printf_chk` 阻止;但顺序(非位置)`%n` 可能仍可用;打印精确字节数后 `%hn`,必须非常精确;或通过 ROP 在 binary/libc 中找未 fortify 的 printf。
- Shadow stack:硬件维护返回地址副本;`ret` 时 CPU 检查 shadow stack 与实际栈匹配,不匹配触发 `#CP`。
- Shadow stack 影响:ROP 被阻止,因为返回地址覆盖在 `ret` 时被检测。
- Shadow stack 下 JOP 可能:`jmp [reg]` 不被 shadow stack 检查。
- Shadow stack 下 COP 可能:`call [reg]` 压入 shadow stack,但目标由 IBT 验证。
- IBT:间接 `jmp`/`call` 必须落在 `ENDBR64` 指令;非 ENDBR 登陆触发 `#CP` fault。
- CET 绕过:数据-only 攻击(不改变控制流)。
- CET 绕过:寻找有效 ENDBR gadgets 链到有用操作。
- CET 绕过:JOP 使用 ENDBR-prefixed gadgets。
- CET 绕过:针对 CFI 范围外结构(`modprobe_path`、function pointer arrays)。
- MTE tag bits:指针 bits 56-59 的 4 bits = 16 个可能标签。
- MTE granule:16 字节,每个 16-byte granule 一个 tag。
- MTE check:load/store 时指针 tag 必须匹配内存 tag。
- MTE 概率性:随机 tag,攻击者猜中概率 1/16。
- MTE 绕过暴力破解:每次 1/16(6.25%)。
- MTE tag oracle:用侧信道(timing、错误消息)确定 tag。
- MTE in-bounds exploit:保持在同一 tagged region,使用相对偏移。
- MTE tag bypass gadget:若可访问,使用 `LDGM`/`STGM` 指令。
- MTE speculative execution:Spectre 风格绕过 tag check。
- 决策:若 NX disabled,直接在栈/堆放 shellcode(最简单路径)。
- 决策:若 NX enabled 且需要代码执行,走 ROP/ret2libc。
- 决策:若 Canary enabled 且 fork server,逐字节暴力破解。
- 决策:若 Canary enabled 且有 format string,用 `%p` 泄漏 canary。
- 决策:若 Canary enabled 且漏洞在堆,canary 不保护堆。
- 决策:若 Canary enabled 且 Partial RELRO,覆盖 `__stack_chk_fail@GOT`。
- 决策:若 PIE enabled 且有 format string,泄漏 .text 地址得到 PIE base。
- 决策:若 PIE enabled,用 partial overwrite,最后 12 bits 固定,1/16 暴力破解。
- 决策:若 PIE enabled 且有 OOB read,泄漏 code pointer。
- 决策:若 ASLR enabled 且有 info leak,泄漏 libc base。
- 决策:若 ASLR enabled 且无 leak,用 ret2dlresolve 或 SROP。
- 决策:若 ASLR enabled 且 32 位,暴力破解可行(约 4096 次)。
- 决策:若 ASLR enabled,可用 Return-to-PLT,无需 libc base 调 PLT。
- 决策:若 RELRO None/Partial,GOT overwrite。
- 决策:若 Full RELRO,按 glibc 版本选目标:glibc < 2.34 用 `__malloc_hook`/`__free_hook`;glibc ≥ 2.34 用 `_IO_FILE`/`exit_funcs`/`TLS_dtor_list`;`.fini_array` 可写则覆盖;否则栈返回地址。
- 决策:若 FORTIFY_SOURCE 阻止 positional `%n`,用 sequential `%n` 或堆利用。
- 决策:若 FORTIFY_SOURCE 阻止 fortified 函数缓冲区溢出,走未 fortify 路径。
- 决策:若 CET shadow stack 阻止 ROP,用 data-only attack 或 JOP;用 ENDBR-gadget chaining。
- 决策:若 ARM MTE,1/16 暴力破解或保持 in-bounds 做相对破坏。
适用边界 · 注意事项
- 不适用于非 ELF 用户态目标(如 Windows PE、Mach-O、内核、固件)或没有可用漏洞原语时,单靠保护绕过无法完成利用。
- 不适用于未授权测试;CET/MTE 相关绕过仅在硬件与系统实际启用对应机制时才有意义。
- 当目标不是 glibc 或 glibc 版本不匹配时,__malloc_hook、__free_hook、__exit_funcs、TLS_dtor_list 等目标可能不存在或结构不同。