zoukankan      html  css  js  c++  java
  • 浅析PHP反序列化漏洞之PHP常见魔术方法(一)

      作为一个学习web安全的菜鸟,前段时间被人问到PHP反序列化相关的问题,以前的博客中是有这样一篇反序列化漏洞的利用文章的。但是好久过去了,好多的东西已经记得不是很清楚。所以这里尽可能写一篇详细点的文章来做一下记录。

      我们来参考这里:

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

    我们根据官方文档中的解释,一个一个来进行测试。

        __construct() 和 __destruct()

        __construct()被称为构造方法,也就是在创造一个对象时候,首先会去执行的一个方法。

    我写了这样的一个demo来做测试:

    class test {
    
        private $flag = '';
        public $filename = '';
        public $data = '';
    
        function __construct($filename, $data) {
            $this->filename = $filename;
            $this->data = $data;
            echo 'construct function in test class';
            echo "<br>";
        }
    }
    $a = new test('test.txt', 'data');

    测试结果:

    同样的,我们编写一个类的析构方法,__destruct()

    析构函数的作用:

    代码如下:

    class test {
    
        private $flag = '';
        public $filename = '';
        public $data = '';
    
        function __construct($filename, $data) {
            $this->filename = $filename;
            $this->data = $data;
            echo 'construct function in test class';
            echo "<br>";
        }
    
        function __destruct() {
            echo 'destruct function in test class';
            echo "<br>";
        }
    }
    
    $a = new test('test.txt', 'data');

    运行结果:

    __set()    __get()    __isset()    __unset()    作用如下:

    我们一样是来写一个代码进行验证:

    class test {
    
        private $flag = '';
        
        # 用于保存重载的数据 
        private $data = array();
    
        public $filename = '';
    
        public $content = '';
    
        function __construct($filename, $content) {
            $this->filename = $filename;
            $this->content = $content;
            echo 'construct function in test class';
            echo "<br>";
        }
    
        function __destruct() {
            echo 'destruct function in test class';
            echo "<br>";
        }
    
        function __set($key, $value) {
            echo 'set function in test class';
            echo "<br>";
            $this->data[$key] = $value;
        }
    
        function __get($key) {
            echo 'get function in test class';
            echo "<br>";
            if (array_key_exists($key, $this->data)) {
                return $this->data[$key];
            } else {
                return null;
            }
        }
    
        function __isset($key) {
            echo 'isset function in test class';
            echo "<br>";
            return isset($this->data[$key]);
        }
    
        function __unset($key) {
            echo 'unset function in test class';
            echo "<br>";
            unset($this->data[$key]);
        }
        
        public function set_flag($flag) {
            $this->flag = $flag;
        }
    
        public function get_flag() {
            return $this->flag;
        }
    }
    
    
    $a = new test('test.txt', 'data');
    
    # __set() 被调用
    $a->var = 1;
    
    # __get() 被调用
    echo $a->var;
    
    # __isset() 被调用
    var_dump(isset($a->var));
    
    # __unset() 被调用
    unset($a->var);
    
    var_dump(isset($a->var));
    
    echo "
    ";

    运行结果:

    我们可以看到调用的顺序为: 构造方法 => set方法(我们此时为类中并没有定义过的一个类属性进行赋值触发了set方法) => get方法 => isset方法 => unset方法 => isset方法 => 析构方法

    同时也可以发现,析构方法在所有的代码被执行结束之后进行的。

        __call()    __callStatic()    

    官方文档中的解释:

    类似以上介绍过的__set()和__get(),刚刚是访问不存在或者不可访问属性时候进行的调用。现在是访问不存在或者不可访问的方法时候:

    代码如下:

    class test {
    
        private $flag = '';
        
        # 用于保存重载的数据 
        private $data = array();
    
        public $filename = '';
    
        public $content = '';
    
        function __call($funcname, $args) {
            echo 'function name is: ' . $funcname. ' args is: ' . implode(', ', $args);
            echo "<br>";
        }
    
        public static function __callStatic($funcname, $args) {
            echo 'static function name is: ' . $funcname. ' args is: ' . implode(', ', $args);
            echo "<br>";
        }
        
        public function set_flag($flag) {
            $this->flag = $flag;
        }
    
        public function get_flag() {
            return $this->flag;
        }
    }
    
    $obj = new test;
    
    # 调用一个不存在或者无法访问到的方法时候将会调用__call()
    $obj->run('run args, test');
    
    # 调用一个不存在的静态方法,将会去调用__callStatic()
    $obj::run('static test');

    运行结果:

    看文档或者注释应该很明白了。

    接下来是对于反序列化漏洞利用最重要的一些方法了。

    __sleep()    __wakeup()    __toString()

    写个代码来进行验证:

    class test {
    
        private $flag = '';
        
        # 用于保存重载的数据 
        private $data = array();
    
        public $filename = '';
    
        public $content = '';
    
        function __construct($filename, $content) {
            $this->filename = $filename;
            $this->content = $content;
            echo 'construct function in test class';
            echo "<br>";
        }
    
        function __destruct() {
            echo 'destruct function in test class';
            echo "<br>";
        }
    
        # 反序列化时候触发
        function __wakeup() {
            // file_put_contents($this->filename, $this->data);
            echo 'wakeup function in test class';
            echo "<br>";
        }
    
        # 一般情况用在序列化操作时候,用于保留数据
        function __sleep() {
            echo 'sleep function in test class';
            echo "<br>";
            return array('flag', 'filename', 'data');
        }
    
        # 当需要输出得到对象名称时候会调用
        function __toString() {
            return $this->data;
        }
        
        public function set_flag($flag) {
            $this->flag = $flag;
        }
    
        public function get_flag() {
            return $this->flag;
        }
    }
    
    $key = serialize(new test('test.txt', 'test'));
    
    var_dump($key);
    
    $b = unserialize($key);

    运行结果:

    在进行序列化的时候,执行了__sleep()方法,在反序列化的时候执行了__wakeup()方法。

    然后是__toString()方法:

    class test {
    
        private $flag = '';
        
        # 用于保存重载的数据 
        private $data = array();
    
        public $filename = '';
    
        public $content = '';
    
        function __construct($filename, $content) {
            $this->filename = $filename;
            $this->content = $content;
            echo 'construct function in test class';
            echo "<br>";
        }
    
        function __destruct() {
            echo 'destruct function in test class';
            echo "<br>";
        }
    
        # 当需要输出得到对象名称时候会调用
        function __toString() {
            return $this->content;
        }
    }
    
    $a = new test('test.txt', 'data');
    echo $a."<br>";

    结果:

  • 相关阅读:
    Css颜色定义的方法汇总color属性设置方式
    关于css中的align-content属性详解
    关于char 指针变量char *=p;这个语句的输出问题
    事件绑定3
    事件绑定2
    事件绑定1
    XPath 初步讲解
    JSON初探
    CSS 媒体类型
    CSS Positioning(定位)
  • 原文地址:https://www.cnblogs.com/magic-zero/p/7360396.html
Copyright © 2011-2022 走看看