
Reproductor de prueba de concepto ejecutable para CVE-2026-88789, que demuestra XXE y SSRF en Apache Camel Quarkus camel-quarkus-support-xalan a través del XSLT TransformerFactory.
TransformerFactory de Xalan forzado elimina las restricciones de acceso externo de JAXPReproductor de prueba de concepto ejecutable para la vulnerabilidad de Apache Camel Quarkus en la que la extensión de soporte XSLT
(camel-quarkus-support-xalan) proporciona su propio TransformerFactory respaldado por Xalan al componente xslt
y lo registra como el predeterminado de JAXP. Xalan-J 2.7.x es anterior a JAXP 1.5 y no puede respetar
javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD ni ACCESS_EXTERNAL_STYLESHEET — setAttribute() lanza
IllegalArgumentException para ambos — por lo que las restricciones de acceso externo que Apache Camel aplica al
TransformerFactory que crea nunca estuvieron en vigor.
| Runtime | Directorio | Stack |
|---|---|---|
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.36.0 (Quarkus 3.36.0, Camel 4.20.0) |
Solo Camel Quarkus. El código vulnerable es una extensión de Camel Quarkus, no un componente de Camel. Camel simple y Camel Spring Boot usan el
TransformerFactorydel JDK, que respeta ambos atributos, por lo que no hay nada que reproducir allí — este repositorio por lo tanto no tiene una variantecamel-spring-boot/.
Un atacante que proporciona el documento XML que se está transformando puede leer archivos locales o alcanzar ubicaciones de red internas a través de una declaración de entidad externa en ese documento.
cd camel-quarkus
mvn clean package
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
Salida esperada en una compilación afectada (abreviada — el controlador ejecuta seis pruebas, véase
camel-quarkus/README.md):
1) xslt endpoint, body is a StreamSource, external entity -> file:///tmp/cve-2026-88789-secrets/db-password.txt
transformation result: [db.password=LOCAL-FILE-s3cr3t-99]
local file contents in the output: true
2) xslt endpoint, body is a StreamSource, external entity -> http://127.0.0.1:8080/internal/secret
transformation result: [INTERNAL-SECRET-s3cr3t-42]
internal endpoint response in the output: true
4) CONTROL - same document as a String body (Camel converts it to a SAXSource itself)
transformation result: []
local file contents in the output: false
5) TransformerFactory.newInstance() anywhere in the application
factory: org.apache.camel.quarkus.support.xalan.XalanTransformerFactory
setAttribute(ACCESS_EXTERNAL_DTD, ""): REFUSED, IllegalArgumentException: ...
identity transform of the same document: [... <data>db.password=LOCAL-FILE-s3cr3t-99</data> ...]
Requests the XML parser made to internal endpoints on its own: [GET /internal/secret, GET /internal/leak.dtd]
>>> PROVEN: ...
Verificado también contra Camel Quarkus 3.40.0: cada prueba que filtra información se silencia, los endpoints
internos no reciben ninguna solicitud, y el controlador imprime NOT reproduced.
En la ruta del componente xslt, solo los cuerpos que llegan al transformador ya como un
javax.xml.transform.Source se ven afectados. Los cuerpos de otros tipos — String, byte[], InputStream — son
convertidos por Apache Camel a un SAXSource con entidades externas y carga de DTD externos deshabilitados, y no
se ven afectados. La prueba 4 en el reproductor es esa ruta segura, junto a la insegura.
Debido a que la fábrica también está registrada como el predeterminado de JAXP (la extensión de soporte incluye
META-INF/services/javax.xml.transform.TransformerFactory), cualquier otro código en la aplicación que obtenga
una fábrica a través de TransformerFactory.newInstance() pierde las mismas restricciones, sin un error. Es por
eso que el aviso lista extensiones que nunca transforman nada por sí mismas:
| Extensión | Exposición |
|---|---|
camel-quarkus-xslt | la ruta del componente xslt y el predeterminado de JAXP |
camel-quarkus-xslt-saxon | el predeterminado de JAXP |
camel-quarkus-tika | el predeterminado de JAXP |
camel-quarkus-xmlsecurity | el predeterminado de JAXP |
| Propiedad | Valor |
|---|---|
| Componente | camel-quarkus-support-xalan (extensión de soporte XSLT) |
| CWE | CWE-611 (Restricción inadecuada de la referencia a entidad externa XML) |
| Severidad | Alta |
| Vector de ataque | Una entidad externa o DTD externo declarado en el documento XML que se está transformando, donde el cuerpo llega al endpoint xslt ya como un javax.xml.transform.Source |
| Impacto | Leer archivos locales; emitir solicitudes a ubicaciones de red internas (SSRF) |
| Versiones afectadas | Desde 3.2.0 anterior a 3.33.3, desde 3.34.0 anterior a 3.40.0 |
| Versiones corregidas | 3.33.3 (rama LTS), 3.40.0 |
| Issue de GitHub | apache/camel-quarkus#9115 |
| Crédito | Descubierto mediante análisis interno, usando Claude Security Tool |
Aviso: https://camel.apache.org/security/CVE-2026-88789.html
XalanTransformerFactory ahora aplica las restricciones por sí mismo en lugar de depender de atributos que Xalan no puede
respetar:
XMLReader que no resuelve ni entidades generales externas ni
entidades de parámetro externas y no carga DTDs externos — la misma configuración que usa
XmlConverter.createSAXParserFactory() de Apache Camel para los cuerpos que camel-xslt convierte a un SAXSource por sí mismo.
Un SAXSource que lleva un XMLReader configurado por el llamador se usa tal cual, y DOMSource y StAXSource
ya están analizados.document() se deniegan a menos que el propio
URIResolver de la aplicación los resuelva, y la restricción se instala en cada punto de entrada que entrega algo
con lo que transformar, incluidos los puntos de entrada push de SAX cuyos transformadores Xalan no copia el resolver de la fábrica
sobre ellos. Las aplicaciones que establecen su propio resolver — camel-xslt lo hace en cada exchange — siguen
sobrescribiéndolo como antes.Corregido en main en
9a570b64 y
9dd11779,
retroportado a 3.33.x en
ad9c5236 y
3d886769.
javax.xml.transform.Source construido a partir de entrada no confiable a un endpoint xslt. Deja el
cuerpo del mensaje como String, byte[] o InputStream para que Apache Camel lo convierta primero a un SAXSource con
entidades externas deshabilitadas.convertBodyTo sobre un cuerpo Source existente no es una solución alternativa: esa conversión realiza una transformación
de identidad a través de la misma fábrica. La prueba 5 en el reproductor es esa transformación de identidad, y filtra información.com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl,
en lugar de depender de TransformerFactory.newInstance(). La prueba 6 es ese control, y deniega la lectura.