[渗透测试]:反序列化漏洞
[渗透测试]:反序列化漏洞
反序列化
为什么要序列化
class 类
obj 对象
百度百科上序列化是这样当以的将对象的状态信息转换为可以存储或传输的形式(字符串)的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
简单的说,序列化就是把一个对象变成可以传输的字符串,可以以特定的格式在进程之间跨平台、安全的进行通信。
PHP 中序列化与反序列化
PHP 反序列化漏洞也叫PHP 对象注入,是一个常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常威胁的后果。漏洞的形成的根本原因是程序员没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell 等一系列不可控的后果。反序列化漏洞并不是PHP 特有的,也存在于Java、Python 等语言之中但其原理基本相通。
PHP中的序列化与反序列化,基本都是围绕serialize()和 unserialize() 两个函数展开的。
*简单的例子
我们可以用json 格式数据的编码与解码,来理解序列化与反序列化过程。虽然json 数据与反序列化漏洞没有什么关系,但是这个例子有助于我们理解。
测试代码:
<?php
$stu=array('name'=>'GGG','age'=>18,'SEX'=>true,'score'=>89.9);
echo $stu;
echo "<hr />";
$stu_json=json_encode($stu);
echo $stu_json;
?>
我们定义一个数组,数组属于抽象的数据结构,为了方便跨平台传输,可以将其进行json 编码。json 格式的数据是以键值对的形式出现的。
Array{"name":"GGG","age":18,"SEX":true,"score":89.9}
*序列化Demo
序列化会将一个抽象的转换为字符串。
我们可以写一个Demo 来说明序列化的过程,首先创建一个类,代码如下
<?php
class Stu{
public $name;
public $sex;
public $age;
public $score;
}
?>
类名是Stu ,有四个变量。
下面我们创建一个对象,并给对象中变量赋值。代码如下
<?php
include "classStu.php";
$stu1 = new Stu();
$stu1->name = "GGG";
$stu1->sex = true;
$stu1->age = 18;
$stu1->score = 89.9;
echo serialize($stu1);
?>
我们最后使用serialize() 函数,将$stu1 这个对象序列化成一个字符串。这样的字符串,就很容易传输和存储。如下
O:3:"Stu":4: //O 代表Object 对象;3对象名有三个字符;对象中有4个变量
{s:4:"name";s:3:"GGG";
s:3:"sex";b:1;
s:3:"age";i:18;
s:5:"score";d:89.900000000000006;}
同样,我们也可以使用unserialize()函数,将字符串反序列化为一个对象。由于字符串中含有双引号,我们可以使用定界符,代码如下
<?php
include "classStu.php";
$stu1 =<<<HTML
O:3:"Stu":4:{s:4:"name";s:3:"GGG";s:3:"sex";b:1;s:3:"age";i:18;s:5:"score";d:89.900000000000006;}
HTML;
$stu1=unserialize($stu1);
var_dump($stu1);
?>
运行这个脚本,我们可以看到反序列化后的对象
object(Stu)#1 (4) { ["name"]=> string(3) "GGG" ["sex"]=> bool(true) ["age"]=> int(18) ["score"]=> float(89.9) }
*漏洞何在?
@ 创建一个类,一个对象并将其序列化和反序列化
<?php
class Test{
public $str='GGG';
function __destruct(){
//echo "This is function __contruct()";
@eval($this->str);
}
}
$test = new Test();
echo serialize($test);
echo "<hr />";
var_dump(unserialize($_GET['obj']));
?>
@ 反序列化注入
构造序列化字符
…/class/loudong.php?obj=O:4:"Test":1:{s:3:"str";s:10:"phpinfo();";}
会发现phpinfo() 函数被执行了
由以上代码可以发现,PHP的反序列化漏洞需要与其他漏洞配合,比如代码执行SQLi等
*为什么会这样呢
我们注入的字符串[phpinfo()],为什么会作为PHP 语句执行呢?我们没有调用__destruct ()函数,为什么它会被执行呢?
我们可以使用如下测试代码来测试__destruct() 函数
<?php
class Test{
public $str='GGG';
function __destruct(){
echo "This is function __destruct()";
//@eval($this->str);
}
}
$test = new Test();
echo serialize($test);
echo "<hr />";
var_dump(unserialize($_GET['obj']));
?>
我们会发现,在销毁实例化类(对象)的时候,__destruct() 函数会被调用,并输出字符串。
以 __ 开头的方法,是PHP 中的魔术方法,类中的魔术方法,在特定的情况下会被自动调用。主要魔术方法如下:
__construct()
|
创建对象时自动调用
|
__destruct()
|
在销毁对象时自动调用
|
__call()
|
在对象中调用一个不可访问方法时,__call() 会被调用
|
__callStatic()
|
在静态上下文中调用一个不可访问方法时调用
|
__get()
|
读取不可访问属性的值时,__get() 会被调用
|
__set()
|
在给不可访问属性赋值时,__set() 会被调用
|
__isset()
|
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用
|
__unset()
|
当对不可访问属性调用 unset() 时,__unset() 会被调用
|
__sleep()
|
serialize() 函数会检查类中是否存在一个魔术方法__sleep() ,如果存在,该方法会鲜卑调用,然后再执行序列化操作
|
__wakeup()
|
unserialize() 会检查是否存在一个 __wakeup() 方法,如果存在会先调用__wakeup方法,预先准备对象需要的资源
|
__toString()
|
__toString() 方法用于一个类被当成字符串时应增氧回应
|
__invoke()
|
当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用
|
__set_state()
|
字PHP 5.1.0 起调用 var_export() 导出类时,此静态 方法会被调用
|
__clone()
|
当复制完成时,如果定义了 __clone 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值。
|
__debugInfo()
|
This method is called by var_dump() anobject to get the properties that should be shown .If the method isn't on an object ,then all public ,protected and private propertieswill be shown.
|