HTB Write-Up: Reactor
| Platform | Hack The Box |
| Difficulty | Easy |
| OS | Linux |
| Author | shreekara |
| Date | May 24, 2026 |
| Source | github.com/ShreekaraKedlaya/Hackthebox-writeups |
Table of Contents
1. Reconnaissance 2. Enumeration — ReactorWatch Dashboard & Version Fingerprinting 3. Initial Access — CVE-2025-55182 (React2Shell RCE) 4. Lateral Movement — SQLite Credential Extraction & Hash Cracking 5. Privilege Escalation — Node.js Debug Port Hijacking 6. Summary & Takeaways
1. Reconnaissance
Starting with a full port scan to get an idea of what's exposed:
Only two ports came back:
| Port | Service | Details |
| 22 | SSH | OpenSSH 9.6p1 (Ubuntu) |
| 3000 | HTTP | Next.js — ReactorWatch application |
Pretty minimal attack surface. SSH is usually a dead end without creds, so port 3000 is where the focus needs to be.
2. Enumeration — ReactorWatch Dashboard & Version Fingerprinting
2.1 — Web Application Fingerprinting
Ran WhatWeb first to fingerprint the stack quickly:
UncommonHeaders[x-nextjs-cache,x-nextjs-prerender,x-nextjs-stale-time], X-Powered-By[Next.js]
The app is called ReactorWatch — a nuclear reactor core monitoring dashboard. More importantly, the X-Powered-By header and the Next.js-specific response headers confirm it's running Next.js 15.0.3. That version number is going to matter.
2.2 — Unauthenticated Dashboard
Navigating to http://10.129.6.0:3000 — no login, no auth, nothing. The dashboard just loads and immediately starts leaking information.
Reactor telemetry (live, unauthenticated):
| Core Status | Reactor Power 98.2%, Criticality 1.0002 (WARNING) |
| Core Temp | 324°C |
| Pressure | 155 bar |
| Coolant Flow | 18.4k m³/h (CAUTION) |
| Turbine Output | 1.21 GW |
On-site personnel — also just... listed there:
| Name | Role | Status |
| Dr. Elena Rodriguez | Lead Nuclear Engineer | ONLINE |
| Marcus Kim | Senior Technician | ONLINE |
| James Thompson | Safety Officer | OFFLINE |
Even if there was no direct exploit here, this is terrible from a security standpoint. Personnel names, roles, and live operational data — all public. Good recon material either way.
2.3 — Directory Enumeration
Threw Feroxbuster at it to find any hidden paths:
One oddity came up:
A /cgi-bin/ on a Next.js app doesn't make much sense — probably a red herring or misconfiguration. Noted it and moved on, since the more interesting angle was the framework version itself.
2.4 — CVE Research
A quick search on Next.js 15.0.3 turns up CVE-2025-55182, sometimes called React2Shell. It's a critical unauthenticated RCE via prototype pollution and unsafe deserialization in the React Server Components handler. The attack vector is injecting a malicious payload through the Next-Action header. No auth needed, which makes this very clean.
3. Initial Access — CVE-2025-55182 (React2Shell RCE)
3.1 — How the Vulnerability Works
The RSC handler in Next.js 15.0.3 doesn't properly sanitize the Next-Action header. By crafting a payload that exploits prototype pollution combined with unsafe deserialization, an attacker can get arbitrary OS command execution on the server. The service doesn't need to be authenticated — the vulnerable endpoint is reachable by anyone.
3.2 — Confirming RCE
Used a public Python PoC to verify execution before going for a shell:
node
/opt/reactor-app
Running as node (uid=999) — low-privileged, but enough to start moving.
3.3 — Getting a Shell
Base64-encoded the reverse shell to avoid issues with quoting and special characters in the command argument:
Set up the listener:
Fired the payload:
Shell landed:
node@reactor:/opt/reactor-app$
4. Lateral Movement — SQLite Credential Extraction & Hash Cracking
4.1 — Poking Around the App Directory
First thing to do after getting a shell — enumerate the application files:
Two files immediately stood out:
| File | Notes |
| .env | Application environment configuration |
| reactor.db | SQLite database — readable by the node user |
An SQLite database sitting right in the app directory, readable by the current user. That's almost always going to have something useful.
4.2 — Dumping the Database
sqlite> SELECT * FROM users;
1|admin|a203b22191d744a4e70ada5c101b17b8|administrator|admin@reactor.htb
2|engineer|39d97110eafe2a9a68639812cd271e8e|operator|engineer@reactor.htb
Two accounts with what look like MD5 hashes. MD5 is weak, so these should crack fast.
4.3 — Cracking the Hashes
Focused on the engineer hash since that account is more likely to have SSH access:
1g 0:00:00:00 DONE 50.00g/s 16857Kp/s
Cracked instantly. Credentials: engineer:reactor1
4.4 — SSH as Engineer
User flag is at /home/engineer/user.txt.
5. Privilege Escalation — Node.js Debug Port Hijacking
5.1 — Checking Internal Services
With a proper shell as engineer, time to look for anything listening internally that wasn't visible from outside:
Port 9229 on localhost — that's the standard Node.js V8 Inspector port. When a Node.js process is started with --inspect, it opens this port and allows anyone who can reach it to attach a debugger and run arbitrary JavaScript. If the process running it is privileged, that's a direct path to root.
5.2 — Identifying the Process
It's a Node.js uptime monitoring worker that pings the ReactorWatch app every 30 seconds. Crucially — it's running as root and was started with --inspect, which is why 9229 is open. Classic misconfiguration: a maintenance script that no one thought twice about running as root.
5.3 — Forwarding the Debug Port
The port is only accessible from localhost on the target, so an SSH tunnel gets it to the attacking machine:
Now 127.0.0.1:9229 locally maps directly to the Node.js debugger on the target.
5.4 — Connecting to the Debugger
debug>
5.5 — RCE as Root
require doesn't work directly in this context, but process.mainModule.require does — it reaches the module system through the already-loaded main module:
Running as root. From here, a root shell gets sent back the same way, and a listener catches it:
root@reactor:~#
Root flag is at /root/root.txt.
6. Summary & Takeaways
Attack Chain
| Phase | Technique | Result |
| Recon | nmap -A | Ports 22, 3000 identified |
| Fingerprinting | WhatWeb + headers | Next.js 15.0.3 → CVE-2025-55182 |
| Initial Access | React2Shell — Next-Action header injection | Shell as node |
| Credential Extraction | SQLite DB dump → MD5 hashes | engineer:reactor1 |
| Hash Cracking | John the Ripper + rockyou.txt | Password cracked instantly |
| Lateral Movement | SSH with cracked credentials | Shell as engineer + user flag |
| Privilege Escalation | Node --inspect → SSH tunnel → mainModule.require | Root shell + root flag |
What I Took Away From This Box
The thing that stuck with me here is how much of the chain came from stuff that wasn't technically a vulnerability on its own. An unauthenticated dashboard handing out personnel names and stack details isn't an exploit, it's just bad defaults — but it's exactly what told me where to look for the real CVE. Same with the X-Powered-By header: nobody strips that on a dev box, and it saved me a lot of guessing on version fingerprinting.
The SQLite file sitting readable in the app directory was the part that annoyed me most, honestly, because it's such a common mistake. If your DB lives inside the web root with world-readable perms, you've basically pre-cracked your own creds for whoever gets a foothold.
Root cause of the privesc was a maintenance script running with --inspect as root. I hadn't actually exploited a Node debug port before this box, so getting process.mainModule.require to hand me code execution was the highlight for me. Once I had that, the rest was just tunneling and catching a shell — but the debug port thing is going straight into my privesc checklist for future boxes.