CiberLab
Logotipo Ciência Embarcada Ciência Embarcada
Relatório técnico de análise de vulnerabilidade

HTTP2Shell

Escrita fora dos limites no decodificador Brotli, CVE-2026-111497, do Node.js ao .NET e ao Python

Apesar do nome, o HTTP2Shell não é uma falha do protocolo HTTP/2. É um defeito no decodificador da biblioteca de compressão Brotli, do Google, que dezenas de produtos embutem em cópia própria. Um fluxo comprimido malformado faz o decodificador pedir mais dados quando deveria rejeitar a entrada, e ele continua gravando bytes num buffer interno de oito posições até passar do fim. Quem descomprime dados de origem não confiável fica exposto: servidores Express 5 que aceitam corpo comprimido, clientes que usam fetch() contra um servidor hostil, aplicações .NET e Python, proxies e leitores de arquivos. A correção do Brotli estava no repositório desde fevereiro, mas só virou versão e aviso em 21 de outubro; em 27 de outubro a CISA incluiu a falha no catálogo de vulnerabilidades exploradas.

Objeto analisado Brotli, CVE-2026-111497
Severidade avaliada Crítica
Classificação TLP:CLEAR
Base de evidência OSINT verificado
Data de emissão 2 de novembro de 2026
Versão 1.0

00Sumário

  1. 01Sumário executivo
  2. 02Identificação do objeto analisado
  3. 03Contexto técnico: quem descomprime o quê
  4. 04Mecanismo da vulnerabilidade e causa raiz
  5. 05Caminhos de exposição e exploração
  6. 06Verificação: a exposição do CiberLab
  7. 07Detecção e inventário
  8. 08Mitigação e resposta a incidentes
  9. 09Indicadores de comprometimento
  10. 10Limitações da análise
  11. 11Referências
Achado central

Quando um fluxo Brotli sinaliza uma troca de bloco inválida para a própria estrutura, o decodificador vulnerável relata uma necessidade temporária de mais entrada, em vez de um erro permanente de formato, e continua anexando bytes a um buffer interno de oito bytes até escrever além do fim [1] [2]. O defeito está na biblioteca C do Brotli, das versões 1.0.3 a 1.2.0, e alcança todo software que descomprime Brotli de origem não confiável com uma cópia sem correção [3]. Atualizar só o Node.js não corrige as outras cópias da biblioteca na mesma máquina [3] [4].

01Sumário executivo

A CVE-2026-111497 é uma escrita fora dos limites (out-of-bounds write) no decodificador da biblioteca Brotli, o formato de compressão do Google usado na web, em fontes e em arquivos [1] [3]. Foi relatada ao Node.js por Khai Tran e Sylvie Mayer, do OpenAI Codex Security, e corrigida em 21 de outubro de 2026 [1] [4]. O Google, autoridade que emitiu a CVE, atribuiu 9.2 em CVSS v4; o Node.js classifica como alta, e a Red Hat, como importante [3] [4].

9.2 pontuação CVSS v4 atribuída pelo Google [3] [4]
8 B tamanho do buffer interno que a escrita ultrapassa [1]
61% das redes monitoradas pela Akamai rodam versões afetadas do Node.js [2]
9 projetos com cópia embutida ainda sem correção no rastreador da Phoenix [3]

O nome HTTP2Shell é o último de uma série: a falha foi chamada de Node2Shell quando saiu a correção do Node.js, de Python2Shell quando as bibliotecas Python entraram na lista e de HTTP2Shell quando a lista se espalhou por toda a pilha HTTP [3]. Não há defeito no protocolo HTTP/2; o nome aponta para o canal mais comum de entrega, o cabeçalho Content-Encoding: br de requisições e respostas HTTP [3] [4].

A exploração é real. Em 22 de outubro, pesquisadores demonstraram execução de código no Node.js 24.22.0 oficial para arm64 [3] [4]; em 27 de outubro, a CISA incluiu a falha no catálogo KEV, com prazo de correção para órgãos federais em 10 de novembro [4]. Não há solução de contorno no Node.js: a única correção completa é atualizar [1] [2].

A ação é: atualize o Node.js para 22.23.4, 24.22.1 ou 26.12.1, e depois procure as outras cópias do Brotli na mesma infraestrutura, como Bun, .NET, Python, Envoy e Istio, com as versões da tabela 2 [1] [3] [4]. Reinicie os processos e reconstrua as imagens de contêiner. A seção 6 traz a verificação da plataforma CiberLab, que roda em Node.js.

02Identificação do objeto analisado

Natureza da evidência

Não houve submissão a sandbox, execução de exploit nem análise de amostra. O CiberLab não reproduziu a falha. A causa raiz vem do aviso oficial do Node.js [1] e da análise da Akamai [2]; o alcance entre ecossistemas, do rastreador da Phoenix Security [3]; versões, cronologia e inclusão no KEV, da Cycode [4]. A verificação da seção 6, ao contrário, foi feita diretamente sobre o código-fonte e a imagem de contêiner da plataforma CiberLab.

Tabela 1. Ficha técnica da vulnerabilidade CVE-2026-111497
CampoValor
IdentificadorCVE-2026-111497, apelidada Node2Shell, Python2Shell, HTTP2Shell e Broccoli2Shell [3]
Componente afetadoDecodificador C da biblioteca Brotli, versões 1.0.3 a 1.2.0 [3]
Classe de fraquezaEscrita fora dos limites de buffer [1] [2]
Ponto de entradaQualquer dado Brotli de origem não confiável que chegue ao decodificador [1]
ImpactoQueda do processo, vazamento de memória ou execução remota de código [1]
Pontuação9.2 em CVSS v4 (Google); alta (Node.js); importante (Red Hat); 8.1 em CVSS 3.1 (Microsoft) [3] [4]
DescobertaKhai Tran e Sylvie Mayer, OpenAI Codex Security [1] [4]
Correção upstreamBrotli 1.0.10, 1.1.1 e 1.2.1, com o novo erro BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH [2] [3]
Inclusão no KEV27 de outubro de 2026, prazo federal em 10 de novembro [4]

A falha está numa biblioteca, então cada produto que a embute tem sua própria versão corrigida. A tabela reúne as principais:

Tabela 2. Versões corrigidas por ecossistema
ProdutoVersões corrigidas
Node.js22.23.4, 24.22.1 e 26.12.1; as linhas 25.x, 23.x, 20.x e anteriores não recebem correção [1] [2]
Bun1.4.4; afetadas de 1.0.21 a 1.4.3 [3] [4]
.NETRuntime 8.0.33, 9.0.22 e 10.0.14, em atualização extraordinária de 25 de outubro; o .NET Framework não é afetado [4]
PowerShell7.4.22, 7.5.13 e 7.6.9 [4]
Python (PyPI)brotli 1.2.1 e brotlicffi 1.2.1.0; o pacote da distribuição é corrigido pela distribuição [3] [4]
Ruby, PHP, JavaGem brotli 0.9.1, extensão PHP brotli 0.21.1, brotli4j 1.24.0 [3] [4]
Envoy1.40.0, 1.39.3, 1.38.6, 1.37.8 e 1.36.12 [3] [4]
Istio1.31.2, 1.30.6 e 1.29.9 [3] [4]
Ubuntu 24.04libbrotli1 1.1.0-2ubuntu0.1 [3]

03Contexto técnico: quem descomprime o quê

Brotli é um formato de compressão sem perdas criado pelo Google, adotado pelos navegadores e servidores web como alternativa ao gzip [3]. Na web, quem envia dados comprimidos declara o formato no cabeçalho Content-Encoding, e o valor br indica Brotli [4]. O ponto que importa para esta falha é quem descomprime, porque é o decodificador que tem o defeito, e não o compressor.

3.1Três lugares onde a descompressão acontece

  1. No servidor, ao receber: um servidor que aceita corpo de requisição comprimido descomprime o que o cliente mandou. No Express 5 com body-parser 2, os leitores JSON, raw, text e urlencoded fazem isso por padrão, porque a opção inflate começa ligada [2]. Um servidor HTTP do Node sem framework não descomprime corpo de requisição sozinho [2].
  2. No cliente, ao buscar: o fetch() nativo do Node descomprime automaticamente respostas em Brotli, então um servidor malicioso ou comprometido ataca quem o consulta [2]. O HttpClient do .NET com descompressão automática faz o mesmo [3].
  3. Em arquivos: leitores de Parquet, extratores de arquivos compactados (7-Zip, p7zip), conversores de fonte WOFF2, leitores de PDF e ferramentas de firmware embutem o decodificador para abrir arquivos [3].

3.2Cópias embutidas

A Brotli é distribuída como código-fonte que os projetos copiam para dentro do próprio repositório (vendoring). O Node.js, o Bun, o .NET, o Chromium, o Firefox, o DuckDB, o ClickHouse e o Apache Arrow carregam cada um a sua cópia [3]. Por isso atualizar o pacote libbrotli do sistema não corrige esses programas, e atualizar o Node.js não corrige o .NET instalado na mesma máquina [3] [4]. Cada cópia precisa da sua própria atualização.

04Mecanismo da vulnerabilidade e causa raiz

4.1O erro que virou espera

Um fluxo Brotli é dividido em blocos, e o fluxo pode sinalizar a troca de um tipo de bloco para outro [1]. Quando essa troca é inválida para a estrutura do próprio fluxo, o decodificador vulnerável não reconhece o erro: relata que precisa de mais entrada, uma condição temporária, em vez de um erro permanente de formato [1] [2]. O decodificador em modo de fluxo (streaming) então continua anexando os bytes que chegam a um buffer interno de oito bytes, e passa a escrever além do fim dele [1].

O resultado é corrupção da memória do processo que descomprime, com três desfechos possíveis: queda, revelação de memória do processo ou execução remota de código [1]. Nenhuma autenticação é necessária: basta que o atacante consiga entregar o fluxo ao decodificador, por uma requisição ou por uma resposta [2].

4.2A correção

A correção do Brotli introduz um código de erro próprio, BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH, e faz o decodificador rejeitar o fluxo malformado em vez de esperar mais dados [2]. Isso dá um teste simples para cópias embutidas: um arquivo decode.h do Brotli sem esse identificador é uma cópia sem correção [3].

Oito meses de correção à vista

O commit da correção entrou no repositório do Brotli em 27 de fevereiro de 2026, sem versão nem aviso; as versões corrigidas e a CVE só saíram em 21 e 22 de outubro [4]. Durante esse intervalo, a correção estava pública para quem comparasse o código, e os produtos que embutem o Brotli continuavam vulneráveis. Não espere um aviso formal para atualizar bibliotecas de análise de dados não confiáveis: acompanhe os commits de segurança dos componentes críticos.

05Caminhos de exposição e exploração

5.1Quando uma aplicação está exposta

Ter uma versão afetada não basta: é preciso que dados Brotli não confiáveis cheguem ao decodificador. A tabela reúne os caminhos documentados:

Tabela 3. Caminhos que levam dado não confiável ao decodificador
CaminhoQuem envia o fluxoExposição
Express 5 ou body-parser 2 com inflate padrãoQualquer cliente, sem login [2]Alta
Koa, @fastify/compress com descompressão de requisiçãoQualquer cliente [4]Alta
zlib.brotliDecompress() ou createBrotliDecompress() sobre entrada externaQuem fornece a entrada [1] [2]Alta
ASP.NET Core com middleware de descompressão de requisiçãoQualquer cliente [3] [4]Alta
fetch() de URL fornecida pelo usuárioO servidor que o usuário indicar [2] [4]Alta
fetch() para APIs fixas de terceirosSó um servidor comprometido ou um intermediário que quebre o TLS [2]Baixa
Envoy e IstioClientes, se a descompressão Brotli estiver habilitada [3] [4]Condicional
Leitura de Parquet, 7z ou WOFF2 de origem externaQuem fornece o arquivo [3]Condicional

Apache com mod_brotli e Nginx não decodificam Brotli por padrão; eles comprimem as respostas, e não descomprimem as requisições [3].

5.2Estado da exploração

  1. Execução demonstrada: a Imperva obteve execução de código no Node.js 24.22.0 oficial (Linux arm64), com várias tentativas; o exploit não foi publicado [3] [4]. A Cycode cita o Express 5 e o body-parser 2 como caminhos de requisição que a pesquisa pública mostrou chegarem à execução de código [4].
  2. Prova de conceito pública: o repositório ikow/brotli2crash só executa código em compilações enfraquecidas; nas normais, causa queda e leitura limitada de memória [3].
  3. Exploração em campo: não havia ataque confirmado até 24 de outubro [3]; em 27 de outubro, a CISA incluiu a CVE no KEV com base em evidência de exploração ativa [4]. A inclusão confirma o abuso do componente, mas não diz quais produtos foram atacados.

5.3As outras falhas da mesma versão do Node.js

A versão de 21 de outubro corrigiu dez falhas; além da do Brotli, três de severidade alta merecem atenção de quem faz requisições de saída [1]:

Tabela 4. Falhas de severidade alta corrigidas na mesma versão do Node.js
CVEFalhaLinhas afetadas
CVE-2026-105482dns.resolve trunca nomes com caractere NUL [1]26.x, 24.x e 22.x [1]
CVE-2026-105483Desvio da checagem de nome do certificado TLS quando o IP é escrito com dígitos de largura total [1]26.x e 24.x [1]
CVE-2026-106069Desvio da checagem de nome do certificado TLS quando o host resolve frouxamente para um IP [1]26.x, 24.x e 22.x [1]

06Verificação: a exposição do CiberLab

A plataforma CiberLab roda em Node.js, então foi verificada contra cada caminho da tabela 3. A conclusão é que a exposição é baixa, mas a versão do Node precisa ser confirmada e fixada.

Tabela 5. Verificação do CiberLab contra a CVE-2026-111497
Ponto verificadoEstado no CiberLabResultado
Versão do Node na imagemO Dockerfile usa a etiqueta móvel node:22-bookworm-slim, atualizada no Docker Hub em 28 de outubro; a versão em produção depende da data do último build e do uso do cacheConfirmar
Express, body-parser ou KoaNenhum deles é dependência; a aplicação usa os manipuladores de rota do Next.jsNão se aplica
Varredor de segurança (lib/security/scanner)Busca sites indicados pelo usuário, mas usa o módulo http com Accept-Encoding: identity e não descomprime a respostaNão exposto
Coleta do boletim (lib/bulletins)Usa fetch(), que descomprime Brotli, mas só contra hosts fixos por HTTPS: NVD, CISA, CVE.org, GitHub, OSV, FIRST, CERT-EU e Exploit-DBExposição baixa
Ambiente de desenvolvimentoA estação de trabalho roda Node 24.19.0, abaixo de 24.22.1Vulnerável
Ação para a operação do CiberLab

Troque a etiqueta móvel por node:22.23.4-bookworm-slim nos dois estágios do Dockerfile e reconstrua sem cache, para que a versão corrigida fique registrada no repositório em vez de depender de quando o build rodou [4]. Atualize o Node da estação de desenvolvimento para 24.22.1 ou superior. Não conte com a etiqueta móvel: um build feito com a camada base em cache continua no Node antigo mesmo depois de a etiqueta mudar.

07Detecção e inventário

Para esta falha, inventário vem antes de assinatura: a Cycode recomenda começar pela verificação de versões, e não por varredura de rede [4]. A ordem abaixo vai do mais barato ao mais caro.

7.1Inventário de versões

Os comandos abaixo cobrem os ambientes mais comuns, inclusive dentro de contêineres e nas máquinas de build [3] [4]:

ShellCompilado de [3] [4]
# Runtimes JavaScript
node -v
bun --version

# Python
pip show brotli brotlicffi

# .NET
dotnet --list-runtimes

# Processos com o decodificador carregado dinamicamente
# (nao encontra copias estaticas ou embutidas)
grep -l brotlidec /proc/*/maps 2>/dev/null

# Copias embutidas no codigo-fonte, inclusive submodulos:
# um decode.h sem o novo erro e uma copia sem correcao
grep -rL BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH \
  $(find . -path '*brotli*' -name decode.h)

Compare com a versão corrigida do pacote da distribuição, e não com a versão do upstream, para não acusar falsamente pacotes com correção retroportada [3].

7.2Caminhos de código

Procure no código os pontos onde o decodificador é alcançado: AddRequestDecompression, UseRequestDecompression, AutomaticDecompression com Brotli ou All, BrotliStream e BrotliDecoder no .NET; Express 5, body-parser 2, Koa e @fastify/compress no Node; fetch() sobre URL fornecida pelo usuário; e chamadas diretas a node:zlib [4].

7.3Regra Suricata para requisições comprimidas

Onde a aplicação não espera corpo comprimido, uma requisição com Content-Encoding: br já é anômala. A regra abaixo alerta esse caso em sensores com visibilidade do tráfego decifrado:

SuricataDerivada de [2] [4], não validada em produção
alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"CIBERLAB BROTLI CVE-2026-111497 REQUEST BODY WITH CONTENT-ENCODING BR"; \
    flow:to_server,established; \
    http.header_names; content:"Content-Encoding"; nocase; \
    http.header; content:"Content-Encoding|3a 20|br"; nocase; \
    classtype:attempted-admin; sid:9000301; rev:1; \
    reference:cve,2026-111497; reference:url,ciberlab.seg.br/reports/http2shell;)

7.4Sinais no host

Uma exploração malsucedida derruba o processo, e uma bem-sucedida tende a gerar processos filhos inesperados [1] [2]. Alerte quedas repetidas de processos Node, Bun ou .NET logo após requisições com corpo comprimido ou após buscas de saída, shells filhos desses processos e conexões de saída novas a partir dos servidores de aplicação.

08Mitigação e resposta a incidentes

8.1Correção definitiva

Atualize cada cópia do Brotli para a versão corrigida do seu produto (tabela 2), reinicie os processos e reconstrua as imagens de contêiner com docker build --pull [4]. Reconstrua também aplicações .NET autocontidas e binários independentes do Bun, que carregam o runtime dentro de si [4]. Linhas do Node.js fora de suporte não recebem correção e precisam migrar [1] [2].

8.2Medidas temporárias

  1. Desligue a descompressão de requisições: no body-parser, inflate: false faz o servidor recusar corpos comprimidos com HTTP 415 [2]. Isso também recusa gzip [4].
  2. Recuse Content-Encoding: br na borda: no proxy ou balanceador, para requisições que chegam [4]. Cloudflare, Azure, Imperva e Vercel publicaram regras de WAF, que só cobrem o tráfego de entrada [3].
  3. Proteja os clientes: remover br do Accept-Encoding não basta, porque um servidor hostil manda Brotli mesmo assim; recuse respostas com Content-Encoding contendo br antes de ler o corpo [3].
  4. Segmente: limite o acesso de entrada aos serviços Node.js e a saída dos hosts afetados [2].
  5. Arquivos: recuse Parquet com codec Brotli de origem não confiável antes de analisá-lo [3].
A borda não protege o fetch()

Regras de WAF e de proxy só inspecionam o que entra. Um serviço que busca dados de fora com fetch() recebe a resposta maliciosa por uma conexão que ele mesmo abriu, e nenhuma regra de entrada a examina [3] [4]. Não declare um serviço protegido só porque a borda recusa Content-Encoding: br: atualize o runtime.

8.3Resposta a um comprometimento

Se houver sinal de exploração, trate o processo afetado como comprometido: gire os segredos que ele podia ler, reconstrua a instância a partir de imagem limpa com o runtime corrigido e confirme, depois do reinício, que o componente corrigido é o que está rodando [3]. Revise as conexões de saída feitas pelo servidor após a requisição ou a busca suspeita.

8.4Mapeamento de técnicas MITRE ATT&CK

IDs e nomes conferidos na base oficial do MITRE ATT&CK for Enterprise [5]:

Tabela 6. Mapeamento de táticas e técnicas conforme MITRE ATT&CK
TécnicaNomeRelação com o caso
T1190Exploit Public-Facing ApplicationCorpo Brotli malformado enviado a servidor que descomprime requisições [2] [4]
T1203Exploitation for Client ExecutionResposta Brotli maliciosa descomprimida pelo fetch() do cliente [2] [4]
T1005Data from Local SystemRevelação de memória do processo que descomprime [1]
T1499.004Endpoint Denial of Service: Application or System ExploitationQueda do processo, desfecho da prova de conceito pública em compilações normais [1] [3]
T1588.005Obtain Capabilities: ExploitsProva de conceito pública e exploit da Imperva, não publicado [3]

09Indicadores de comprometimento

Nenhuma fonte publicou IPs, domínios ou hashes ligados à exploração. Os indicadores abaixo são de exposição e de comportamento:

Tabela 7. Indicadores de comprometimento e artefatos de detecção
TipoIndicadorSeveridadeContexto e ação
Vulnerabilidade CVE-2026-111497 Crítica Escrita fora dos limites no Brotli, no KEV desde 27/10. Atualize cada cópia [1] [4].
Requisição Content-Encoding: br Alta Corpo comprimido onde a aplicação não espera; recuse na borda [4].
Resposta Content-Encoding: br em busca de saída Média Comum e legítimo; relevante quando o destino foi indicado por usuário [3].
Artefato de código decode.h sem BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH Alta Cópia embutida do Brotli sem correção [3].
Host queda de processo node, bun ou dotnet Alta Repetida após requisição comprimida ou busca de saída [1] [3].
Host shell filho de node, bun ou dotnet Crítica Possível execução de código; isole o host.

10Limitações da análise

  1. Ausência de detonação: o CiberLab não reproduziu a falha nem executou exploit. A causa raiz e as versões vêm das referências [1] a [4].
  2. Divergência sobre a exploração: a Phoenix registrava a falha fora do KEV em 24 de outubro [3]; a Cycode registra a inclusão em 27 de outubro [4]. Este relatório adota a data mais recente, mas nenhuma fonte descreve quais ambientes foram atacados.
  3. Rastreador incompleto: a lista de cópias embutidas da Phoenix ignora submódulos, alguns repositórios grandes e cópias privadas [3]. A tabela 2 não é exaustiva.
  4. Regra derivada: a regra Suricata da seção 7.3 foi deduzida dos caminhos descritos em [2] e [4] e não foi testada em produção. Ela detecta a presença do cabeçalho, e não o fluxo malformado.
  5. Versão em produção: a seção 6 verificou o Dockerfile e a etiqueta do Docker Hub, mas não a versão do Node em execução no servidor, que depende de quando o build rodou. O comportamento do Next.js diante de corpo de requisição com Content-Encoding: br não foi testado.

11Referências

  1. [1] Node.js October 2026 Security Releases. Projeto Node.js, 21 de outubro de 2026. Fonte primária da causa raiz, das linhas afetadas, das versões corrigidas, dos créditos e das demais falhas da mesma versão. https://nodejs.org/en/blog/vulnerability/october-2026-security-releases
  2. [2] Inside Node2Shell: A Closer Look at CVE-2026-111497. Akamai Security Research, 26 de outubro de 2026. Caminhos de alcance no Node.js (Express 5, body-parser 2, fetch), correção upstream, telemetria e mitigações. https://www.akamai.com/blog/security-research/inside-node2shell-closer-look-cve-2026-111497
  3. [3] HTTP2Shell Expansion (CVE-2026-111497): Where the Brotli Decoder Bug Went After Node.js and Python. Phoenix Security, outubro de 2026. Histórico dos nomes, rastreador de cópias embutidas, versões por ecossistema e orientação de detecção. https://phoenix.security/http2shell-expansion-cve-2026-111497-brotli-bundled-copies/
  4. [4] node2shell / HTTP2Shell (CVE-2026-111497): What to patch and how to check if you're exposed. Cycode, atualizado em 26 de outubro de 2026. Cronologia desde o commit de fevereiro, inclusão no KEV, versões do .NET e demais produtos, comandos de inventário. https://cycode.com/blog/node2shell-cve-2026-111497/
  5. [5] MITRE ATT&CK Enterprise. The MITRE Corporation. Fonte dos IDs e nomes de técnica da tabela 6, conferidos em 2 de novembro de 2026. https://attack.mitre.org/

HTTP2Shell, escrita fora dos limites no decodificador Brotli. Relatório técnico de análise de vulnerabilidade, versão 1.0, emitido em 2 de novembro de 2026.
CiberLab, uma iniciativa do Ciência Embarcada.
Classificado TLP:CLEAR: distribuição livre, sem restrição de compartilhamento.