lctf2018
bestphp’s revenge
https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html#_label1_0
session反序列化->soap(ssrf+crlf)->call_user_func激活soap类
有关session触发反序列化的资料
有关soapclient扩展反序列化的资料
index.php
1 |
|
Flag.php
1 | session_start(); |
官方Hint:反序列化
在call_user_func
的第二个参数传入了一个$_POST
数组
直接传数组的函数确实想不起来几个,更别提什么危险函数了.
通过反序列化+SSRF才能解决的题目,解决的思路就是Session处理器差异化反序列化+SOAP类CRLF/SSRF。
PHP Session 处理器的安全问题
PHP Session数组对象是通过序列化进行存储在文件中,序列化及反序列化处理器设置如果不同会导致安全问题。
PHP 内置了多种处理器用于 $_SESSION 数据时会对数据进行序列化和反序列化,常用的有以下三种,对应三种不同的处理格式:
处理器 | 对应的存储格式 |
---|---|
php | 键名 + 竖线 + 经过 serialize() 函数反序列处理的值 |
php_binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值 |
php_serialize (php>=5.5.4) | 经过 serialize() 函数反序列处理的数组 |
PHP 通过 session.serialize_handler
配置选项设置序列化及反序列化时使用的处理器,默认情况下的处理器为php
。
如果我们使用php_serialize
作为序列化$_SESSION
的处理器,同时$_SESSION
注入恶意的代码,但使用php
处理器进行反序列化,,就会出现解析问题,造成对象注入。
例如当一开始是以php_serialize
方式存储之后,在字符串中间添加了一个|
第二次以php
方式进行反序列化,只要控制|
后面的字符传为一个反序列化字符串,就会自动进行反序列化
虽然最后会有一个不可控的";}
结尾字符,但是亲测反序列化字符后面可以添加一些脏字符,但是前面不行
1.php
1 |
|
修改处理器,并使用php_serialize
进行序列化,为$_SESSION
注入|
,此时$_SESSION
中的变量为字符串
2.php
1 |
|
使用php
进行反序列化,此时存储的内容变为对象,造成对象注入。
SOAP类SSRF/CRLF注入
因为题目代码过于简单,没有可以利用的自定义类,因此只能选择php自带的类库,也就是soapclient
https://xz.aliyun.com/t/2148#toc-0
https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html#_label1_0
原生类解决了很多找不到类的情况的麻烦,但是利用Soap进行SSRF也有两个需要注意的点
- Soap不是默认开启的,需要手动开启
- 需要触发
__call
方法才能进行SSRF
soapclient的实例在访问不存在的方法时,会调用__call方法,该方法如果参数可控就能SSRF,同时其中的user_agent还可以造成CRLF注入。
1 |
|
SSRF&&CRLF注入Cookie
¶call_user_func执行类方法
执行类方法
1 | class myclass |
解决
首先通过第一个call_user_func
首先设置session处理器,然后注入soapclient的恶意代包含码。
然后然后反序列化造成$_SESSION
对象注入,再通过第一个call_user_func
实现对$b的变量覆盖,覆盖为call_user_func
,控制第二个call_user_func
,触发soapclient的welcome_to_the_lctf2018
方法,因为不存在因此触发soapclient的SSRF,需要注入PHPSESSID不然无法将Flag返回当前会话中。
题解
查看session_start
的官方文档就可以看到,允许其中传入一个数组,来设置session.
的一些参数
这样,我们就可以利用call_user_func($_GET[f],$_POST);
来设置session.serialize_handler
的值,从而进行Soap的反序列化
1 |
|
1 | http://172.81.210.82/?f=session_start&name=|O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A3%3A%22123%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2Flocalhost%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A52%3A%22AAA%3ABBB%0D%0ACookie%3APHPSESSID%3Db8govp8041cfm1cb307bsf66v3%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D |
提交之后,就能看到,这里以php_serialize
模式保存session的时候,会显示name
的value值是一个序列化字符串
当我们直接访问这个页面,也就是以php
的默认模式开启session的时候,就可以看到,这里将|
后面的反序列化字符串,反序列化成了一个Soap
类
要触发Soap
的SSRF,还得需要触发它的__call
方法,这样我们就可以尝试利用extract
覆盖掉变量$b
去调用一个不存在的方法,就会触发SSRF
1 | http://172.81.210.82/?f=extract&name=Soapclientb=call_user_func |
这样,就可以让Soap带上自己的Cookie去访问flag.php,再次刷新页面,就可以看到flag了