
Proof of Concept CVE-2025-24990 (driver Agere Systems)
Windows Agere Modem Driver (ltmdm64.sys). Questo driver è molto vecchio e non viene caricato di default sulla mia macchina di test, quindi lo sfrutterò in uno scenario BYOVD. Curiosamente, secondo le mie ricerche questo driver esisteva su Windows 7 e ha almeno un bug. qui
A quel tempo, MSRC non ha intrapreso alcuna azione 🤡
Alcuni IOCTL all'interno di questo driver utilizzano METHOD_NEITHER ma non verificano se l'indirizzo del buffer fornito dal chiamante proviene dalla modalità utente o dalla modalità kernel. Ecco un esempio di codice IOCTL che ho decodificato con OSR:
Ciò significa che puoi fornire un indirizzo kernel all'API DeviceIoControl e il driver lo gestirà normalmente.
Nota che devi prima bypassare kASLR per divulgare l'indirizzo kernel, userò EnumDeviceDrivers (Su Windows 24h2 hai bisogno di SeDebugPriv per farlo).
Il problema è nell'IOCTL 0x802b200f (ud_response). Ancora una volta, questo dispatch IOCTL non valida l'indirizzo che fornisco dalla modalità utente, ma lo sfrutterò più avanti.
ud_response chiama ll_load_diagnostics, e raggiungerò il seguente codice:
All'inizio la variabile globale eeprom non è inizializzata, quindi conterrà NULL. Ecco un semplice codice che attiverà questo.
Lo sfrutterò più avanti.
Questo IOCTL converte semplicemente la stringa della versione del driver "8.36" nel numero 0x836 (un DWORD) e lo scrive all'indirizzo fornito dal chiamante (grazie a METHOD_NEITHER). Tecnicamente, posso scrivere questi quattro byte (36 08 00 00) a un indirizzo kernel arbitrario. Sfrutterò questo per sovrascrivere le variabili globali del driver e cambiare il flusso di esecuzione.
Chiamerò questo 0x802b2003 IOCTL_GET_VERSION
Null arbitrario di 1 byte:
Tornando al caso di null dereference, uso l'API VirtualAlloc per allocare un indirizzo fisso (0x083600000000). Poi uso IOCTL_GET_VERSION per scrivere a *(eeprom + 4) i quattro byte descritti sopra. Quando il driver successivamente dereferenzia eeprom, leggerà dall'indirizzo che ho allocato.
Dopo aver corretto la null dereference, l'IOCTL scrive una stringa all'indirizzo che fornisco dalla modalità utente, in base alla dimensione del buffer.
Questo codice dimostra semplicemente ciò che ho descritto sopra: alloca un buffer e lo riempie con 0xAA, corregge la null dereference, poi chiama il driver. Nota che alloco 11 byte ma fornisco solo una dimensione del buffer di 10 al driver per vedere come si comporta.
Scrive una sequenza fissa di byte nel mio buffer e poi azzera il byte finale (l'11°), anche se fornisco solo una dimensione di 10. Sostituisce l'ultimo 0xAA nel mio buffer con 0x00. Questo indica che se fornisco una dimensione di 0, il driver scrive comunque un singolo byte 0x00 all'indirizzo di destinazione.
Decremento arbitrario
Ora ho il null e l'arbitrario con 4 byte fissi, creiamo altre primitive.
Questo IOCTL imposterà il globale LtMsgEvent al mio buffer utente, poi verificherà che WDM sia null e lo imposterà di nuovo a zero.
Poi in 0x802b2207 chiamerà l'API ObfReferenceObject.
Nello stato iniziale WDM è null, ma con l'aiuto di IOCTL_GET_VERSION posso impostare WDM a 0x36 (la sua dimensione è solo 1 byte) e LtMsgEvent è ancora il mio buffer. Poi azzererò WDM e chiamerò 0x802b2207. Infine raggiungo ObfReferenceObject. Chiamerò questi due ioctl IOCTL_SET_LtMsgEvent e IOCTL_DEREF_LtMsgEvent.
La tecnica di exploit che usa ObfReferenceObject cambia il PreviousMode del nostro KTHREAD da UserMode a KernelMode, puoi leggerne qui). Tuttavia, Windows ha corretto questo exploit, quindi non possiamo usarlo.
Ma la primitiva in ObfReferenceObject esiste ancora. L'API sottrae 0x30 dall'indirizzo che forniamo, converte il risultato in un intero a 8 byte, e poi sottrae 1.
*(signed long long)(LtMsgEvent-0x30) -= 1
Ma il problema è che verifica se il valore successivo è 0 o se il valore corrente è < 1 (interpretato come un intero con segno a 8 byte). Se una delle due condizioni è vera, salta a KeBugCheckEx e manda in crash il sistema.
Scrittura arbitraria
Con il decremento arbitrario in mano, devo trovare un altro posto dove scrivere il byte 0xFF e poi decrementarlo al byte che voglio, e ho trovato questo ioctl 0x802b2243: