php反序列化漏洞(魔术方法)

序列化:对象转换为数组或字符串等格式

反序列化:将数组或字符串等格式转换成对象

serialize() //将一个对象转换成一个字符串

unserialize() //将字符串还原成一个对象

PHP 反序列化漏洞

原理:未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而 导致代码执行,SQL 注入,目录遍历等不可控后果。在反序列化的过程中自动触发了某些 魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法

{PHP的反序列化漏洞又可以叫做 PHP对象注入漏洞,这种思想类似SQL注入,在unserialize接受的参数可控的情况下,通过注入我们可控的属性值,来控制类中的方法(危险函数)的执行,从而造成安全隐患。}

php魔术方法利用点分析:

先了解一下什么是魔术方法: PHP: 魔术方法 - Manual

PHP 将所有以 (两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以__为前缀。

触发unserialize函数的变量可控,文件中存在可利用的类,类中有魔术方法:

__construct(): //构造函数,当对象 new 的时候会自动调用 

__destruct()://析构函数当对象被销毁时会被自动调用 

__wakeup(): //unserialize()时会被自动调用 

__invoke(): //当尝试以调用函数的方法调用一个对象时,会被自动调用 

__call(): //在对象上下文中调用不可访问的方法时触发 

__callStatic(): //在静态上下文中调用不可访问的方法时触发 

__get(): //用于从不可访问的属性读取数据 

__set(): //用于将数据写入不可访问的属性 

__isset(): //在不可访问的属性上调用 isset()或 empty()触发 

__unset(): //在不可访问的属性上使用 unset()时触发 __

__toString(): //把类当作字符串使用时触发 __

__sleep(): //serialize()函数会检查类中是否存在一个魔术方法__sleep() 如果存在,该方法会被优先调用

示例:

<?php  
  
// 定义一个名为 magic 的类  
class magic {  
    // 类的公共属性 $name,初始化为 "Annevi"  
    public $name = "Annevi";  
  
    // 构造函数,在创建对象时调用  
    function __construct() {  
        // 输出构造函数被调用的信息  
        echo "__construct";  
        echo "<br>";  
    }  
  
    // 析构函数,在对象被销毁时调用  
    function __destruct() {  
        // 输出析构函数被调用的信息  
        echo "__destruct";  
        echo "<br>";  
    }  
  
    // 当对象被反序列化时调用的魔术方法  
    function __wakeup() {  
        // 输出 __wakeup 方法被调用的信息  
        echo "__wakeup";  
        echo "<br>";  
    }  
  
    // 当对象被当作字符串使用时调用的魔术方法  
    function __toString() {  
        // 返回一个字符串,表示对象被转换为字符串时的值  
        return "__toString" . "<br>";  
    }  
}  
  
// 创建一个 magic 类的实例  
$obj = new magic(); // 调用 __construct,输出 "__construct<br>"  
  
// 输出 "1 <br>"  
echo "1 <br>";  
  
// 序列化 $obj 对象,将其转换为字符串  
$data = serialize($obj); // 此时不调用任何魔术方法  
  
// 输出 "2<br>"  
echo "2<br>";  
  
// 反序列化字符串 $data,将其转换回对象  
$un_obj = unserialize($data); // 调用 __wakeup,输出 "__wakeup<br>"  
  
// 输出 "3 <br>"  
echo "3 <br>";  
  
// 输出对象的字符串表示,调用 __toString 方法,输出 "__toString<br>"  
print($un_obj); // 注意这里使用了 print,它和 echo 功能相似,但通常用于单个值  
  
// 输出 "4 <br>"  
echo "4 <br>";  
  
// 脚本结束时,$un_obj 对象不再被引用,调用 __destruct,输出 "__destruct<br>"  
// 注意:析构函数的调用时机可能因 PHP 的垃圾回收机制而异,但通常会在脚本结束时调用

输出结果:

__construct  // 构造函数在创建对象时被调用  
1            // 第一个 echo 语句  
2            // 第二个 echo 语句  
__wakeup     // 反序列化时调用 __wakeup 方法  
3            // 第三个 echo 语句  
__toString   // 对象被当作字符串输出时调用 __toString 方法  
4            // 第四个 echo 语句  
__destruct   // 脚本结束时调用析构函数(可能因垃圾回收机制而有所延迟)

从这里我们可以看到,在创建一个对象的时候,首先调用了construct方法,紧接着执行序列化操作,并没有触发调用任何的魔术方法,之后执行反序列化操作,自动调用了wakeup方法,之后我们使用print输出对象,调用了toString,最后销毁实例化创建的对象和我们反序列化生成的对象,调用destruct两次。

<?php  
//定义类
class Test    
{   
  //定义两个变量
    public $variable = 'BUZZ';    
    public $variable2 = 'OTHER'; 
    //定义方法
    public function PrintVariable()    
    {    
        echo $this->variable . '<br />';    
    }    
    public function __construct()    
    {    
        echo '__construct<br />';    
    }    
    public function __destruct()    
    {    
        echo '__destruct<br />';    
    }    
    public function __wakeup()    
    {    
        echo '__wakeup<br />';    
    }    
    public function __sleep()    
    {    
        echo '__sleep<br />';    
        return array('variable', 'variable2');    
    }


    public function __toString() {
        return "Test object";
    }    
}    
// 创建对象调用__construct  
$obj = new Test();    
// 序列化对象调用__sleep    
$serialized = serialize($obj);    
// 输出序列化后的字符串    
print 'Serialized: ' . $serialized . '<br />';    
// 重建对象调用__wakeup    
$obj2 = unserialize($serialized);
echo $obj2.'<br>';    
// 调用PrintVariable输出数据   
$obj2->PrintVariable();    
// 脚本结束调用__destruct     
?>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值