
Reproductor funcional de prueba de concepto para CVE-2026-95629, una vulnerabilidad de path traversal en Apache Camel camel-docling donde el encabezado output-path llega literalmente al parámetro --output de docling.
--output tal cualReproductor de prueba de concepto ejecutable para la vulnerabilidad de Apache Camel en la que DoclingProducer, al ejecutar
la herramienta externa docling en modo CLI (el predeterminado), entregaba el valor del
encabezado CamelDoclingOutputFilePath a la opción --output de docling tal cual: sin normalizar, una ruta
absoluta o una ruta que contiene segmentos .. se aceptaba tal como se recibía, y ninguna opción de endpoint podía restringirla.
| Entorno de ejecución | Directorio | Stack |
|---|---|---|
| Camel Spring Boot | camel-spring-boot/ | Spring Boot 3.5.13 + camel-docling 4.22.1 |
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.39.0 (Quarkus 3.39.0, Camel 4.22.0) |
Docling escribe el resultado de la conversión —Markdown, HTML, JSON o texto— en ese directorio, nombrando el archivo igual que el archivo de entrada, y el productor después elimina únicamente su propio directorio temporal, por lo que el archivo queda en su sitio. Un actor capaz de establecer el encabezado podría, por tanto, hacer que se creara la salida de la conversión en cualquier directorio en el que el proceso de Camel pueda escribir.
cd camel-spring-boot # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
until [ "$(curl -s http://localhost:8080/exploit/ready)" = "Started" ]; do sleep 1; done
curl -s http://localhost:8080/exploit/attack
docker compose down
En una compilación afectada (idéntico en ambos runtimes):
1) No header at all - the producer picks its own temporary directory
argv docling received: --to md --ocr-lang en --output /tmp/docling-<uuid> /tmp/docling-<uuid>/docling-<n>.tmp
exchange outcome: ok
2) An ABSOLUTE path of the attacker's choosing
header CamelDoclingOutputFilePath = /tmp/cve-2026-95629-attacker-chosen
argv docling received: ... --output /tmp/cve-2026-95629-attacker-chosen ...
3) A path with .. segments - watch whether they survive into the argv below
header CamelDoclingOutputFilePath = /tmp/cve-2026-95629-attacker-chosen/../cve-2026-95629-escaped
argv docling received: ... --output /tmp/cve-2026-95629-attacker-chosen/../cve-2026-95629-escaped ...
4) CONTROL - asking for the same thing through CamelDoclingCustomArguments instead
REJECTED by Camel: IllegalArgumentException: Custom argument '--output' is not allowed because the
output directory is managed by the producer. Use the CamelDoclingOutputFilePath header or endpoint
configuration instead.
Files now sitting in directories the route never chose:
/tmp/cve-2026-95629-attacker-chosen/document.md : true
/tmp/cve-2026-95629-escaped/document.md : true
>>> PROVEN: ...
La sonda 4 es la forma del problema. La validación de argumentos personalizados añadida por la corrección para
CVE-2026-40047 rechaza --output y -o en
CamelDoclingCustomArguments y dice a quienes la invocan que elijan el directorio de salida con esta cabecera en su lugar —
así que el único valor que decide dónde escribe docling era el único valor que llegaba a la línea de comandos sin ninguna de
las comprobaciones aplicadas a los argumentos personalizados.
El intercambio en sí falla tras las sondas 2 y 3 ("falló después"), porque el productor entonces busca la salida convertida en su propio directorio temporal y no la encuentra allí. Eso es cosmético: la escritura ya ha ocurrido, que es lo que muestra el listado de archivos al final.
No se necesita una instalación de docling. La opción doclingCommand del endpoint apunta a un sustituto que registra las llamadas a la CLI, el cual tanto registra el vector de argumentos que recibió como escribe un archivo en el directorio que --output haya nombrado, tal como lo hace docling. Se ejecutan el DoclingProducer real y el addOutputDirectoryArguments() real.
Solo el modo CLI usa esta cabecera; el modo docling-serve no.
CamelDoclingOutputFilePath pertenece al espacio de nombres de cabeceras de Camel, que los consumidores que aplican el filtrado de cabeceras predeterminado de Camel —los consumidores JMS y Kafka, por ejemplo— eliminan de los mensajes entrantes. El problema se alcanza cuando una ruta reenvía valores influidos externamente a la cabecera, o cuando un consumidor mapea cabeceras entrantes sin ese filtrado.
El contenido del archivo escrito es la conversión del documento transportado por el mensaje, por lo que está moldeado por quien suministra ese documento sin ser escrito byte a byte; cuando el documento llega en el cuerpo del mensaje, el productor lo almacena bajo un nombre temporal aleatorio, que el archivo de salida lleva entonces.
| Propiedad | Valor |
|---|---|
| Componente | camel-docling (DoclingProducer.addOutputDirectoryArguments()) |
| CWE | CWE-22 (Limitación incorrecta de un nombre de ruta a un directorio restringido) |
| Gravedad | Media |
| Vector de ataque | Un directorio absoluto o con .. en la cabecera CamelDoclingOutputFilePath |
| Impacto | Salida de conversión creada en cualquier directorio en el que el proceso de Camel pueda escribir |
| Versiones afectadas | Desde 4.15.0 anterior a 4.18.5, desde 4.19.0 anterior a 4.22.2 |
| Versiones corregidas | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-24894 |
| Crédito | Zhixi "Jace" Sun, investigador de seguridad independiente |
Aviso: https://camel.apache.org/security/CVE-2026-95629.html
Problema hermano en el mismo componente, misma oleada de publicación: CVE-2026-95628 (rutas absolutas aceptadas como valores de argumentos personalizados).
La cabecera ahora recibe la misma normalización y la misma contención opcional en un directorio base que las rutas de entrada, mediante
resolveWithinOutputBaseDirectory(). Pero la opción de endpoint outputBaseDirectory que añade la corrección está
sin establecer de forma predeterminada, y su propia documentación dice que, cuando está vacía, no se aplica ninguna restricción de directorio
y el valor de la cabecera solo se normaliza.
Verificado con 4.22.1 → 4.22.2, con outputBaseDirectory sin establecer:
3) A path with .. segments - watch whether they survive into the argv below
argv docling received: ... --output /tmp/cve-2026-95629-escaped ... <- normalized
Files now sitting in directories the route never chose:
/tmp/cve-2026-95629-attacker-chosen/document.md : true <- still written
/tmp/cve-2026-95629-escaped/document.md : true
>>> PARTLY CLOSED: the header was normalized before reaching --output ... It can still name any directory
>>> the Camel process can write to, because the outputBaseDirectory option the fix added is UNSET by
>>> default ... Set outputBaseDirectory on every docling endpoint whose CamelDoclingOutputFilePath header
>>> can carry a value influenced from outside the deployment; upgrading alone is not enough.
El driver reporta ese estado por separado en lugar de llamarlo corregido, porque es el estado en el que
la mayoría de los despliegues quedarán justo después de una actualización. Después de actualizar, configure outputBaseDirectory en cada
endpoint de docling cuya cabecera pueda transportar un valor influenciado externamente.