PHP serialization and deserialization

preface

Concept:
In fact, this is to solve a problem of PHP object transfer, because PHP files will destroy the object after execution. If there is a page that happens to use the object just destroyed next time, there will be nothing to do. You can't always let it destroy. Wait for you, so people came up with a method that can save the object for a long time, which is PHP serialization, Then when we need to use it next time, just deserialize it
The purpose of serialization is to facilitate data transmission and storage json is for the convenience of transferring data.

1, Serialization and deserialization

Serialization:
Functions: serialize()
Compress complex data types into a string. The data types can be arrays, strings, objects, etc
Serializing an object will save all variables of the object, but will not save the method of the object, but only the name of the class.
Deserialization:
Functions: serialize()
Restore the original serialized variable

2, Magic function

__construct   Called when an object is created,
__destruct   Called when an object is destroyed,
__toString   When an object is called as a string.
__wakeup()   use unserialize Time trigger
__sleep()    use serialize Time trigger
__destruct()    Triggered when an 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()    Called on an inaccessible property isset()or empty()trigger
__unset()     Use on inaccessible properties unset()Time trigger
__toString()    Triggered when a class is used as a string,The return value needs to be a string
__invoke()   Triggered when a script attempts to call an object as a function

1. Serialization

class people{

    public $name = 'sam';  

    private $sex = 'man';  

    protected $age = '20';

}

$people1 = new people();

$object = serialize($people);

print_r($object);

Define a people class with public attribute name, private attribute sex and protected attribute age. Then instantiate a people1 and serialize it. The final output is:
O:6:"people":3:{s:4:"name";s:3:"sam";s:11:" people sex";s:3:"man";s:6:" * age";s:2:"20";}

2. Deserialization

<?php
class people{

    public $name = 'sam';

    private $sex = 'man';

    protected $age = '20';

}

$people1 = new people();

$object = serialize($people1);
$a=unserialize($object);
#print_r($object);
var_dump($a);

Output is:
object(people)#2 (3) { ["name"]=> string(3) "sam" ["sex":"people":private]=> string(3) "man" ["age":protected]=> string(2) "20" }
It should be noted here that there is no need for other special operations for the public attribute, but for the private attribute, you need to add a space before and after the description, or bring the class name where it is located, and "*" for the protected attribute.

3. Call of several magic functions

1. __wakeup() when deserialize() function, the function will be called to initialize before the data stream is deserialized
2. __destruct() is triggered when the object is destroyed, that is, as long as you deserialize or instantiate an object, the function will be triggered when your call is over.

<?php
class star{
    public $a;
    function __wakeup(){
        echo "hi";
    }
    function __destruct(){
        echo "It's over";
    }
}
$s='O:4:"star":1:{s:1:"a";N;}';
unserialize($s);

Result: hi is over
As you can see, hi is output first, indicating__ The wakeup() function is called first, and then the whole deserialization is completed. The object is destroyed and triggered__ destruct() function, the output is over.
3. __toString() is triggered when an object is used as a string.

<?php
class star{
    public $a;
    function __wakeup(){
        echo $this->a;
    }
}
class next{
    function __toString(){
        echo "I'm here";
    }
}
$t='O:4:"star":1:{s:1:"a";O:4:"next":0:{}}';
unserialize($t);

Result: I'm here. Catchable fatal error: Method next::__toString() must return a string value in /tmp/41bac5636b55eff5c8abea138d605489916c2612abc45fd39fdaa87a827a0e00/main.php on line 5
There is no retrun here, nor does it ignore the error message. There is an error message. It doesn't matter, but what I want to say is__ toString() needs to return again, otherwise an error will be reported. The results show that when the class star

echo $this->a;

During execution, a is treated as a string. At this time, I set a as class next. At this time, class next is called as a string, so the trigger in class next is triggered__ toString() function, output "I'm here".
4. __invoke() is triggered when the class is called as a function. See the example.

<?php
class star{
    public $a;
    function __wakeup(){
        $function=$this->a;
        return $function();
    }
}
class next{
    function __invoke(){
        echo "I'm here";
    }
}
$t='O:4:"star":1:{s:1:"a";O:4:"next":0:{}}';
unserialize($t);

Result: I'm here
The analysis process is the same as that of the above function. It assigns a value through deserialization, but it is assigned to other classes instead of strings, and then

return $function();

When the class is called as a function, it is triggered__ The invoke() function outputs "I'm here".
5. __ The get () function is triggered when accessing inaccessible properties. There are two types of inaccessible properties

  • Private attribute or protected attribute, which will be triggered when the access is restricted__ get()
  • When the attribute does not exist, it will also be triggered__ get()
<?php
class star{
    public $a;
    function __wakeup(){
        return $this->str['str']->source;
    }
}
class next{
    function __get($name){
        echo "I'm here";
        return;
    }
}
$t='O:4:"star":2:{s:1:"a";N;s:3:"str";a:1:{s:3:"str";O:4:"next":0:{}}}';
unserialize($t);

Result: I'm here
Assign str ['str'] to class next to access the source of next, but the attribute source does not exist in class next, so it is triggered__ get() function, access protection properties, etc.
Summary: in the process of deserialization, we can achieve the operations we want through these magic functions, especially the following three functions. You will find that these three functions can achieve the continuous use of multiple classes, so as to achieve the effect of chain, that is, the compilation of pop chain in deserialization. For specific knowledge of pop chain, see the following:
Construction of POP chain
Next, let's talk about the vulnerability of deserialization

3, Deserialization vulnerability

1.__wakeup() bypass

(CVE-2016-7124)
During deserialization, if the value representing the number of object attributes is greater than the actual number of attributes, it will be skipped__ Execution of wakeup().
Affected version:
PHP before 5.6.25
7.x before 7.0.10

<?php
class star{
    public $a;
    function __wakeup(){
        echo $this->a;
    }
}
$t='O:4:"star":1:{s:1:"a";s:9:"I'm here";}';
unserialize($t);

Result: I'm here

O:4:"star":2:{s:1:"a";s:9:"I'm here";}

Result: no output
The results show that when the number of representation attributes is greater than the real number__ The wakeup() function is not executed and is bypassed. Usually in the topic__ Wake () contains many restrictions, which can be bypassed through this vulnerability__ wake() can bypass the restriction.

2.POP chain

Construction of POP chain

summary

1. Construct deserialization chain
2. Magic function
3.pop chain
4.__wakeup() bypass

Keywords: CTF

Added by mattchewone on Mon, 31 Jan 2022 03:04:35 +0200