Exploiting PHP Deserialization: CCCamp19 CTF PDFCreator Challenge

ctf exploitation deserialization

Deserialization is a vulnerability class that’s often overlooked. It’s great that this year’s CCCamp CTF included an interesting web based challenge that is based on this vulnerability class.

The Target

The challenge includes a link to a web service that allows converting user-supplied images into PDF files. Users can upload image files, add some additional HTML content in a textbox and render the whole thing into a PDF file:


A ZIP file with the source code of the web application is also available.

The following things are noteworthy:

I decided to check the TCPDF libary for public vulnerabilities. I came across a RCE vulnerability for versions <= 6.2.19 that’s based on insecure phar deserialization. The exploit also referenced the slides of a talk at BlackHat 2018 that explains phar based exploits and also contains a case study of the TCPDF library.

Exploiting PHP Phar Deserialization

Because I couldn’t explain phar deserialization exploits any better, let’s quote Daniel Timofte:

Similar to ROP (return-oriented programming) attacks on compiled binaries, this type of exploitaton is carried through PHP object injection (POI), a form of property-oriented programming (POP) in the context of object-oriented PHP code.

Cool. What?

A phar file allows merging whole PHP applications into a single compressed file. Therefore this can also contain serialized PHP objects. The PHP phar:// stream wrapper can be used to work with these files and cause the phar file to be read and the stored objects to be instantiated. Various magic PHP functions are called implicitly while this whole thing takes place, such as the object destructor called __destruct().

In order to use this for exploitation, you need these things:

An example of such a dangerous thing can be executing system() on an object attribute value. An attacker could therefore upload a malicious phar file that contains a malicious shell command as a value of one of the string attributes. After causing the phar to be deserialized, the destructor is called which in turn executes system() with the attacker-supplied command as argument.

Insecure phar:// Handling in TCPDF

The essence of the TCPDF vulnerability is that user-supplied <img> tags are handled in a way that allows an attacker to reach a call to file_exists in the vulnerable library. There are various file system functions that cause phars to be deserialized, and of course this one is among them. This can be exploited using an attacker-controlled parameter that references a file path with the phar:// handler. Consider the following example in the context of TCPDF:

<img src="phar://upload/hax.phar">

Upon entering this line in the web applications HTML input textbox, the following things happen:

It seems that the vulnerable functions mentioned in the talk are also present in the 6.2.12 version of TCPDF that the challenge is based on. This seems like a good strategy to solve this challenge.

Ok cool but how to exploit this?

Crafting A Malicious Phar

The first thing that’s required for this to work is a fitting gadget. There’s a nice toolkit for this that’s called phpggc. Unfortunately, the gadgets included in this tool can’t be used to exploit the target but it serves as a good example of how do pull this attack off.

Finding A Gadget

After doing some research it became clear that TCPDF doesn’t contain a fitting gadget, so I decided to check out the web applications itself and not just the vulnerable library. After some really thorough analysis (really 1337 grep), the contents creator.php file were found to be suspicious. It contains a class called PDFCreator that also has its own destructor:

function __destruct()
    if (file_exists($this->tmpfile))
    $info = pathinfo($this->tmpfile);
    if ($info['extension'] == "pdf")
        echo "Could not delete created PDF: Not a pdf. Check the file: " . file_get_contents($this->tmpfile);

We can use this to gadget read arbitrary files on the remote server. When destructing an object with a tmpfile value without pdf extension, the tmpfile is being shown to the user with echo. This can be used to get the contents of flag.php :)

Please note that gadgets of this kind can also exist in any third party library that a web application is using internally.

Constructing The Phar

First it’s required to create a php.ini file with this content:

phar.readonly = 0

By default this setting is set to 1 which prevents creating phar files.

Consider this PHP listing that can be used to create a phar that can be deserialized by the target web application:

namespace PDFStuff {

  class PDFCreator { public $tmpfile = "/etc/passwd"; }

  $phar = new \Phar("test.phar");
  $phar->setStub("<?php __HALT_COMPILER();");

  # Setting the metadata serializes the payload object
  $payload = new PDFCreator();
  # Here’s where the object is serialized and added to the Phar

  # Add a dummy file to respect the Phar specifications
  $phar->addFromString("test.txt", "test");

This is a modified version of the phar creator that can be found in the second part of Daniel Timofte’s great blog post.

PHP saves objects in phar files according to their namespaces and class names. This means that in order to allow correct deserialization on the target server, the original namespace and class names have to be used for the malicious serialized object. This is why the whole code is wrapped in the correct namespace, as determined using the provided source code.

Another interesting thing is that we only need to provide the correct names and attributes for the deserialization to work on the other end. No unnecessary other attributes or imports are required.


php -c php.ini creator.php

yields a test.phar that contains the crafted object. After commenting out the file type and exif checks on a local instance of the target web application, this has been found to work perfectly.

Making A Phar-JPEG Polyglot

The only missing thing is that the created phar is in fact not a valid picture file. Therefore this file will be rejected by the target server and can’t be uploaded this way.

The talk mentioned before also contains information on Phar-JPEG polyglot files. These are files that seem to be images but in fact they embed valid phar file inside. This can be built manually or using this great tool. I’ve used the latter with some slight modifications to make it work with the custom gadget. Of course, $tmpfile has been set to './flag.php' to get the flag.

Getting The Flag

After uploading the flag and modifying the HML to include

<img src="phar://upload/<filename>.jpg">

the flag can be found in the HTML source code:

$flag = "ALLES{phar_jpeg_polyglot_madness_such_w0w}"

Peace out, thanks to ALLES CTF and 0x4d5a for this challenge :)

SROP Exploitation with radare2

r2 radare2 rop exploitation ctf

MemLabs: An Introduction To Memory Forensics

forensics ctf volatility

Fuzzing A GameBoy Emulator With AFL++

fuzzing reversing exploitation