Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
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.

FeedsContatoPrivacidade© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-88773 — Análise técnica de uma falha de contrabando de requisições por estouro de inteiro no tamanho de chunk HTTP/1.1 do NetScaler nsppe, com desmontagem, fluxos de PoC em nível de rede e diff de patch de firmware. | Kitploit
Ferramentas/GitHubGitHub/scyrix-llc/cve-2026-88773
Análise de VulnerabilidadesExploraçãoEngenharia ReversaExploração de Aplicações WebSegurança WebAnálise de BináriosPapers e PesquisaAnálise de Firmware
GitHubscyrix-llc/cve-2026-88773

CVE-2026-88773

Análise técnica de uma falha de contrabando de requisições por estouro de inteiro no tamanho de chunk HTTP/1.1 do NetScaler nsppe, com desmontagem, fluxos de PoC em nível de rede e diff de patch de firmware.

11há 1 mêsAinda não revisado
Ver RepositórioSite

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

CVE-2026-88773: NetScaler nsppe (HTTP Smuggle)

Estouro de acumulador de 32 bits no tamanho do chunk no parser HTTP/1.1

TL;DR

O parser de corpo chunked do HTTP/1.1 do nsppe lê o campo chunk-size em um registrador de 32 bits e nunca verifica overflow. Com nove ou mais dígitos hexadecimais, o valor sofre wrap silencioso: 100000005 (2^32 + 5) é consumido como um tamanho de chunk de 5.

Isso é apenas um bug engraçado de parser até você lembrar o que o NetScaler faz com o corpo: ele encaminha o enquadramento chunked do cliente para o backend exatamente como recebido, mas decide onde a requisição termina usando seu próprio valor com wrap. Assim, o proxy acredita que a requisição #1 está completa após cinco bytes de corpo, enquanto um backend correto lendo os mesmos bytes calcula 4.294.967.301 e se prepara para um corpo de 4 GB que nunca chegará. Duas máquinas, um único fluxo de bytes, duas opiniões diferentes sobre onde uma requisição termina.

Tudo após a versão do NetScaler de "o fim" é reclassificado como uma nova requisição pipelined na mesma conexão do cliente e então encaminhado de forma independente na conexão compartilhada proxy-para-backend.

O que você obtém depende do próprio parser do backend:

  • O backend também trunca para 32 bits: os bytes contrabandeados são executados como uma requisição independente. Sequestro de prefixo clássico.
  • O backend faz o parse corretamente: dessincronização severa, conexão travada em um corpo de 4 GB que nunca chega. DoS por esgotamento do pool de conexões.

Uma requisição não autenticada. Configuração padrão. Nenhuma pré-condição além de alcançar um vserver HTTP.


Causa raiz

O parser hexadecimal de chunk-size

A tarefa do parser é simples: transformar uma linha de chunk-size como 1a3f\r\n em um número. Ele recebe o buffer em rdi com seu comprimento em esi, acumula o valor hexadecimal no slot de 32 bits em *rdx (entrada e saída, para que um tamanho dividido entre dois pacotes seja retomado sem problemas), ignora espaços em branco e exige o \r\n final. Ele responde com os bytes consumidos, 0 para "precisa de mais dados" ou -1 para malformado.

O loop inteiro, pequeno o suficiente para ver o bug a olho nu:

20af5e0: movzx  eax, BYTE PTR [rbx]
20af5e3: test   BYTE PTR [rax+rax*1+0x2746c80], 0x10   ; ctype: hex digit?
20af5eb: je     20af60b
20af5ed: mov    ecx, r11d
20af5f0: shl    ecx, 0x4                               ; value <<= 4
20af5f3: movzx  r11d, BYTE PTR [rax+0x2746b80]         ; hexval(digit)
20af5fb: add    r11d, ecx                              ; value = value*16 + d
20af5fe: inc    rbx
20af601: dec    r8d
20af604: jne    20af5e0                                ; next digit

r11d é o acumulador, e tem 32 bits de largura. Cada dígito faz value <<= 4; value += digit. Sem contador de dígitos, sem limite de magnitude, sem verificação de overflow, sem jo em lugar algum do loop. O nono dígito não gera erro; ele desloca o topo do número para fora do fim do registrador e ninguém percebe. 0x100000005 & 0xffffffff = 5, e o parser fica perfeitamente satisfeito com isso.

A única verificação de sanidade rejeita a metade errada

Existe exatamente uma verificação de sanidade a jusante, e ela está errada de uma forma quase poética. O driver de dechunking (sub_20af6b0, pontos de chamada 0x20af719 / 0x20af9fb, tamanho armazenado em [r12+0x344]) aplica um teste de sinal ao valor acumulado. Reflita sobre isso por um segundo: os únicos wraps que são capturados são os que caem na metade negativa, [0x80000000, 0xffffffff]. Um tamanho com wrap cujos 32 bits baixos resultam abaixo de 0x80000000 passa como um tamanho de chunk completamente normal. 0x100000005 com wrap para 5 é o caso canônico: com wrap, despercebido e devastador.


Por que ele contrabandeia

O pacote se transforma em um smuggle por causa de uma decisão de design na forma como o NetScaler trata corpos: ele encaminha o enquadramento chunked do cliente para o backend byte a byte, intocado, enquanto usa sua própria compreensão (com wrap) desse enquadramento para decidir onde a requisição do cliente termina.

Assim, o NetScaler encerra a requisição #1 após cinco bytes de corpo e enfileira os 37 restantes como uma segunda requisição, pipelined. O backend, fazendo a aritmética corretamente, lê 100000005 como 4.294.967.301 e continua esperando pelo corpo da requisição #1. A partir desse ponto, ambas as máquinas estão lendo o mesmo stream e vivendo em mundos diferentes.

Em qual mundo você acaba depende do backend.

Fluxo A: backend de 64 bits correto. A cunha de dessincronização.

Attacker                           NetScaler (<=73.30)                            Backend (RFC 64-bit)
   |                                        |                                               |
   |  [1] one TCP segment, 122 bytes:       |                                               |
   |    POST /wrap HTTP/1.1                 |                                               |
   |    Host: poc                           |                                               |
   |    Transfer-Encoding: chunked          |                                               |
   |    100000005\r\nAAAAA\r\n0\r\n\r\n     |                                               |
   |    GET /SMUGGLED HTTP/1.1              |                                               |
   |    Host: poc\r\n\r\n                   |                                               |
   |--[1]---------------------------------->|                                               |
   |                                        | [2] parses "100000005" into a u32             |
   |                                        |     wraps to 5; consumes AAAAA;               |
   |                                        |     sees terminating 0-chunk                  |
   |                                        |     ==> request #1 COMPLETE                   |
   |                                        |     trailing 37 bytes held as a               |
   |                                        |     pending pipelined request #2              |
   |                                        |                                               |
   |                                        |--[3] 85 bytes, framing VERBATIM-------------->|
   |                                        |   POST /wrap HTTP/1.1                         |
   |                                        |   Transfer-Encoding: chunked                  |
   |                                        |   100000005\r\nAAAAA\r\n0\r\n\r\n             |
   |                                        |                                               |
   |                                        |                                               | [4] parses 0x100000005 (64-bit)
   |                                        |                                               |     = 4,294,967,301 bytes
   |                                        |                                               |     waiting for a body that never co
   |                                        |                                               |     [WEDGED: no response, ever]
   |                                        |                                               |
   |                                        | GET /SMUGGLED waits in NetScaler's            |
   |                                        | pipeline queue forever: response #1           |
   |                                        | never arrives                                 |
   |                                        |                                               |

Fluxo B: backend que trunca para 32 bits como o NetScaler. O contrabando concluído.

Baixar ferramenta