Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-88773 — Технический анализ уязвимости контрабандного запроса из-за целочисленного переполнения размера чанка HTTP/1.1 в NetScaler nsppe, с дизассемблированием, PoC-потоками на уровне сетевых пакетов и диффом патча прошивки. | Kitploit
Инструменты/GitHubGitHub/scyrix-llc/cve-2026-88773
Анализ уязвимостейЭксплуатацияОбратная инженерияЭксплуатация веб-приложенийВеб-безопасностьАнализ Бинарных ФайловСтатьи и ИсследованияАнализ Прошивок
GitHubscyrix-llc/cve-2026-88773

CVE-2026-88773

Технический анализ уязвимости контрабандного запроса из-за целочисленного переполнения размера чанка HTTP/1.1 в NetScaler nsppe, с дизассемблированием, PoC-потоками на уровне сетевых пакетов и диффом патча прошивки.

111 месяц назадЕщё не проверено
РепозиторийСайт

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

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

Переполнение 32-битного аккумулятора размера чанка в парсере HTTP/1.1

TL;DR

Парсер тела с чанковой передачей (chunked-body) в HTTP/1.1 nsppe считывает поле размера чанка в 32-битный регистр и никогда не проверяет переполнение. Девять или более шестнадцатеричных цифр — и значение молча переполняется: 100000005 (2^32 + 5) воспринимается как размер чанка 5.

Это забавный баг парсера, пока не вспомнишь, что NetScaler делает с телом: он пересылает чанковую разметку клиента бэкенду ровно в том виде, в каком получил, но определяет, где заканчивается запрос, используя собственное переполненное значение. Так что прокси считает запрос №1 завершённым после пяти байт тела, тогда как корректный бэкенд, читающий те же байты, вычисляет 4 294 967 301 и готовится принять тело размером 4 ГБ, которое так и не придёт. Две машины, один поток байтов, два разных мнения о том, где заканчивается запрос.

Всё после версии NetScaler «конца» переклассифицируется как новый конвейерный запрос в том же клиентском соединении, а затем независимо пересылается по общему соединению прокси-бэкенд.

Что получится в итоге, зависит от собственного парсера бэкенда:

  • Бэкенд тоже усекает до 32 бит: контрабандные байты выполняются как независимый запрос. Классический перехват префикса (prefix-hijack).
  • Бэкенд парсит корректно: жёсткий рассинхрон (desync), соединение зависает на теле размером 4 ГБ, которое так и не придёт. DoS через исчерпание пула соединений.

Один неаутентифицированный запрос. Конфигурация по умолчанию. Никаких предварительных условий, кроме доступа к HTTP vserver.


Первопричина

Парсер шестнадцатеричного размера чанка

Задача парсера проста: превратить строку размера чанка вроде 1a3f\r\n в число. Он принимает буфер в rdi с его длиной в esi, накапливает шестнадцатеричное значение в 32-битный слот по адресу *rdx (вход и выход, так что размер, разделённый между двумя пакетами, корректно возобновляется), пропускает пробельные символы и требует завершающий \r\n. Он возвращает число потреблённых байт, 0 для «нужно больше данных» или -1 для некорректных данных.

Весь цикл, достаточно маленький, чтобы увидеть баг невооружённым глазом:

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 — это аккумулятор, и он имеет ширину 32 бита. Каждая цифра выполняет value <<= 4; value += digit. Ни счётчика цифр, ни ограничения величины, ни проверки переполнения, ни jo нигде в цикле. Девятая цифра не вызывает ошибку; она сдвигает старшие разряды числа за пределы регистра, и никто этого не замечает. 0x100000005 & 0xffffffff = 5, и парсер этим полностью удовлетворён.

Единственная проверка отбраковывает не ту половину

Ниже по потоку есть ровно одна проверка, и она ошибочна почти поэтическим образом. Драйвер дечанкинга (sub_20af6b0, места вызова 0x20af719 / 0x20af9fb, размер хранится в [r12+0x344]) применяет проверку знака к накопленному значению. Задумайтесь на секунду: ловятся только те переполнения, которые попадают в отрицательную половину, [0x80000000, 0xffffffff]. Переполненный размер, младшие 32 бита которого оказываются ниже 0x80000000, проходит как совершенно нормальный размер чанка. 0x100000005, переполняющийся в 5, — канонический случай: переполнен, не замечен и разрушителен.


Почему это контрабандой протаскивается

Пакет превращается в контрабанду из-за одного проектного решения в том, как NetScaler обращается с телами: он пересылает чанковую разметку клиента бэкенду байт в байт, нетронутой, при этом используя собственное (переполненное) понимание этой разметки, чтобы решить, где заканчивается запрос клиента.

Таким образом, NetScaler завершает запрос #1 после пяти байт тела и ставит оставшиеся 37 в очередь как второй, конвейеризованный запрос. Бэкенд, выполняя арифметику правильно, читает 100000005 как 4 294 967 301 и продолжает ждать тело для запроса #1. С этого момента оба устройства читают один и тот же поток и живут в разных мирах.

В каком мире вы окажетесь, зависит от бэкенда.

Поток A: корректный 64-битный бэкенд. Клин рассинхронизации.

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

Поток B: бэкенд, который усекает до 32 бит, как NetScaler. Завершённый smuggle.

Скачать инструмент