
Prova de Conceito CVE-2025-24990 (driver da Agere Systems)
ltmdm64.sys). Este driver é muito antigo e não é carregado por padrão na minha máquina de teste, então vou explorá-lo em um cenário BYOVD. Curiosamente, de acordo com minha pesquisa, este driver existe desde o Windows 7 e tem pelo menos um bug. aqui
Naquela época, a MSRC não tomou nenhuma ação 🤡
Alguns IOCTLs dentro deste driver usam METHOD_NEITHER, mas não verificam se o buffer de endereço fornecido pelo chamador é do modo usuário ou do modo kernel. Aqui está um exemplo de código IOCTL que decodifiquei com OSR:
Isso significa que você pode fornecer um endereço de kernel para a API DeviceIoControl e o driver o manipulará normalmente.
Observe que você precisa contornar o kASLR primeiro para vazar o endereço do kernel. Usarei EnumDeviceDrivers (No Windows 24h2, você precisa de SeDebugPriv para fazer isso).
O problema está no IOCTL 0x802b200f (ud_response). Novamente, este despacho de IOCTL não valida o endereço que forneço do modo usuário, mas vou aproveitar isso mais tarde.
O ud_response chama ll_load_diagnostics, e chegarei ao seguinte código:
No início, a variável global eeprom não é inicializada, então conterá NULL. Aqui está um código simples que acionará isso.
Vou aproveitar isso mais tarde.
Este IOCTL simplesmente converte a string de versão do driver "8.36" para o número 0x836 (um DWORD) e o escreve no endereço fornecido pelo chamador (graças ao METHOD_NEITHER). Tecnicamente, posso escrever esses quatro bytes (36 08 00 00) em um endereço arbitrário do kernel. Vou aproveitar isso para sobrescrever as variáveis globais do driver e alterar o fluxo de execução.
Chamarei este 0x802b2003 de IOCTL_GET_VERSION
Null arbitrário de 1 byte:
Voltando ao caso de desreferência nula, uso a API VirtualAlloc para alocar um endereço fixo (0x083600000000). Em seguida, uso o IOCTL_GET_VERSION para escrever em *(eeprom + 4) os quatro bytes descritos acima. Quando o driver desreferenciar eeprom mais tarde, ele lerá do endereço que aloquei.
Após corrigir a desreferência nula, o IOCTL escreve uma string no endereço que forneço do modo usuário, com base no tamanho do buffer.
Este código simplesmente demonstra o que descrevi acima: alocar um buffer e preenchê-lo com 0xAA, corrigir a desreferência nula e então chamar o driver. Observe que aloco 11 bytes, mas forneço apenas um tamanho de buffer de 10 ao driver para ver como ele se comporta.
Ele escreve uma sequência fixa de bytes no meu buffer e então zera o byte final (o 11º), mesmo que eu forneça apenas um tamanho de 10. Ele substitui o último 0xAA no meu buffer por 0x00. Isso indica que, se eu fornecer um tamanho de 0, o driver ainda escreve um único byte 0x00 no endereço de destino.
Decremento arbitrário
Agora que tenho o null e o arbitrário com 4 bytes fixos, vamos criar outra primitiva.
Este IOCTL definirá o global LtMsgEvent para o meu buffer de usuário e então verificará se WDM é nulo e o definirá como zero novamente.
Então, em 0x802b2207, ele chamará a API ObfReferenceObject.
No estado inicial, o WDM é nulo, mas com a ajuda do IOCTL_GET_VERSION, posso definir WDM para 0x36 (seu tamanho é apenas 1 byte) e o LtMsgEvent ainda é meu buffer. Então, anularei WDM e chamarei 0x802b2207. Finalmente, alcançarei o ObfReferenceObject. Chamarei esses dois ioctls de IOCTL_SET_LtMsgEvent e IOCTL_DEREF_LtMsgEvent.
A técnica de exploit usando ObfReferenceObject altera o PreviousMode do nosso KTHREAD de UserMode para KernelMode; você pode ler sobre isso aqui). No entanto, o Windows corrigiu esse exploit, então não podemos usá-lo.
Mas a primitiva em ObfReferenceObject ainda existe. A API subtrai 0x30 do endereço que fornecemos, converte o resultado em um inteiro de 8 bytes e então subtrai 1.
*(signed long long)(LtMsgEvent-0x30) -= 1
Mas o problema é que ela verifica se o próximo valor é 0 ou se o valor atual é < 1 (interpretado como um inteiro assinado de 8 bytes). Se qualquer uma das condições for verdadeira, ela salta para KeBugCheckEx e trava o sistema.
Escrita arbitrária
Com o decremento arbitrário em mãos, preciso encontrar outro lugar para escrever o byte 0xFF e então decrementá-lo para o byte que desejo, e encontrei este ioctl 0x802b2243: