
prova di concetto per CVE-2024-38063 (RCE in tcpip.sys)
Questa è una PoC (piuttosto instabile) per CVE-2024-38063, una RCE in tcpip.sys corretta il 13 agosto 2024. Non ho trovato e segnalato questa vulnerabilità, quello è stato Wei.
pip3 install scapy
Modifica i campi nello script:
iface <- Se hai più adattatori, devi scegliere quale usare per inviare i pacchetti. es. "eth0" su linux o "Hyper-V Virtual Ethernet Adapter" su windows. Se intendi usare la tua interfaccia predefinita, lascialo vuoto.ip_addr <- Indirizzo IP del sistema di destinazione (IPv6)num_tries e num_batches <- Quanti diversi batch di pacchetti inviare. Più sono = più corruzioni dell'heap causate + maggiore probabilità di innescare la vulnerabilità.mac_addr <- Lascia vuoto, a meno che scapy non segnali che non riesce a trovare l'indirizzo MAC. Vedi sotto nella risoluzione dei problemi.Esegui lo script:
python3 cve-2024-38063.py
Il modo più semplice per riprodurre la vulnerabilità è usando bcdedit /set debug on sul sistema di destinazione e riavviando la macchina/VM. Questo fa sì che il driver dell'adattatore di rete predefinito diventi kdnic.sys, che è molto propenso a coalescere i pacchetti. Se stai cercando di riprodurre la vulnerabilità su una configurazione diversa, dovrai portare il sistema in una posizione in cui coalesce i pacchetti che hai inviato. Puoi leggere la sezione di risoluzione dei problemi qui sotto per maggiori dettagli.
Puoi leggere questa ottima analisi della vulnerabilità di Marcus se sei interessato ai dettagli tecnici. I dettagli che ho scritto qui sotto hanno lo scopo di servire come riassunto, piuttosto che come analisi tecnica seria.
NET_BUFFER che contiene i dati del pacchetto bufferizzato. All'offset 0x30 abbiamo anche un campo current-offset che indica quanto è stato analizzato il pacchetto. In questa fase, il valore dell'offset sarà generalmente 0x28, indicando che l'header IPv6 è stato analizzato ma nient'altro.tcpip!Ipv6pReceiveDestinationOptions, un errore di parsing causerà la chiamata di tcpip!IppSendErrorList. Questa funzione chiama tcpip!IppSendError su ogni oggetto pacchetto nella lista collegata (partendo da quello corrente).tcpip!IppSendError ha effetti collaterali. 'Riporta' i dati del pacchetto bufferizzato all'inizio e resetta il campo current-offset a zero.0x8C). Ciò significa che il driver continuerà a elaborare gli header di estensione di altri pacchetti nella lista collegata, anche se sono stati 'riportati' in IppSendError.0x28.Ipv6pReceiveFragment. La funzione analizza l'header di estensione del frammento e assume che il campo offset del pacchetto sarà almeno 0x28 quando calcola la lunghezza dei dati non-header nel pacchetto sottraendo 0x30 dal valore corrente dell'offset. Questo valore viene poi memorizzato nell'oggetto di riassemblaggio il cui scopo è riassemblare il pacchetto frammentato.IppSendError. Il valore dell'offset sarà zero e aumentato a 8 da qualche parte prima in Ipv6pReceiveFragment. Quando si calcola la dimensione dei dati non-header, il valore andrà in underflow e sarà uguale a 0xffd8 (la sottrazione è fatta in 16 bit).Ipv6pReassembleDatagram, dove viene usato per calcolare la lunghezza di un buffer di output del pacchetto riassemblato. Tuttavia, tutti i calcoli sono fatti in 32 bit e c'è un controllo di integrità che la lunghezza totale non superi 0xFFFF, cosa che in questo caso accade.Ipv6pReassemblyTimeout, dove viene usato allo stesso modo. Tuttavia, i calcoli qui sono fatti in 16 bit e si verifica un overflow di interi. Questo porta a un buffer overflow quando si copiano i dati nel buffer successivamente.Per innescare Ipv6pReassemblyTimeout, il mittente del frammento deve rimanere inattivo per 1 minuto. La nostra strategia è quindi:
IppSendError, seguite da un pacchetto frammentoIpv6pReceiveFragment e creare un nuovo oggetto di riassemblaggio con una lunghezza dei dati del frammento che sia un valore alto a 16 bitIpv6pReassemblyTimeout venga innescato.Ipv6pReassemblyTimeout e innescare un heap-based buffer overflow.I pacchetti nello script sono inviati a raffica in modo che ci sia una maggiore probabilità che vengano coalesciuti. Il payload principale è piuttosto semplice:
Impostiamo anche manualmente i campi hop limit e flow label nell'header IPv6. Ricordiamo che i dati del pacchetto bufferizzato vengono resettati a causa della vulnerabilità. Ciò significa che, quando si elabora il pacchetto frammento, l'header IPv6 verrà interpretato come dati dell'header del frammento. Il campo hop limit nell'header IPv6 verrà interpretato come uno dei bit del campo id nell'header del frammento. Cambiandolo, ci assicuriamo di innescare la vulnerabilità per diversi frammenti diversi e causare diverse corruzioni, aumentando la probabilità di un crash (dato che questa è una PoC, dopotutto). Il campo flow label dell'header IP verrà interpretato come i campi offset e 'more indicator' dell'header del frammento. Impostandolo a 1, indichiamo che ci sono altri header in arrivo (quindi essere in grado di innescare Ipv6pReassemblyTimeout in seguito) e che l'offset è zero (poiché questo è il primo pacchetto con tale id in arrivo).
Ipv6pReassemblyTimeout richiede che il pacchetto frammento originale sia inviato come unicast.Se non funziona, potrebbe essere perché:
Ether(raw(sr1(IPv6(dst={your_dest_ip})/ICMPv6EchoRequest()))).src, ma a volte non funzionatcpip!Ipv6pReceiveDestinationOptions -> tcpip!Ipv6pProcessOptions -> tcpip!IppSendErrorList viene colpito?tcpip!Ipv6pProcessOptions e controlla se [rcx] è zero per tutto il tempo. Se sì, allora i pacchetti non vengono coalesciuti per qualche motivo.tcpip!Ipv6pReceiveFragment e controlla se [rcx+0x30] è uguale a zero. Se non lo è, allora la vulnerabilità non è stata innescata per qualche motivo.