Round 1
<?php $content = '<?php exit; ?>'; $content .= $_POST['txt']; file_put_contents($_POST['filename'], $content);
$content
在开头增加了exit过程,导致即使我们成功写入一句话,也执行不了。幸运的是,这里的$_POST['filename']
是可以控制协议的,我们即可使用 php://filter协议来施展魔法。
#方法一、base64编码
使用php://filter流的base64-decode方法,将$content
解码,利用php base64_decode函数特性去除“死亡exit”。
众所周知,base64编码中只包含64个可打印字符,而PHP在解码base64时,遇到不在其中的字符时,将会跳过这些字符,仅将合法字符组成一个新的字符串进行解码。
所以,当$content
被加上了<?php exit; ?>
以后,我们可以使用 php://filter/write=convert.base64-decode 来首先对其解码。在解码的过程中,字符<、?、;、>、空格等一共有7个字符不符合base64编码的字符范围将被忽略,所以最终被解码的字符仅有“phpexit”和我们传入的其他字符。
“phpexit”一共7个字符,因为base64算法解码时是4个byte一组,所以给他增加1个“a”一共8个字符。这样,"phpexita"被正常解码,而后面我们传入的webshell的base64内容也被正常解码。结果就是<?php exit; ?>
没有了。
最终效果:
#方法二、利用字符串操作方法
除了使用base64特性的方法外,我们还可以利用php://filter字符串处理方法来去除“死亡exit”。我们观察一下,这个<?php exit; ?>
实际上是什么?
实际上是一个XML标签,既然是XML标签,我们就可以利用strip_tags函数去除它,而php://filter刚好是支持这个方法的。
编写如下测试代码即可查看 php://filter/read=string.strip_tags/resource=php://input 的效果:
echo readfile('php://filter/read=string.strip_tags/resource=php://input');
可见,<?php exit; ?>
被去除了。但回到上面的题目,我们最终的目的是写入一个webshell,而写入的webshell也是php代码,如果使用strip_tags同样会被去除。
万幸的是,php://filter允许使用多个过滤器,我们可以先将webshell用base64编码。在调用完成strip_tags后再进行base64-decode。“死亡exit”在第一步被去除,而webshell在第二步被还原。
最终效果:
#方法三、ROT13编码
原理和上面类似,核心是将“死亡exit”去除。<?php exit; ?>
在经过rot13编码后会变成<?cuc rkvg; ?>
,在PHP不开启short_open_tag时,php不认识这个字符串,当然也就不会执行了:
Round 2
<?php a = $_POST['txt']; file_put_contents($a,”<?php exit();”.$a);
这种是前后两个变量相同,假设$a可控情况。
这种相同变量的构造方式和不同变量的构造方式思路是大差不差的,都是需要干掉<?php exit();
,只不过构造起来相对更复杂一些。
#方法一、base64编码
##########明天继续更