本篇文章用于巩固对自己PHP反序列漏洞的学习总结,其中部分内容借鉴了以下博客。
链接: pikachu PHP反序列化(皮卡丘漏洞平台通关系列))
在理解这个漏洞前,你需要先搞清楚php中serialize(),unserialize()这两个函数。
先跟着pikachu简单学习一下什么叫序列化和反序列化:
但其实我已知这里有3个地方写的不对,需要更正一下:
(1)序列化结果中的S不是对象名称,而是类名称;同样的,O和S中间的1应该是类名字长度。
(2)几个魔法函数的举例有点让人误会,很容易误以为这些魔法函数都会造成反序列化漏洞,但其实__construct()在unserialize()时并不会被自动调用,这点在之后会进行演示。
(3)__wakeup()不是下图中写的那个作用,而是执行unserialize()时,先会调用这个函数,然后再进行反序列化
总之,序列化是把对象转换为字符串,从而达到传输和存储的目的;反序列化是把字符串还原为对象,以便被代码处理。
这里说的字符串不是普通字符串,是有固定格式的。
此外,根据《PHP和MySQL Web开发》一书,PHP在重新实例化类之前,需要了解类结构,因此在调用session_start()或unserialized()之前,需要引入类定义文件。
综上,要想利用反序列化漏洞,至少要知道两点:
(1)类名称
(2)类包含的变量名称
所以通常需要代码审计查找反序列化漏洞
观察页面,就下图这个输入框,什么提示也没有,显然不符合知道类名称和知道类中变量名两个条件
那么,要么就需要代码审计,要么估计这关的类和pikachu的解说中的类是一样的。
那先用解说中的payload:(O:1:"S":1:{s:4:"test";s:29:"";})
试一下,果然可以:
接下来可以手动根据格式修改payload,也可以用代码构造payload
(1)手动修改payload
比如想要弹框显示cookie,那xss payload是:
其长度是39字节。因此,反序列化payload为:O:1:"S":1:{s:4:"test";s:39:"";}
创建以下内容的文件,放在www目录下(当然如果直接把xss payload写到类定义里面也可以)
代码如下
alert(document.cookie)";
$s->test=$payload;
echo serialize($s);
?>
浏览器访问这个文件,就可以得到反序列化的payload
究竟是哪行代码造成了这个反序列化漏洞呢?
先看一下源代码:
反正问题肯定就在下面这段了
狼人看着反正要么就是20~22行的 {$unser->test}__construct()
,要么就是33行的$html.="
(完整源代码最后有echo $html)
就这两个地方涉及到向网页输出
那就用排除法尝试吧,先把33行注释掉
输入payload:(O:1:"S":1:{s:4:"test";s:29:"";})
之后没有弹框,说明这不是__construct()的锅,到此已经可以判断是第33行起的作用了,保险起见还是再试一下。
这次把33行放出来,把__construct()注释掉:
输入payload:(O:1:"S":1:{s:4:"test";s:29:"";})
之后弹框了,果不其然,抓住狼人,就是第33行。
那么疑惑就来了,为啥__construct()没起作用呢?
网上搜索了一下,很多讲解反序列化的文章都说了,__construct()这个函数虽然在对象创建时会被调用,但反序列化(unserialize())的时候并不会被调用。
最后测试一下__destruct()函数被读取的情况。 {$unser->test}
把代码中的__construct()改成__desstruct()
,并把之前的狼人($html.="
注释掉: