反序列化渗透与防御之PHP

目录

一.PHP的类与对象

1.类

2.对象

二.序列化与反序列化

1.序列化

1.1什么是序列化

1.2序列化格式

2.反序列化

三.Magic函数

1.常用的Magic函数

2.__sleep()函数的补充说明

四.反序列化漏洞利用

1.参数可控

2.存在Magic方法

3.涉及成员变量

五.__wakeup()函数的绕过

六.反序列化漏洞防御


一.PHP的类与对象

在讲序列化与反序列化之前,我们可以先了解一下PHP的类与对象。(熟悉PHP的可以直接跳过)

1.类

定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。

<?php
    class Person{
/*成员变量*/
        public $name;//公有
        var $weight;//如果用var来定义,则默认为公有
        private $age;//私有
        protected $sex;//受保护
/*成员函数*/
        //构造函数:用来在创建对象时初始化对象(与new运算符搭配使用)
        function __construct($n,$w,$a,$s){
            $this->name=$n;
            $this->weight=$w;
            $this->age=$a;
            $this->sex=$s;
        }
        //析构函数:当对象结束其生命周期时,系统会自动调用
        function __destruct(){
            print "销毁";
        }
        function SetAge($a){
            $this->age=$a;
        }
        function getAge(){
            echo $this->age;
        }
}
?>

2.对象

是类的实例。

/*对象的创建*/
$person=new Person("张三",60,20,"男");//用new运算符来实例化该类的对象
/*调用成员函数*/
$person->setAge(18);
$person->getAge();

二.序列化与反序列化

1.序列化

1.1什么是序列化

序列化是将对象的状态信息转化为可以存储或传输的形式的过程。简单讲,就是将数据转化成一种可逆的数据结构。

序列化的作用就是便于传输对象和用作缓存。

在PHP中,使用serialize()函数进行序列化。

1.2序列化格式

(以下使用的对象均为上述的例子)

<?php
$str1="abcd";//字符型
$str2=20;//整型
$str3=true;//boolean
$str4=null;//null
$str5=array("index1"=>"value1","index2"=>"value2");//数组
$str6=new Person("张三",60,20,"男");//对象
/*序列化*/
$str11=serialize($str1);
$str22=serialize($str2);
$str33=serialize($str3);
$str44=serialize($str4);
$str55=serialize($str5);
$str66=serialize($str6);
echo $str11."</br>";
echo $str22."</br>";
echo $str33."</br>";
echo $str44."</br>";
echo $str55."</br>";
echo $str66."</br>";
?>

运行结果:

在序列化时,只会保存类名和属性值,并不会保存方法。

2.反序列化

知道什么是序列化,反序列化就比较好理解了,将序列化后的字符串恢复成原始数据的逆向过程就叫做反序列化。

在PHP中,使用unserialize()函数进行反序列化。

<?php
$str111=unserialize($str11);
$str222=unserialize($str22);
$str333=unserialize($str33);
$str444=unserialize($str44);
$str555=unserialize($str55);
$str666=unserialize($str66);
echo var_dump($str111)."</br>";
echo var_dump($str222)."</br>";
echo var_dump($str333)."</br>";
echo var_dump($str444)."</br>";
echo var_dump($str555)."</br>";
echo var_dump($str666)."</br>";
?>

运行结果:

在反序列化中:

1、如果传递的字符串不可以序列化,则返回 FALSE
2、如果对象没有预定义,反序列化得到的对象是__PHP_Incomplete_Class

三.Magic函数

1.常用的Magic函数

__construct当一个对象创建时被调用
__destruct当一个对象销毁时被调用
__toString当一个对象被当作一个字符串使用
__serialize()调用serialize()前执行
__unserialize()调用unserialize()前执行
__sleep在对象被序列化之前运行
__wakeup在对象被反序列化之后被调用
__call()在对象上下文中调用不可访问的方法时触发
__callStatic()在静态上下文中调用不可访问的方法时触发
__get()用于从不可访问的属性读取数据
__set()用于将数据写入不可访问的属性
__isset()在不可访问的属性上调用isset()或empty()触发
__unset()在不可访问的属性上使用unset()时触发
__invoke()当脚本尝试将对象调用为函数时触发

2.__sleep()函数的补充说明

<?php
class Person{
        public $name;
        var $weight;
        private $age;
        protected $sex;
        function __construct($n,$w,$a,$s){
            $this->name=$n;
            $this->weight=$w;
            $this->age=$a;
            $this->sex=$s;
        }
        function __sleep(){
            return array('name','weight');
        }
}
$person=new Person("张三",60,20,"男");
$str=serialize($person);
echo $str;
?>

运行结果:

在类中,如果定义了__sleep()函数,则在序列化中,只会序列__sleep()函数数组里的属性,而其他并不会被序列化。

四.反序列化漏洞利用

这里以BUUCTF在线评测 (buuoj.cn)为例来讲解。

(此处主要讲反序列化漏洞出现的原因,不讲如何找flag,当然,你学完本篇之后,这道题你就完全可以做了,我后面也会写一篇题解)

补上BUUCTF[极客大挑战 2019]PHP1题解-CSDN博客

我们先找到网站的源代码。

1.参数可控

此处,我们可以看到这里用get提交方式获取select。

一个可序列化漏洞出现的原因必然有:unserialize()函数的参数可控,比如通过GET请求传参,此处也是漏洞触发点。

如果没有这个条件,我们就无法控制传进的数据,接下来的任何一步渗透就都无法执行。

2.存在Magic方法

在上述中讲过,有些Magic方法会在对象在被反序列化中被调用,因此,所涉及到的Magic方法就是获取信息的关键点。

由此,我们可以得出反序列化漏洞出现的另一原因:类中存在Magic函数,函数里面有向php文件做读写数据或者执行命令的操作。

常见的利用函数:

类别                                        函数
命令执行system(),passthru(),popen().exec()
文件操作file_put_contents(),file_get_contents(),unlink()

3.涉及成员变量

这个并不难理解,可以与第一点联系起来,参数可控就是为了传进我们所想要的对象成员变量,如果在Magic函数中并不会因为所传进的数据而有不同的执行结果,那就没有意义了。

因此,反序列化漏洞存在的又一原因:操作的内容需要有对象中的成员变量的值。

总结,对反序列化漏洞的利用就是:序列化一个对象,修改成员变量的值,达到操作其他
文件或者执行命令的目的。

五.__wakeup()函数的绕过

由前面所学可以知道,__wakeup()函数在对象被反序列化之后被调用。在反序列化渗透中,我们往往需要绕过该函数。

绕过方式十分简单,在反序列化的时候,如果写的参数个数,与实际的参数个数不一致,就会绕过__wakeup()函数。

<?php
header("Content-type:text/html;charset=utf-8");
class Person{
    public $name;
    public $age;
    function __construct($n,$a){
        $this->name=n;
        $this->age=a;
    }
    function __wakeup(){
        echo "__wakeup()";
    }
}
$person=new Person("张三",20);
$str='O:6:"Person":3:{s:4:"name";s:1:"n";s:3:"age";s:1:"a";}';
$str1=unserialize($str);
echo var_dump($str);
?>

运行结果:

明显绕过了__wakeup()函数。

六.反序列化漏洞防御

既然我们知道了造成反序列化漏洞的原因,那我们就可以针对这些原因一一防御了。

1.针对unserialize()和Magic函数审计。

2.对用户输入的内容过滤。

3.设置白名单,限制反序列化的类;不能动态传参。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值