Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2024-30051 — Análise técnica detalhada e exploit de prova de conceito para CVE-2024-30051, um estouro de buffer baseado em heap na Windows DWM Core Library possibilitando escalonamento local de privilégios para o nível Integrity System. | Kitploit
Ferramentas/GitHubGitHub/fortra/cve-2024-30051
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoEngenharia ReversaAprendizado e EducaçãoExploração de Binários
GitHubfortra/cve-2024-30051

CVE-2024-30051

Análise técnica detalhada e exploit de prova de conceito para CVE-2024-30051, um estouro de buffer baseado em heap na Windows DWM Core Library possibilitando escalonamento local de privilégios para o nível Integrity System.

Ver Repositório
1263612há 2 anosRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Vulnerabilidade de Elevação de Privilégio da Biblioteca Core DWM do Windows (CVE-2024-30051) (Publicado em 15 de agosto de 2024)

Nesta postagem de blog, explicarei uma vulnerabilidade na biblioteca Core DWM do Microsoft Windows que analisei quando o exploit para o Core Impact estava sendo desenvolvido. Permite que um atacante não privilegiado execute código como um usuário DWM com privilégios de Integrity System (CVE-2024-30051).

Como não havia informações públicas suficientes na época para desenvolver o exploit, tive que fazer muito reverse engineering, então aqui mostrarei como reverter o patch KB5037771 para Windows 23H2 usando IDA PRO. Usarei o BINDIFF para realizar a diff binária entre a dwmcore.dll versão 10.0.22621.3447 e a versão 10.0.22621.3593, mostrarei como o heap overflow é produzido e, em seguida, o explorarei elevando privilégios. Por fim, criarei um PoC funcional.

Índice:

[Vulnerabilidade de Elevação de Privilégio da Biblioteca Core DWM do Windows (CVE-2024-30051) 1](#windows-dwm-core-library-elevation-of-privilege-vulnerability-cve-2024-30051)

[Detalhes da vulnerabilidade: 2](#vulnerability-details)

[Diferenças para encontrar o bug: 3](#diffing-to-find-the-bug)

[Análise do PoC explorando CVE-2024-30051: 8](#analysis-of-the-poc-exploiting-cve-2024-30051)

[1)Inicialização 8](#initialization)

[2)Hooking 8](#hooking)

[3)Criando a janela 16](#creating-the-window)

[4)Criar Dispositivo 16](#create-device)

[5) Criar Fábrica 22](#create-factory)

[6) Criar Contexto de Dispositivo 28](#create-a-device-context)

[7)Criar Dispositivo de Composição 29](#create-a-composition-device)

[8)Chamando a função hook3 31](#calling-dcompositioncreatedevice-function)

[9)Criando alvo para o HWND 32](#creating-a-target-for-handle-hwnd)

[10)Criando Superfície 33](#creating-surface)

[11)Chamando BeginDraw, EndDraw e CreateVisual. 34](#calling-begindraw-enddraw-and-createvisual)

[11)Chamando Visual SetContent 36](#calling-visual-setcontent)

[12)Liberar objetos 38](#release-objects)

[13)Commit do Dispositivo de Composição 38](#commit-composition-device)

[14)Chamando hook2 39](#calling-hook2)

[15)Chamando hook 39](#remember-that-the-vulnerable-function-can-be-reached-using-some-methods-of-the-cprimitivegroup-class.-at-this-point-it-creates-a-heap-then-hook2-captures-and-saves-the-corresponding-heaphandle.)

[16)Chamando hook4 41](#calling-the-function-hook4)

[17)Realizando Heap Spray 49](#performing-heap-spray)

[18)Modificando o chunk base antes do envio. 51](#modifying-the-base-chunk-previous-to-send)

[19)Depurando o processo DWM 52](#debugging-the-dwm-process)

[20)Elevando Privilégios ao nível Integrity System 62](#elevating-privileges-to-integrity-system-level)

Detalhes da vulnerabilidade:

Vulnerabilidade de Elevação de Privilégio da Biblioteca Core DWM do Windows CVE-2024-30051

Lançado: 14 de maio de 2024

CNA de atribuição: Microsoft CVE-2024-30051

Impacto: Elevação de Privilégio

Severidade Máxima: Importante

Fraqueza:

CWE-122: Estouro de Buffer Baseado em Heap

CVSS: 3.1 7.8 / 7.2

A vulnerabilidade existe devido a um erro de cálculo de tamanho em uma divisão inteira dentro da principal biblioteca DWM do Windows chamada dwmcore.dll. Um usuário local pode causar um estouro de buffer no heap no método CCommandBuffer::Initialize em dwmcore.dll e pode executar código arbitrário com o usuário DWM com Privilégios de Integrity System. O exploit realizará um Heap Spray no processo DWM para preparar a memória e, finalmente, produz um Heap Overflow em dwmcore.dll que será acionado ao liberar certas partes do heap spray.

Uma vez que o exploit seja bem-sucedido, o processo DWM carregará nossa DLL personalizada que executa nosso código ou nosso executável (no nosso caso, um CMD) como o usuário DWM que possui Privilégios de Integrity System.

Vamos percorrer essa vulnerabilidade e ver como ela nos permite executar como um usuário DWM com Integrity Level SYSTEM. Observe que, como este não é um usuário pertencente ao grupo Administradores, ele tem algumas restrições de privilégio.

Diferenças para Encontrar o Bug:

O patch para Windows 11 23H2 pode ser baixado de:

https://www.catalog.update.microsoft.com/Search.aspx?q=KB5037771

windows11.0-kb5037771-x64_19a3f100fb8437d059d7ee2b879fe8e48a1bae42.msu

A versão vulnerável de dwmcore.dll é: 10.0.22621.3447

A versão corrigida de dwmcore.dll é: 10.0.22621.3593

Analisando as funções alteradas, fica claro que a versão corrigida de CCommandBuffer::Initialize tem muitos blocos adicionados, tornando-a bastante diferente da versão não corrigida.

Após reverter estaticamente essa função, há duas chamadas para CD2DSharedBuffer::GetBufferSize.

A primeira chamada obtém o tamanho para alocar no new e a segunda chamada obtém o mesmo tamanho para o memcpy.

Tudo parece inicialmente correto. No entanto, antes da alocação, ele realiza algumas operações com o tamanho.

Ele obtém buffer_size e buffer_size2 chamando a mesma função CD2DSharedBuffer::GetBufferSize, retornando ambas o mesmo valor. Mas no new ele realiza uma pré-operação, uma divisão inteira do buffer_size por 0x90 e depois multiplicando por 0x90, enquanto no memcpy ele usa o buffer_size2 retornado sem operar sobre ele.

Com essas operações, descobri que o tamanho finalmente usado no new e no memcpy podem ser diferentes.

buffer_size = buffer_size2 (tamanhos retornados)

size_new= buffer_size/0x90 x 0x90

size_memcpy=buffer_size2

Por exemplo, se buffer_size for 0x91

buffer_size = buffer_size2=0x91

size_new= buffer_size/0x90 x 0x90 =0x90

size_memcpy= buffer_size2= 0x91

Este exemplo prova que há um heap overflow. Está copiando mais bytes do que alocados, e o tamanho é controlável.

Por exemplo, se buffer_size for 0x23f como usado no POC.

buffer_size = buffer_size2=0x23F

size_new= buffer_size/0x90 x 0x90 =0x1b0

size_memcpy== buffer_size2=0x23f

Com a função vulnerável analisada, quis ver como chegar à função vulnerável CCommandBuffer::Initialize. É aqui que as coisas começam a ficar complicadas.

Olhando para as referências desta função, parece que ela é alcançada a partir de métodos da classe CPrimitiveGroup:

Tais métodos podem ser acessados a partir da vftable de objetos CPrimitiveGroup:

Ela tem seu construtor:

E é alcançada desta forma:

Conforme percorria esse processo inicialmente, reservei um tempo para ler o PDF “The Lost World of DirectComposition: Hunting Windows Desktop Window Manager Bugs” e mergulhei no mundo da Direct Composition. Isso me ajudou a criar meu primeiro PoC.

Além disso, precisei reverter o win32ksys e tentei enviar pacotes através das funções:

  • NtDCompositionCreateChannel

  • NtDCompositionProcessChannelBatchBuffer

  • NtDCompositionCommitChannel

Meu primeiro PoC alcançou o construtor CPrimitiveGroup. No entanto, após muita engenharia reversa, não encontrei uma maneira de manipular as chamadas aos métodos da vftable para chegar à função vulnerável diretamente através de chamadas ALPC usando essas funções.

Passei muito tempo fazendo engenharia reversa complicada. Durante este processo, encontrei a amostra do malware que explorou a vulnerabilidade, o que foi imensamente útil porque o método de exploração é muito mais complexo do que pensei inicialmente. Ele também inclui vários hooks em APIs do sistema e usa métodos que são talvez um pouco questionáveis. Mas tudo é válido em guerra e exploits, então comecei a analisar o malware e, a partir dessa análise, criei meu PoC final que finalmente explora a vulnerabilidade, o que explicarei abaixo.

Primeiro, quero esclarecer que o malware não apenas explora a vulnerabilidade CVE-2024-30051 que eleva nosso processo ao Integrity System Level, mas também realiza uma segunda parte que, a partir daí, acaba elevando um usuário SYSTEM com todos os privilégios, o que já excede o CVE explicado.

Além disso, é importante notar que o malware é muito mais complexo que meu PoC, que tenta minimizar o código. O malware realiza muito mais verificações para garantir confiabilidade e, por causa disso, funciona na primeira tentativa. Descartei todas essas verificações para simplificar e me dediquei à exploração pura, mesmo que talvez seja necessário executar o PoC duas ou três vezes para alcançar a exploração.

Análise do PoC explorando CVE-2024-30051:

1)Inicialização

O link para o PoC executável é https://github.com/fortra/CVE-2024-30051

Primeiro, o PoC chama GetVersion para obter a versão do SO onde está executando e, de acordo com isso, realiza diferentes inicializações de algumas variáveis globais. Meu PoC foi testado no Windows 11 23H2 e Windows 11 22h2. Outros sistemas também são vulneráveis e adicionei os valores para explorá-los.

2)Hooking

Ele faz hook em quatro funções do sistema e, sem fazer hook nelas, não consegue alcançar a exploração. Essas funções são: RtlAllocateHeap, RtlCreateHeap, NtDCompositionCreateChannel e NtDCompositionCommitChannel.

Nessas funções, ele corrigirá os primeiros 5 bytes para fazê-las pular para seu próprio código. Claro, o código não pode estar muito longe, já que um salto de 5 bytes não cobre toda a memória e deve estar próximo.

Para fazer isso, o malware usa um código muito longo, analisando o mapa de memória para decidir onde pode realizar a alocação de seu próprio código. Como o código é complicado, concentrei-me em fazer duas linhas simples:

base_ntdll = GetModuleHandleW(L"ntdll.dll");

global4_ = (char *)VirtualAlloc((LPVOID)(base_ntdll-0x2000), 0x1000uLL, 0x3000u, 0x40u);

Subtraí da base ntdll, 0x2000 e passei esse endereço para VirtualAlloc para alocar lá.
As DLLs de 64 bits são mapeadas de forma bastante separada na memória umas das outras, com espaços vazios entre elas.
Vamos ver como os hooks funcionam:

Ele chama uma função hooking, que é a que realizará o hook da API RtlAllocateHeap, que tem três argumentos, o primeiro é o endereço da API a ser corrigida, chamada sym_RtlAllocateHeap.
Antes do patch, ela aponta para o início da API:

Aqui está a função RtlAllocateHeap:

O segundo argumento é a rotina chamada hook que será executada quando a API estiver completamente corrigida:

A função hook chama my_RtlAllocateHeap.

A função hooking corrigirá os primeiros 5 bytes da API para que ela pule para hook.

Ela chamará o código na área alocada onde executará a primeira instrução da API que foi substituída pelos 5 bytes e, em seguida, pulará para RtlAllocateHeap+5, logo após os bytes corrigidos:

É assim que a API ficará após o hook. Os primeiros 5 bytes foram alterados para que pule para hook. Ela chamará my_RtlAllocateHeap, o código que está logo acima, que retornará para a área marcada em roxo para continuar a execução da API:

Quando a API termina de executar, ela retorna para hook. A partir daí, ela comparará a variável global heap_base (que é inicialmente zero) com o primeiro argumento passado para RtlAllocateHeap:

Depois disso, o código espera por uma certa alocação especial, que tem um HeapHandle específico. No início, esta variável é zero e, enquanto for zero, ela ignorará e funcionará como um RtlAllocateheap normal:

O parâmetro HeapHandle é obtido dentro de RtlCreateHeap, que coincidentemente é a segunda API com hook.

Procurando referências à variável global heap_base, ela só muda seu valor na função hook2, que é a executada após o hook de RtlCreateHeap:

Então, a ideia é capturar um certo HeapHandle e salvá-lo em heap_base. Como agora é diferente de zero, a função hook começará a comparar cada alocação. Assim, o PoC salvará o endereço de memória que tem o mesmo HeapHandle que o armazenado anteriormente.

Quando este for o caso, ele salvará o endereço da alocação na variável chamada base:

Esses dois primeiros hooks agora estão encadeados. Quando hook2 salva o valor esperado de HeapHandle, ele ativa a função hook que salvará o endereço de alocação que usa o mesmo HeapHandle.

O terceiro hook é submetido a NtDCompositionCreateChannel. Na primeira vez que é chamado, ele salvará o MappedAddress, que é o conteúdo do terceiro argumento. A partir daí, ele alterará hooked_flag para 1, de modo que, a partir de então, não salvará mais e funcionará normalmente.

O endereço salvo na variável base será lido posteriormente três vezes. Duas delas ocorrerão no último hook, chamado hook4:

A função hook4 para NtDCompositionCommitChannel será analisada mais adiante, pois é bastante complexa e muito importante.

3)Criando a Janela

Após os quatro hooks serem concluídos, ele retorna à função principal para começar a criar uma janela. Isso é feito chamando RegisterClassExW. No entanto, para registrar uma classe de janela para uso posterior, ela deve ser chamada com a função CreateWindowExW.

Isso inicializa a biblioteca COM chamando CoInitializeEx para ser usada pela thread chamadora:

Ele calcula o tamanho necessário do retângulo da janela, com base no tamanho desejado:

A função CreateWindowExW é chamada para criar uma janela que será desenhada:

4)Criar Dispositivo

A partir daí, chama D3D11CreateDevice para criar um dispositivo ou dispositivo DirectX que representa o adaptador de exibição:

No meu PoC, ppDevice é chamado de d3dDevice e ppInmediateContext é chamado de d3dContext:

O argumento flags precisa ser definido como 0x20:

Em seguida, chama AddRef:

Isso incrementa o contador de referência para um ponteiro de interface para um objeto COM:

O valor 0x10 é subtraído de THIS:

No offset 0xf8 de ID3D11Device-0x10 há um ponteiro para TComObject:

Este será o novo THIS e termina pulando para TComObject::AddRef:

E termina adicionando um ao contador de objetos que está no offset 8 de TComObject:

Em seguida, AddRef aumentará o contador do outro tipo de objeto criado em D3D11CreateDevice, que é do tipo ID3D11DeviceContext:

Neste caso, para encontrar o novo THIS, ele subtrai 0x108:

Ele pula para cá, onde no offset 0x98 está o novo THIS:

Este é o contador. Neste exemplo, é um QWORD:

5) Criar Fábrica

O PoC chama D2D1CreateFactory para usar Direct2D e criar a Interface ID2D1Factory que é usada para criar outros recursos Direct2D que podem ser usados para desenhar ou descrever formas:

O argumento riid é o sugerido pela página da Microsoft:

https://learn.microsoft.com/en-us/windows/win32/api/d2d1/nf-d2d1-d2d1createfactory

Estes são os usados pelo malware:

O correto para ID2D1Factory pode ser encontrado aqui**:**

https://github.com/apitrace/dxsdk/blob/master/Include/d2d1_1.h

Como não sou especialista em Direct Composition, usei os mesmos passos do malware:

A nova fábrica que retorna não fornece nenhum tipo detalhado. Diz void *, o que significa que não está oficialmente documentado:

Como não conheço um tipo de objeto como neste caso, desenvolvi um executável que o usa para vê-lo na memória facilmente:

Adicione breakpoints nas quatro funções hook. Neste caso, um breakpoint em hook2 mostrará quando ele captura o HeapHandle:

O hook deve ser interrompido quando o chunk desejado for capturado:

Coloque breakpoints nos outros dois hooks:

Em seguida, ele continua chamando QueryInterface:

https://help.solidworks.com/2020/english/api/sldworksapi/queryinterface_example_cplusplus_com.htm

https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/dxgi.idl

Ele tenta realizar uma espécie de casting dinâmico. Se o objeto do tipo ID3D11Device pode aceitar a interface (usar os métodos, etc.) de IDXGIDevice, ele cria uma cópia do objeto original que aceita o novo tipo, depois disso retorna o ponteiro para ele. Neste caso, a variável d3dContext1 será do tipo IDXGIDevice:

Ambos os objetos herdam de CLayeredObject<Cdevice>

O ID3D11Device original é**:**

Assim como aquele que retorna o ponteiro.

Em seguida, cria um objeto ID2D1Device com a função CreateDevice:

Em value2 retorna um objeto do tipo ID2D1Device.

6) Criar um Contexto de DispositivoNeste ponto, o PoC cria um novo contexto de dispositivo a partir de um dispositivo Direct2d. Usando a função CreateDeviceContext

https://learn.microsoft.com/en-us/windows/win32/api/d2d1_1/nf-d2d1_1-id2d1device-createdevicecontext

Aqui está implementado no PoC:

7) Criar um Dispositivo de Composição

Em seguida, chame DCompositionCreateDevice

https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nf-dcomp-dcompositioncreatedevice

O IID pertence a _IDCompositionDevice

8) Chamando a Função DCompositionCreateDevice

No mesmo momento em que a função é rastreada sobre DCompositionCreateDevice, ela para em hook3, quando chama NtDCompositionCreateChannel:

Dessa forma, está capturando o MappedAddress que o sistema usa internamente quando DCompositionCreateDevice foi chamado:

Esta é a pilha de chamadas até aqui:

Este é o ponto onde o módulo dcomp chama a função NtDCompositionCreateChannel:

9) Criando um Destino para o Identificador HWND

Após retornar da etapa anterior, salve o MappedAddress. Usando ALPC, ele se conectará ao processo DWM e então chamará CreateTargetForHwnd

https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nf-dcomp-idcompositiondevice-createtargetforhwnd

Ele usa o identificador HWND da janela criada. Está relacionado ao dispositivo que acabei de criar, que é o THIS deste método:

10) Criando Superfície

Em seguida, chame CreateSurface

https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nf-dcomp-idcompositiondevice-createsurface

11) Chamando BeginDraw, EndDraw e CreateVisual

Em seguida, chame BeginDraw, EndDraw, e chegue a CreateVisual.

Chama BeginDraw

https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nf-dcomp-idcompositionsurface-begindraw

Isso usa o IID _IDXGISurface:

Em seguida, usa EndDraw:

https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nf-dcomp-idcompositionsurface-enddraw

Finalmente, chama CreateVisual:

https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nf-dcomp-idcompositiondevice-createvisual

12) Chamando Visual SetContent

Em seguida, chama IDCompositionVisual::SetContent:

https://learn.microsoft.com/en-us/windows/win32/api/dcomp/nf-dcomp-idcompositionvisual-setcontent

E chama SetRoot:

O updateObject que é recebido em BeginDraw não especifica que tipo é na documentação.

13) Liberar Objetos

Em seguida, libera os objetos criados anteriormente:

14) Confirmar Dispositivo de Composição

E agora, usando o mesmo objeto dcompDevice do tipo IDCompositionDevice, ele chama o método Commit:

15) Chamando hook2

Chamar esse método Commit para em hook2 que captura o desired HeapHandle:

Esta é a pilha de chamadas agora:

Lembre-se de que a função vulnerável pode ser alcançada usando alguns métodos da classe CPrimitiveGroup. Neste ponto, ele cria um Heap, então hook2 captura e salva o HeapHandle correspondente.

16) Chamando o Hook da Função

Antes de retornar ao main, ele também cria um chunk usando RtlAllocateHeap. Ele é então capturado e armazenado na variável base dentro da função hook:

As chamadas para Create e Allocate são executadas uma após a outra:

Ambas (Allocate e Create) são chamadas de DirectComposition::Cdevice::Commit:

17) Chamando o Hook da Função hook4

Depois disso, quando NtDCompositionCommitChannel é chamado, ele para em hook4:

NtDCompositionCommitChannel é chamado daqui:

Também é chamado de DirectComposition::Cdevice::Commit

Vale a pena mencionar que o sistema já agrupou os comandos para enviar por ALPC para DWM. Depois disso, envia comandos usando NtDCompositionCommitChannel.
A função hook4 intercepta as chamadas NtDCompositionCommitChannel e neste ponto mais comandos serão adicionados ao lote.

Vamos ver o que hook4 faz:

Um loop é executado através do chunk apontado por base.

Ele sai do loop quando encontra o valor 0x120 dentro do chunk:

Ele armazena o endereço e o deslocamento onde o valor 0x120 foi localizado:

Ele sobrescreve o valor 0x120 com value4, que é igual a 0x1b0 + 0x8f = 0x23f. Este é o tamanho que ele usará em memcpy quando transbordar:

Ele adiciona 0xbc + 0x90 ao ponteiro de endereço onde o 0x120 estava localizado:

Lembre-se de que no deslocamento 0x48 de base estava o tamanho 0x120. Isso foi sobrescrito por 0x23f, portanto o chunk original deve ter tamanho 0x120:

A origem é o ponteiro de endereço de 0x23f + 0x2c:

Ele inicialmente adicionou 0x90 mas agora está subtraindo 0x90 novamente.
O destino será o endereço do ponteiro para 0x120 + 0xbc:

Ele vai escrever neste:

Todas as escritas estarão dentro do chunk:

Ele vai repetir o loop 3 vezes, que é o resultado da divisão inteira de 0x1b0/0x90:

Depois disso, como o ArgChannelHandle é o mesmo canal que foi usado quando o MappedAddress foi capturado. O PoC adicionará comandos ao lote usando NtDCompositionProcessChannelBatchBuffer. Estes serão processados junto com aqueles que o sistema já havia adicionado. O lote os coleta e então os comandos são enviados todos juntos usando NtDCompositionCommitChannel:

O comando enviado tem o valor 8, que corresponde a SetResourceIntegerProperty para 4 rastreadores diferentes (1,2,3 e 4).

18) Executando Heap Spray

Quando o PoC retorna à função main, ele cria um canal diferente para executar o HeapSpray.

Ele agrupa 0x10000 comandos, que são enviados com _NtDCompositionCommitChannel:

Isso usa o valor CreateResource=1 e o tipo que corresponde a CHolographicInteropTextureMarshaler = 0x50:

As alocações são realizadas no código abaixo. O tamanho dos objetos criados para fazer o spray é 0x1b0:

Em seguida, executa um loop para liberar os objetos criados na etapa anterior e agora faz buracos na distribuição de memória.
A variável counter2 começa em 0x3000 e adiciona passos de 0x20 enquanto é menor que 0x7000:

19) Modificando o Chunk Base Antes do Envio

Ele escreve 0x41s a partir da direção do chunk que estava em base + 0x48 + 44 + 0x1b0
Ou seja, está escrevendo valores que serão usados posteriormente—quando transbordar o chunk adjacente:

Esse pvalue7 está localizado no endereço 0x224 da base:

Em seguida, vai para a função “escribe”:

Ele escreve o pKernelCallbacktable mais 0x388, o endereço LoadLibraryA e o caminho para a DLL que será carregada. Neste caso, eu nomeei de s11.dll.

20) Depurando o Processo DWM

Agora, um depurador de kernel é necessário para parar na função vulnerável quando ocorre o estouro de heap. Isso ocorre porque o processo DWM não pode ser depurado com um depurador de modo de usuário.

Usando IDA PRO para depurar remotamente o alvo, defina um ponto de interrupção condicional para que ele pare quando o tamanho for igual a 0x1b0:

print ("VALUE1 %x" % ((cpu.rax)))
return cpu.rax==0x1b0.

Como um programa de modo de usuário está sendo depurado a partir do kernel, é necessário mudar para o contexto do processo DWM para colocar o ponto de interrupção. Recarregue os símbolos de usuário com:

. reload /user

Recarregue os do kernel com:

. reload /f

Ele irá parar quando ShowWindow for ultrapassado**:**

Ele aloca com tamanho 0x1b0 e copia com tamanho 0x23f, produzindo o estouro de heap:

Neste ponto, a pilha de chamadas se parece com isso:

Para criar o estouro, o DWM recebe valores no código abaixo:

Os valores elaborados em base enviados do meu PoC são lidos usando MapViewofFile do processo DWM no módulo dwmcore.dll:

A função anterior é chamada de:

Quando é enviado usando ALPC de hook4 usando destination_copy (NtDCompositionCommitChannel) ele para:

Lembre-se de que em hook4 comandos, comandos foram adicionados ao lote. No entanto, o sistema também já havia adicionado alguns comandos ao lote, incluindo o base e os dados elaborados:

Neste caso, ele compartilha uma área de memória que começa em 000001cd'178d0000. Quando é usada como origem para executar o memcpy será 0x794 bytes depois nessa mesma área de memória.

O tamanho da área de memória compartilhada é 0x4000:

Ele irá parar quando o tamanho a ser alocado for 0x1b0, e atinge o memcpy para copiar 0x23f bytes:

Além de 0x1b0 na memória está o código que transbordará sobrescrevendo o bloco adjacente:

Quando os chunks são liberados do PoC, ele termina saltando para LoadLibraryA , que carrega a biblioteca elaborada:

Isso vem daqui:

O Heap spray foi feito com objetos de tamanho 0x1b0 do tipo CHolographicInteropTexture.

Como eu fiz buracos na distribuição de memória, isso libera alguns objetos. Como o bloco que vai transbordar também tem tamanho 0x1b0, tem alta probabilidade de estar localizado nos buracos do heap spray.

No destino do memcpy os blocos estão localizados a cada 0x1b0 bytes:

O ponteiro para uma vftable é sobrescrito pelo ponteiro para LoadLibrary:

Antes de sobrescrever:

Depois de sobrescrever:

Lembre-se de que ele terminou saltando para [R11+50], que é o ponteiro para LoadLibraryA.

21) Elevando Privilégios para Nível de Integridade do Sistema

Executando o PoC, copie a DLL no mesmo caminho que consta no PoC:

Após executar o PoC, um processo CMD é executado com privilégios de Nível de Integridade do Sistema do usuário DWM:

Referências:

PoC no GitHub da Fortra: https://github.com/fortra/CVE-2024-30051

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-30051

https://msrc.microsoft.com/update-guide/en-US/advisory/CVE-2024-30051

Isso conclui o PoC. Lembre-se de que, se você executá-lo muitas vezes, o heap permanecerá em um estado instável, então pode ser necessário reiniciar a máquina para que funcione novamente. Além disso, embora possa nem sempre funcionar na primeira tentativa, normalmente funcionará corretamente na segunda ou terceira tentativa. Como você pode ver, a engenharia reversa pode ser difícil, então se você tiver alguma dúvida, pode me consultar.

E-mail: [email protected]

X: @ricnar456

Baixar ferramenta