先挖个坑再总结一下php反序列化的字符串逃逸…
2019安洵杯easy_serialize_php
- buuoj和xctf都有,但是很离谱的一个开不出环境,一个开了环境访问不了phpinfo。。。不知道是平台的问题还是怎样(后来发现buuoj的除了phpinfo看不了其他都正常,妈的绝了)
- 最后直接clone了题目的docker自己跑
1 |
|
代码审计
GET请求参数f的值
- highlight_file: index.php
- phpinfo: phpinfo()
- show_image: 把\$_SESSION序列化后经过filter过滤,再反序列化,最后打印base64解码后的img属性
- img_path: 若存在,则进行base64编码和sha1哈希后赋值给\$_SESSION[“img”];若不存在,则将guest_img.png base64编码后赋值给\$_SESSION[“img”]
看一眼phpinfo()
找到一个疑似可能有flag或者hint的页面d0g3_f1ag.php
extract(\$_POST)
extract() 函数从数组中将变量导入到当前的符号表。
extract第二个参数用来指定当出现冲突时应该怎么做,默认是EXTR_OVERWRITE
即覆盖变量
若选择EXTR_SKIP,则不覆盖
这道题我们只要知道我们传的数组会覆盖掉原来的值就行了,结合后面分析\$_SESSION应该是通过这个地方的POST赋值的
$_SESSION
有三个元素- user=guest
- function=$function 可控
- img: 取决于GET的参数
- 要注意\$_SESSION无法直接通过赋值控制,因为
unset($_SESSION);
会把SESSION销毁
思路
1 | else if($function == 'show_image'){ |
这几行是重点
GET传
?f=show_image
;POST传$\_SESSION
$serialize_info = filter(serialize($_SESSION));
\$_SESSION序列化后经过过滤器将一些字符串过滤,然后反序列化后赋给\$userinfo
\$_SESSION -> 序列化 -> 过滤 -> 反序列化 -> \$userinfo
打开\$_userinfo 的img属性的值经过base64解码后的文件名对应的文件,我们这里选择d0g3_f1ag.php看看里面有什么
反序列化字符串逃逸
关键点在于
file_get_contents(base64_decode($userinfo['img']))
,如果按照正常的流程,img属性的值为guest_img.png的base64编码,我们需要令其为d0g3_f1ag.php的base64编码。由于filter过滤器是对序列化后的内容进行过滤,是一种典型的长变短反序列化字符串逃逸
可以看到出现flag的地方都会被替换为空,导致长度不符反序列化出错
如果直接把d0g3_f1ag.php的base64编码直接作为img参数传递的话,会发现img属性的值被替换为guest_img.php
因此我们需要序列化字符串在反序列化时,读取到
s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";
前,让反序列化结束,同时读取到img=ZDBnM19mMWFnLnBocA==(d0g3_f1ag.php的base64编码)先随便赋个值尝试一下
发现在没有过滤的情况下多了一个属性
我们知道php反序列化的分隔符为 ;} ,当读到这里时反序列化结束,因此我们需要伪造一个分隔符,在分隔符前构造img=ZDBnM19mMWFnLnBocA==的payload
可以看到灰色部分就是我们需要的内容,接下来就要利用序列化字符串的flag会被过滤的特性让前面的内容作为“值”被覆盖
先简单尝试一下
经过过滤后变为
a:2:{s:4:"";s:40:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
,可以看到第一个s长度为4,所以读到";s:
就结束了,导致反序列化出错我们计算出需要覆盖的内容
";s:40:
长度为7,刚好flag和php两个被过滤的字符串长度和为7重新尝试
_SESSION[flagphp]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
发现还是不对,这是因为长度7虽然覆盖了那几个字符串,但是他只是把
";s:40:
作为属性名而已,还缺少了属性值,因此还要在后面给他加上属性值继续构造payload,属性值其实就无所谓了,因为不影响前后的构造
_SESSION[flagphp]=;s:1:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
可以发现终于成功了
把payload丢到buuoj的靶机上,查看源代码即可发现hint
最后一步就把d0g3_f1ag.php的base64编码换为/d0g3_fllllllag的base64编码即可
答案
payload1
_SESSION[flagphp]=;s:1:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
payload2
_SESSION[flagphp]=;s:1:"a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
做的过程还遇到一个小坑:burp2021的重放模块,不知道为什么,post数据没经过url编码的话会导致post错内容
没有url编码的情况
发现值没有被修改
有url编码的情况
值正常修改
这次遇到的是中括号[]引起的,其他符号不知道会不会也这样,搞了好久还一直以为是自己做法有问题,,,直到发现hackbar没问题才意识到是burp的问题
本题测试代码
1 |
|