2020网鼎杯青龙组-php反序列化
赛后拿到了题目的源码,搭到本地做了一遍,今天不比赛,先试试题目难度~
题目代码如下:
|
|
大致过了遍代码,看到这里存在php反序列化,有两条利用链,分别对应文件的读和写操作,具体分析如下:
首先传入序列化字符串str,反序列化时触发FileHandler类的__destruct
方法,这里会对$this->op
的值进行判断,当$this->op==="2"
,会将$this->op
重新赋值为"1"
,并将$this->content
赋为空字符串;之后进入process
方法,当$this->op=="1"
时,进入write
方法,但是由于这里$this->content
的值被赋为了空字符串,所以不能向文件写入任何可控内容,导致“写”这一条利用链不能利用,所以要想办法绕过__destruct
当中的判断,进行读文件的利用。
这里可以将$this->op
的值设置为整型数字2进行绕过,因为在__destruct
当中使用===
进行了强比较,所以2==="2"
结果为False,而在process
方法中,使用了==
进行比较,由于php弱类型,2=="2"
结果为True,进入read函数
pop链构造如下
|
|
得到payload:O:11:"FileHandler":2:{s:5:"*op";i:2;s:11:"*filename";s:11:"/etc/passwd";}
直接将上述字符传入str并不能读取成功,这是因为序列化字符串当中protected属性的影响,三种属性对应的序列化字符串格式如下
|
|
所以这里的payload实际上为:O:11:"FileHandler":2:{s:5:"00*00op";i:2;s:11:"00*00filename";s:11:"/etc/passwd";}
因为存在protected属性,导致序列化字符串当中存在字符00,而在对字符串进行反序列化之前,这里的is_valid
函数对字符串当中的字符进行了检查,要求其字符必须为可显示字符(ascii码值32-125),所以这里要将payload改为:O:11:"FileHandler":2:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";s:11:"/etc/passwd";}
,将s改为S
\00
三个字符均符合!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)
,所以这里读取成功
然后就可以通过任意文件读取获得flag了
5月11日补充:
题目环境buuoj上已经有了,今天看了颖奇L’Amore师傅的博客,对于这道题在这里需要补充几点
文章链接:第二届“网鼎杯”青龙组WEB部分题目Writeup
文章里提到php7.1+版本对属性类型不敏感,所以在构造poc时直接将对象属性类型设为public也能绕过is_valid
的检查,导致非预期解
|
|
payload:O:11:"FileHandler":2:{s:2:"op";i:2;s:8:"filename";s:11:"/etc/passwd";}
另外需要注意的是,这里如果使用相对路径的话有可能会失败,原因在文章里也说了,在执行完序列化字符串后,如果没有出现异常就会穿越到根目录,导致读取不到flag.php,在本地环境var_dump
一下scandir(".")
和$res
可以看到反序列化后跳到了根目录,获取/flag.php
为false
(本地php版本:7.0.33)
payload:O:11:"FileHandler":2:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";s:8:"flag.php";}
当使用相对路径读取失败时,可以尝试使用以下几种解法:
方法一:使用绝对路径,需要获取网站根目录,可以从apache配置文件当中获取跟目录,默认配置下,可以从/etc/apache2/sites-available/000-default.conf
当中获取网站根目录
payload:O:11:"FileHandler":2:{s:2:"op";i:2;s:8:"filename";s:45:"/etc/apache2/sites-available/000-default.conf";}
可以看到buuoj上这道题的环境没有修改根目录,所以直接读取/var/www/html/flag.php
即可
payload:O:11:"FileHandler":2:{s:2:"op";i:2;s:8:"filename";s:22:"/var/www/html/flag.php";}
而在比赛环境当中是修改了网站的根目录的,而且apache网站配置文件也不是上面说的那个,需要通过/proc/self/cmdline
才能获取到网站的根目录
方法二:在反序列化字符串时触发异常,这样就不会跳转到根目录了
当序列化字符串当中属性个数大于实际个数时,就会触发异常,所以把payload修改为O:11:"FileHandler":3:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";s:8:"flag.php";}
即可
另外,有一个奇怪的问题,经过测试发现,buuoj的题目环境通过相对路径就可以读取到flag.php,自己的docker环境也是这样(php版本:7.0.33),有点奇怪,不清楚是什么因素决定的,,,,