Analisando um kernel address disclosure via Windows Event Log - CVE-2026-32217

CVE-2026-32217 Media Produto Microsoft Windows — serviço Windows Event Log (wevtsvc.dll) Classe Divulgação de endereço de kernel (bypass de KASLR) Vetor Local · autenticado CorreÇÃo Atualizações cumulativas de 14/04/2026 Author: Felipe Xavier Introdução Este artigo analisa o CVE-2026-32217, uma vulnerabilidade que expõe endereços do kernel por meio do Windows Event Log. O caminho afetado envolvia registros WHEA disponíveis no canal Microsoft-Windows-Kernel-WHEA/Operational, nos quais endereços virtuais do kernel podiam ser obtidos por um usuário local com baixos privilégios.  Depois de algum tempo analisando vulnerabilidades relacionadas ao Windows Event Log, assisti a uma palestra de Yarden Shafir sobre as mudanças recentes nas proteções contra vazamentos de endereços do kernel. A apresentação forneceu o estímulo que faltava para que eu voltasse a investigar essa superfície de ataque com mais atenção. Um dos detalhes mais interessant...

Desenvolvendo o exploit para o CVE-2022-2992

CVE-2022-2992 Crítica
ProdutoGitLab CE/EE (< 15.3.3, 15.2.5, 15.1.6)
ClasseInjeção de comando no Redis via desserialização (Sawyer)
VetorRede · autenticado (importação de repositório)
CorreçãoAtualizar para 15.3.3 / 15.2.5 / 15.1.6

o bug

Acompanhando as publicações de vulnerabilidades na plataforma de bug bounty HackerOne, encontramos a vulnerabilidade 1679624, reportada pelo usuário vakzz, e que se tornou pública dia 6 de Outubro de 2022.

Relatório original de vakzz no HackerOne.

Como de constume, o relatório do vakzz fornece uma visão completa do bug, entretanto algo que chamou bastante atenção foi o fato da equipe de segurança do Gitlab não fornecer o ID do problema, dificultando o rastreamento da vulnerabilidade.

Trecho do relatório que chamou nossa atenção.

Após algum tempo investigando o repositório do Gitlab, finalmente encontramos a issue 371884 com o titulo Remote code execution via Github import, o que parecia bastante promissor. A issue, de acordo com um comentário, havia sido corrigida na versão 15.3.2.

A issue relacionada no repositório do GitLab.

O próximo passo foi olhar para os commits dessa versão, onde identificamos um commit interessante, provavelmente relacionado ao problema:

No overriding methods for Sawyer class

Busca pelos commits que corrigiram o problema.

Após ler o commit, percebemos que o problema era um pouco mais complexo do que imaginávamos.

A Causa

O Sawyer é uma biblioteca que converte um hash em um objeto cujos métodos são baseados nas chaves do hash.

O exemplo de código abaixo mostra como o Sawyer converte um hash em um objeto:

Como o Sawyer converte um hash em um objeto.

O problema é que a gem redis usa to_s e bytesize para gerar o comando RESP (protocolo de serialização do Redis).

https://github.com/redis/redis-rb/blob/v4.4.0/lib/redis/connection/command_helper.rb#L19-L21

def build_command(args)
  command = [nil]

  args.each do |i|
    if i.is_a? Array
      i.each do |j|
        j = j.to_s
        command << "$#{j.bytesize}"
        command << j
      end
    else
      i = i.to_s
      command << "$#{i.bytesize}"
      command << i
    end
  end

  command[0] = "*#{(command.length - 1) / 2}"
  command << ""
  command.join(COMMAND_DELIMITER)
end

A gem redis usa to_s e bytesize para montar o comando RESP (build_command em redis-rb).

Portanto, se o Sawyer::Resource for controlável por um adversário, ele pode ser usado para sobrescrever os metódos .to_s e .bytesize que posteriormente serão usado para "montar" o comando comando RESP e executar comandos no Redis.

Os dois trechos de código subsequentes nos ajudam a exemplificar como o Redis se comporta quando adicionamos uma hash e um objeto Sawyer e depois recuperamos esse valor. O resultado será o seguinte:

Hash

Comportamento do Redis ao receber um Hash.

Sawyer::Resource

Comportamento do Redis ao receber um Sawyer::Resource.

No Gitlab o método import_repository recebe o parâmetro default_branch que vem do repositório do cliente (que é um Sawyer::Resource aninhado de dados), e o parâmetro é passado para change_head, que então chama branch_exists? e branch_names_include? e por fim, passa o valor para o redis.

https://gitlab.com/gitlab-org/gitlab/-/blob/v15.3.1-ee/lib/gitlab/repositorycacheadapter.rb#L71

O parâmetro default_branch chega ao Redis via import_repository.

Ou seja, o "default_branch" é um valor controlado pelo atacante, e é passado para o Redis.O cenário de exploração consiste em criar um repositório no GitHub com um branch chamado to_s e outro chamado bytesize, e então importar esse repositório para o GitLab.

A Correção

Como podemos ver no commit a classe Sawyer::Resource é "patcheada" para evitar que métodos já existentes sejam sobrescritos. Isso significa que o método to_s não será sobrescrito pela classe Sawyer::Resource e, portanto, nenhum dado malicioso será passado para a gem redis.

O commit de correção.

Ambiente de testes

Após entender qual versão do GitLab estava vulnerável, criamos um ambiente de testes para reproduzirmos o problema.

Provavelmente a forma mais fácil de criar um ambiente de testes é usando o GitLab Omnibus Docker, utilizando a seguinte imagem: gitlab/gitlab-ce:15.1.0-ce.0

Esses são os detalhes do ambiente de testes:

Detalhes do ambiente de testes.

A Exploração

Resumidamente a exploração dessa vulnerabilidade consiste em:

1. Criar um payload

O payload deve ser no formato RESP (protocolo de serialização Redis) que executa o comando escolhido. Esse payload é gerado usando uma chave de sessão aleatória do GitLab, que é usada posteriormente para forçar o GitLab a executar o payload.

Isso pode ser feito usando o script disponível aqui.

2. Criar um grupo no GitLab

O grupo criado será usado para importar o repositório falso.

Criação do grupo no GitLab.

3. Criar um repositório falso no GitHub

Servir um repositório falso no GitHub que responde a solicitações GET em /api/v3/repos/\w+/\w+ com um default_branch que substitui to_s e bytesize, por exemplo:

{
  "default_branch": {
    "to_s": {
      "to_s": "ggg\r\nPAYLOAD",
      "bytesize": 3
    }
  }
}

4. Importar o repositório falso no GitLab.

Importação do repositório falso no GitLab.

Podemos confirmar que o payload foi injetado no Redis usando o seguinte comando:

Confirmação de que o payload foi injetado no Redis.

5. Acionar o payload

Para acionar o payload é preciso acessar o GitLab com o cookie de sessão utilizado para gerar o payload, para forçar a execução do payload:

Requisição que aciona o payload.

❕ As seguintes premissas precisam ser atendidas:

  • Possuir um token de acesso pessoal do GitLab com permissões para criar grupos e importar repositórios. Tal token dever estar definido na variável de ambiente API_TOKEN;
  • A variável de ambiente GROUP_NAME deve estar definida com o nome do grupo que será criado;
  • O Ngrok deve estar rodando e apontando para o servidor falso. A variável de ambiente NGROK_URL é definida com o endereço do Ngrok. esse passo é importante para garantir que o servidor falso seja acessível a partir do servidor do GitLab e tenha um TLD válido com um certificado SSL válido [1].

O Exploit

Criamos uma prova de conceito para explorar o problema, que pode ser encontrada no nosso repositório. Para a utilização da prova de conceito, será preciso uma credencial de acesso e a execução dos seguintes comandos:

Execução da prova de conceito.

Também submetemos um módulo de exploração para o Metasploit Framework, que pode ser encontrado aqui

Notas extras

Na sua configuração padrão, o GitLab não permite importar de uma rede local, deve ser um TLD válido. Essa é a razão pela qual o Ngrok é usado para criar um servidor falso com um TLD válido e um certificado SSL válido.

Na configuração padrão, o GitLab não importa de uma rede local.

References