Przejdź do treści
INDEKS ANALIZ BREACHROAD / NOTA TECHNICZNA

Netty OHTTP: 17 bajtów może zatrzymać bramę, a log ujawnić klucz HPKE

Sześć podatności codec-ohttp i Binary HTTP obejmuje pętle parsera, OOM, wyciek off-heap, overflow oraz logowanie prywatnego klucza HPKE.

MATERIAŁ PUBLICZNY
AUTOR
/ CEO Breachroad · OSCP · PNPT
PUBLIKACJA
20 sierpnia 2026
CZAS CZYTANIA
20 min czytania
TEMAT
Podatności i CVE
Netty OHTTP: 17 bajtów może zatrzymać bramę, a log ujawnić klucz HPKE

20 sierpnia baza GitHub zaktualizowała zestaw przejrzanych advisory dla netty-incubator-codec-ohttp. Poprawki w 0.0.23.Final zamykają kilka niezależnych problemów w obsłudze Oblivious HTTP i binarnej reprezentacji HTTP z RFC 9292. Najbardziej obrazowy błąd pozwala około 17-bajtowej wiadomości zająć jeden event-loop thread na 100% CPU bez uwierzytelnienia. Inne mogą prowadzić do nieograniczonego buforowania, wycieku pamięci direct, wyjątku po integer overflow oraz zapisania prywatnego klucza HPKE w logu.

To dobry przypadek pokazujący, że kryptografia nie naprawia błędów parsera. OHTTP szyfruje żądanie do bramy przy użyciu publicznej konfiguracji HPKE. Atakujący może więc utworzyć poprawną kryptograficznie kopertę, której odszyfrowane wnętrze jest złośliwą ramką BHTTP. Brama musi traktować plaintext jako dane w pełni niezaufane.

CVE-2026-63202: pętla bez postępu

BinaryHttpParser#readFieldSection przetwarza sekcję pól. Pętla kończy się, gdy fieldSectionLength != 0 przestaje być prawdziwe, a gwarancje postępu zapisano jako instrukcje Java assert. W produkcyjnej JVM asercje są zwykle wyłączone. Jeżeli zadeklarowana długość jest mniejsza od faktycznie zużytych danych, licznik może stać się ujemny. Jeżeli readFieldLine() zwróci null bez przesunięcia bufora, kolejna iteracja widzi identyczny stan.

Advisory opisuje ramkę około 17 bajtów, która po umieszczeniu w legalnym żądaniu OHTTP przypina event loop na stałe. Netty ma ograniczoną liczbę wątków I/O, często zależną od liczby rdzeni. Kilka równoległych żądań może zająć całą grupę i zatrzymać przyjmowanie legalnego ruchu aż do restartu procesu.

Nie jest to atak przez duży request. Klasyczny limit rozmiaru HTTP go nie zatrzyma, bo obciążenie powstaje na małym buforze i w ciasnej pętli. Potrzebna jest poprawka parsera: jawne sprawdzenie postępu, odrzucenie overshoot i wyjątek kontrolowany zamiast polegania na assert.

CVE-2026-61827 i CVE-2026-63124: długość jako zobowiązanie

CVE-2026-61827 dotyczy braku limitów zakodowanych długości pól. Zdalny peer może deklarować wartości prowadzące do ciągłego buforowania i ostatecznie OutOfMemory. Limit całego żądania na reverse proxy może zmniejszyć ryzyko, ale parser powinien sam egzekwować maksymalną długość przed alokacją lub oczekiwaniem na kolejne dane.

CVE-2026-63124 jest drugą pętlą nieskończoną w obsłudze granicy sekcji pól o znanej długości. Operacyjnie efekt jest podobny: pojedynczy channel zajmuje wątek, a powtarzane żądania odbierają dostępność całej bramie. Dwa odrębne CVE w tej samej klasie pokazują, że poprawka punktowa nie wystarcza bez property-based tests dla wszystkich stanów parsera.

CVE-2026-61799: varint większy niż int

RFC 9292 korzysta ze zmiennych liczb całkowitych. Implementacja odczytywała kontrolowaną długość jako long, ale sumowała ją do int. W Javie operacja złożona może zawęzić wynik i spowodować przepełnienie. Wartość taka jak 2³¹ może zmienić dodatni offset na ujemny, ominąć prostą kontrolę i zakończyć się ArrayIndexOutOfBoundsException albo IndexOutOfBoundsException.

Język chroni pamięć procesu, więc nie jest to klasyczne memory corruption. Nadal jednak mała ramka może zamykać kanały i generować trwały DoS. Poprawne rozwiązanie wykorzystuje checked arithmetic, sprawdza zakres przed konwersją do int i zwraca kontrolowany CorruptedFrameException lub TooLongFrameException.

CVE-2026-54251: direct memory znika poza GC

OHTTP gateway alokuje pooled direct ByteBuf na odszyfrowany plaintext przed weryfikacją tagu AEAD. Jeśli tag jest błędny, metoda rzuca CryptoException, ale wcześniej zaalokowany bufor nie był zwalniany przez finally. Powtarzane niepoprawne ciphertexty prowadzą do wycieku natywnej pamięci off-heap.

Ten przypadek może być mylący w observability. Heap JVM wygląda względnie zdrowo, a proces rośnie w pamięci RSS lub zgłasza błędy direct buffer. Monitoring powinien rozdzielać heap, direct memory, liczbę błędów AEAD i tempo odrzucanych żądań. Restart przywraca usługę tylko chwilowo, jeśli endpoint pozostaje podatny.

CVE-2026-61798: prywatny klucz w toString()

Klasy integracji BoringSSL przechowywały bajty prywatnego klucza HPKE. BoringSSLAsymmetricCipherKeyPair.toString() dołączał obiekt private key, a jego toString() renderował pełną tablicę przez Arrays.toString(bytes). Osobna ścieżka błędu inicjalizacji także wstawiała prywatne bajty do IllegalArgumentException.

Samo istnienie metody nie oznacza, że każdy klucz wyciekł. Warunkiem jest zalogowanie obiektu lub wyjątku. W aplikacjach Java framework loggingowy często automatycznie wywołuje toString(), dlatego trzeba potraktować logi jako potencjalne miejsce ekspozycji. Rotacja klucza po aktualizacji może być konieczna, jeśli historia logów potwierdza zapis materiału. Należy także zbadać kopie, telemetry, systemy APM i archiwa, bo retencja klucza bywa dłuższa niż jego życie w procesie.

Zakres wersji i aktualizacja

Advisory wskazują komponenty BHTTP i HPKE w wersjach do 0.0.22.Final oraz codec-ohttp przed 0.0.23.Final. Wersją naprawioną jest 0.0.23.Final. W Mavenie trzeba sprawdzić nie tylko bezpośrednią deklarację, ale cały dependency tree, platform/BOM, shaded JAR oraz obrazy zawierające aplikację.

Aktualizacja biblioteki niskopoziomowej wymaga testów interoperacyjności. Sprawdź klientów i bramy, znane i nieokreślone długości ramek, fragmentację ByteBuf, błędy AEAD, retry, timeout i zachowanie przy niepoprawnej ramce. Oczekiwanym wynikiem negatywnym jest szybkie, kontrolowane odrzucenie pojedynczego żądania bez wzrostu CPU i pamięci.

Nie zalecamy odtwarzania opisanych ramek na produkcji. Wystarczającym dowodem operacyjnym jest wersja zależności, hash artefaktu, test regresji dostawcy i obserwacja, że zaktualizowana brama zachowuje stabilność pod bezpiecznym testem malformed corpus.

Mitygacje przed aktualizacją

Jeżeli zmiana wymaga okna, ogranicz dostęp do bramy, włącz rate limiting per źródło oraz budżet połączeń, krótki timeout i izolację procesu. Te kontrole mogą zmniejszyć liczbę równoległych ramek, ale nie rozwiązują sytuacji, w której jedno małe żądanie zajmuje wątek bez końca. Orkiestrator może restartować zawieszony pod, lecz atakujący może ponownie go wyczerpać.

Ustaw limity direct memory i alerty, aby awaria była kontrolowana, ale pamiętaj, że niski limit szybciej kończy proces. Usuń debug logging obiektów kryptograficznych, zablokuj treść wyjątków na poziomie pipeline i ogranicz dostęp do logów. To obrona warstwowa, a nie zamiennik 0.0.23.Final.

Hunting i telemetry

Szukaj event-loop threads stale w BinaryHttpParser.readFieldSection, długich okresów 100% jednego rdzenia, wzrostu oczekujących kanałów i braku postępu przy małym ruchu. Dla direct memory obserwuj usedDirectMemory, RSS, błędy alokacji oraz serię niepoprawnych tagów AEAD. Dla overflow interesujące są powtarzane bounds exceptions w decoderze.

Dla kluczy przeszukaj kontrolowane log stores po nazwach klas BoringSSL i komunikatach inicjalizacji, nie kopiując znalezionego materiału do nowych ticketów. Jeśli bajty klucza są widoczne, ogranicz dostęp do dowodu, ustal okres ekspozycji, rotuj parę, usuń starą konfigurację publiczną i potraktuj archiwa zgodnie z procedurą sekretów.

Fakty projektu i wnioski Breachroad

Mechanizmy sześciu CVE, zakres pakietów, oceny CVSS i wersja 0.0.23.Final pochodzą z advisories projektu Netty. Nie ma informacji o aktywnym wykorzystaniu. Zalecenia dotyczące rotacji, telemetry off-heap, staged rollout i testów negatywnych są analizą Breachroad zależną od architektury wdrożenia.

Szkolenie bezpiecznego tworzenia i utrzymania usług pomaga zespołom rozpoznawać takie granice parsera i sekretów. Testy penetracyjne web i API mogą zweryfikować odporność bramy na błędne ramki w kontrolowanym środowisku.

Źródła

UDOSTĘPNIJ / KOPIUJ