unserialize反序列化漏洞


title: unserialize反序列化漏洞

理解序列化要有这种意识:
内存数据是“稍纵即逝”的;——通常,程序执行结束,立即全部销毁。
变量所存储的数据,就是内存数据;
文件是“持久数据”;
数据库也是持久数据;

序列化:
就是,将内存的变量数据,“保存”到文件中的持久数据的过程。
简化就是:将内存变为文件;

s=serialize(s = serialize(s=serialize(变量); //该函数将变量数据进行序列化转换为字符串
file_put_contents(‘./目标文本文件’, s);//s); //将s);//将s保存到指定文件

反序列化:
就是,将序列化过存储到文件中的数据,恢复到程序代码的变量表示形式的过程。
简化就是:将文件变为内存;

$s = file_get_contents(‘./目标文本文件’); //取得文本文件的内容(之前序列化过的字符串)
=unserialize(变量 = unserialize(变量=unserialize(s); //将该文本内容,反序列化到指定的变量中

演示序列化与反序列化

unserialize反序列化漏洞
String(字符串) : s:size:value;
Integer(整数型) : i:value;
Boolean(布尔型) : b:value;(1或0)
Null(空值): N;
Array(数组): a:size:{键名;键值;(每个元素重复)}

类的序列化

unserialize反序列化漏洞
Object(对象): O:对象名的长度:对象名:对象数量:{s:属性名长度:属性名;(每个元素重复)}

魔术方法

魔术方法是PHP面向对象中特有的特性。它们在特定的情况下被触发,都是以双下划线开头,你可以把它们理解为钩子,利用模式方法可以轻松实现PHP面向对象中重载(Overloading即动态创建类属性和方法)。

__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() 、__debugInfo() 

PHP 将所有以 _(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以 _ 为前缀。
1.__construct,__destruct
__constuct构建对象的时被调用;
__destruct明确销毁对象或脚本结束时被调用;
2.__get,__set
__set当给不可访问或不存在属性赋值时被调用
__get读取不可访问或不存在属性时被调用
3.__isset,__unset
__isset对不可访问或不存在的属性调用isset()或empty()时被调用
__unset对不可访问或不存在的属性进行unset时被调用
4.__call,__callStatic
__call调用不可访问或不存在的方法时被调用
__callStatic调用不可访问或不存在的静态方法时被调用
5.__sleep,__wakeup
__sleep当使用serialize时被调用,当你不需要保存大对象的所有数据时很有用
__wakeup当使用unserialize时被调用,可用于做些对象的初始化操作
6.__clone
进行对象clone时被调用,用来调整对象的克隆行为
7.__toString
当一个类被转换成字符串时被调用
8.__invoke
当以函数方式调用对象时被调用
9.__set_state
当调用var_export()导出类时,此静态方法被调用。用__set_state的返回值做为var_export的返回值。
10.__debuginfo
当调用var_dump()打印对象时被调用(当你不想打印所有属性)适用于PHP5.6版本

靶场演示

unserialize反序列化漏洞
这是一道反序列化的题目,可以看到flag就在这个同级目录中flag.php,前面有一个类,里面定义了一个类readme,并且这里有一个魔术方法__tostring:当一个类被转换成字符串时被调用。就是当对象被当作字符串输出的时候会触发这个魔法方法。highlight_file是语法的高亮显示。
unserialize反序列化漏洞
而这段代码的作用是显示源码。而我们的思路就是在source=__FILE__的地方把这个__FILE__换成flag.php就可以把我们的源码显示出来。但是这里的传参并不是我们能够控制的。接着找输出点。
unserialize反序列化漏洞
这里找到一个我看不太懂的东西。我们去百度一下这个<?=$todo?>是什么意思。
unserialize反序列化漏洞
发现这是<?php echo $todo ?>的缩写。所以找到一个输出点。然后发现这个todotodo是todo是todos遍历出来的。然后控制这个todostodos的输出,并且这个todos的输出,并且这个todos是一个数组。也只有数组才能foreach遍历。
unserialize反序列化漏洞
然后我们来分析一下这个段代码,ccookiec获取cookie,c获取cookie,h截取cookie前32位,m32md5m截取后32位。然后判断md5后的m截取后32位。然后判断md5后的m是否等于hh,如果等于就会触发h,如果等于就会触发todos获取反序列化$m的值。然后我们思路清楚了,首先构造我们需要传入的payload
unserialize反序列化漏洞
然后使得

$m=a:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}

而cookie的值分析得出cookie=md5(m)+m)+m)+m时触发反序列化。

md5($m)=e2d4f7dcc43ee1db7f69e76303d0105c

然后把两端加起来

e2d4f7dcc43ee1db7f69e76303d0105ca:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}

最后传入cookie,因为cookie传参也是要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

传入cookie,成功输出flag.php的内容。
unserialize反序列化漏洞

上一篇:php数组如何存到数据库中?采用序列化方案serialize() 和 unserialize()


下一篇:Cacti如何实现电话告警