php-unserialize反序列化漏洞
unserialize反序列化漏洞相关知识
在了解反序列化漏洞之前,先了解一下php中的序列化。
php中的序列化和反序列化都是通过函数来实现的:
- 序列化用到serialize
- 反序列化则是unserialize
序列化(serialize):序列化是将对象的状态信息转换为可保存或传输的字符串的过程
反序列化(unserialize):反序列划就是将字符串转换为对象原本的状态信息。
0x01. 序列化:
以上代码,就是进行序列化的处理
其中:
- O是指类型object
- 4是指类名的长度
- tset是类名
- 1是指其中的属性数量
- 花括号里面是整个属性内容,s是指string类型(字符串)
- 5是指属性名的长度为5
- s是属性值类型
- 4是属性值的长度
- 最后xxba是属性值内容
0x02. 反序列化
以上代码,就是进行反序列化的处理。
从序列化后的结果中恢复对象的状态信息
test类中有一个变量index内容是xxba
0x03. 反序列化漏洞
本质上serialize()和unserialize()在PHP内部实现上是没有漏洞的,漏洞的主要产生是由于应用程序在处理对象、魔术函数以及序列化相关问题的时候导致的。
当传递给unserialize()的参数可控时,那么攻击者就可以注入payload,当进行反序列化的时候就有可能会触发对象中的一些魔术方法。
0x04. 魔术方法
php中的魔术方法有很多,如__construct()、__destruct()、 __call() 、__callStatic()、__get()、__set()、__isset()等
主要关注一下几个:
-
__construct():当对象创建的时候自动调用,但在unserialize()的时候不会调用
-
__destruct():当对象被销毁的时候会自动调用
-
__wakeup():unserialize()反序列化的时候会自动调用
代码执行解析:
- 定义一个类,里面有一个变量还有三个方法。
- 之后定义一个变量值固定为序列化过后的字符串
- 输出这个字符串
- 对$class2这个变量内容进行反序列化
- 这时候unserialize()会调用test类检查到存在wakeup方法所以就输出了wakeup相关内容
- 当代码继续执行的时候看见__construct()方法,直接跳过,因为这个方法是对象创建的时候调用的
- 输出 $class2序列化后的结果中恢复的对象的状态信息
- 最后当脚本运行结束之前,会调用__destruct()析构函数
靶场实操
打开靶场之后,发现就是一个简单的页面,进入flag.php发现页面是空的,没有任何东西。
那么就只能将这个页面源码 解析一下了,首先看到第一个方法有两个下划线,这里可能是魔术方法,搜索一下,果然是魔术方法。
具体用途是当一个对象被当作字符串对待的时候,会触发这个魔术方法
highlight_file是对文件进行语法高亮显示。
php代码部分解读完毕,其实只有一处能为我们所用,就是第二个判断语句,这样会继续向下执行。
因为在最下面还有一串foreach,所以需要代码执行到最下面,能够满足的只有第二个if语句。
这里就是循环遍历数组,输出反序列化后的对象。
接下来就要想办法让flag.php的内容输出出来,这里就可以利用反序列化,找到反序列化代码段、可控制的点。
这个点很明显就能够满足,我们能够控制cookie传参。
将源码前半段复制到本地,使其进行序列化,但是因为遍历需要是数组才能遍历,所以在发序列化的时候就直接加上一个数组
本地访问,得到序列化后的结果。
当数组遍历的时候会进入__tostring()方法,会输出readme.txt和传递进去的值,因为这里传递的是flag.php所以就会输出flag.php的值。
得到了序列化之后的结果,就需要满足第二个if中的条件,md5($m)=$h。
那么将序列化之后的结果进行md5加密:e2d4f7dcc43ee1db7f69e76303d0105c
然后拼接上序列化之后的结果.就是
e2d4f7dcc43ee1db7f69e76303d0105ca:1:{i:0;O:6:“readme”:1:{s:6:“source”;s:8:“flag.php”;}}
但是因为cookie要经过url,所以需要将这一串字符串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