前言
注:审计环境均为本地搭建,非生产环境。
环境搭建: *左右滑动查看更多 搭建过程可按照上文搭建说明进行,如在加载依赖时遇到提示Cannot resolve com.google.code:kaptcha: 2.3.2,解决方法如下: 解压src/main/resources/db/dreamer-cms.zip文件,并修改src/main/resources/application-dev.yml中的地址为解压后文件夹的地址。 搭建redis和mysql服务,创建数据库dreamer-cms将src/main/resources/db/dreamer-cms.sql导入即可。 如果创建dreamer-cms数据库报错提示ERROR 1064 (42000): 数据库名可以通过反引号包裹。 *左右滑动查看更多 漏洞分析 Shiro反序列化 代码分析: pom.xml中发现系统使用shiro。 全局搜索shiroconfig,定位到: *左右滑动查看更多 rememberMeManagerx0;方法中发现aes硬编码key:wGiHplamyXlVB11UXWol8g==。 rememberMeCookiex0;方法中发现关键词:dreamer-cms-rx0; 。 漏洞利用:
修复建议: 升级shiro组件至安全版本,或重新生成一个新的秘钥替换cipherKey,保证唯一且不要泄漏。 Log4j2代码执行 代码分析: 发现存在log4j2依赖。 src/main/resources/log4j2.yml看一下日志保存的情况。 通过搜索关键词log.error发现src/main/java/cn/itechyou/cms/websocket/WebSocketServer.java中存在多个日志写入内容可控的参数。 查看代码发现,获取目标用户的toUserId,并检查是否存在对应的WebSocket连接(通过webSocketMap进行查找)。 如果存在对应的WebSocket连接,将解析得到的JSON对象转换为字符串并通过该WebSocket连接发送给目标用户。 如果不存在对应的WebSocket连接,通过日志打印错误信息,表示请求的toUserId不在当前服务器上。 漏洞利用: 了解了上面的逻辑,构造起来就比较方便了,我们通过浏览器插件WebSocket Test Client进行测试。 打个断点看一下情况。 dnslog成功收到请求。 修复建议: 将Log4j2组件升级到安全版本。 未授权文件上传 代码分析: 通过搜索发现 src/main/java/cn/itechyou/cms/controller/admin/UploadController.java是存在上传功能,对应的路由为/upload/uploadFilex0; src/main/java/cn/itechyou/cms/security/config/ShiroConfiguration.java查到对应路由的鉴权。发现/**是不需要去进行权限验证的。 除未进行鉴权外,发现其保存文件的后缀名也是直接获取原文件的后缀名。 漏洞利用: 构造上传报文进行利用,不带cookie也是可以成功上传的,遗憾的是springboot默认不解析jsp文件。 修复建议: 建议在后端服务器对用户请求进行响应前,对用户会话和权限进行验证。 限制用户上传附件的类型。 任意文件下载 代码分析: 在src/main/java/cn/itechyou/cms/controller/admin/ AttachmentController.java中找到download一处下载操作,下载的内容是: *左右滑动查看更多 我们去看一下attachment是怎么来的。 发现此java文件中的add方法中保存了attachment。 src/main/java/cn/itechyou/cms/entity/Attachment.java中看到如果如果传入的filepath不为null,则使用trim()方法修剪传入的filepath,去除前后的空格,并将结果赋值给this.filepath属性。可以看到是没有任何过滤的。 漏洞利用: 首先在盘符的根目录新建1.txt 。 附件管理处上传文件后,点击确定抓取数据包。
修改filepath的值为....................1.txt。 我们在save方法处打上断点,可以看到此时filepath的值。 保存完毕后,点击下载预览,即可读取到1.txt。
修复建议: 对filepath入参时进行特殊符号过滤,可参考以下代码: *左右滑动查看更多 任意文件删除 代码分析: 了解了上面任意文件下载的原理,此处任意文件删除也是一样的,查到对应的attachment后直接将其删除。 利用add方法中将attachment.filepath绑定上指定的文件时,利用file.delete();进行删除。 漏洞利用: 通过上面文件文件下载的方式修改好对应的filepath,点击删除即可。之后再去磁盘查找我们存放的文件,发现已经被删除了。 修复建议: 对filepath入参时进行特殊符号过滤,参考方法同上。 解压缩路径穿越 代码分析: src/main/java/cn/itechyou/cms/controller/admin/ThemesController.java中发现获取压缩包后直接进行了解压操作。 我们可以构造恶意的压缩包,将我们文件进行跨目录上传。 漏洞利用: 先上传一个正常压缩包查看一下正常的保存路径: *左右滑动查看更多 之后通过工具制作下压缩包→test.zip。
向上跳了6层目录,之后应该写入到/xxxx/dreamer_cms-v3.0.1/目录中,我们打个断点去测试一下。 上传刚刚制作的test.zip后,点击确定。 断点处查看到zipEntryName的值为../../../../../../1.txt,之后outPathx0;为原本的资源目录拼接刚刚的zipEntryName,即 *左右滑动查看更多 完成后发现成功解压至项目根目录中。 修复建议
下载地址:
https://gitee.com/iteachyou/dreamer_cms/releases
审计版本:
Previous Releases 3.0.1
搭建说明:
http://cms.iteachyou.cc/article/07d10ba665644d40ba558b0fe3d4831f
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
create database `dreamer-cms`;
mysql> source E:xxxdreamer_cms-v3.0.1srcmainresourcesdbdreamer-cms.sql
src/main/java/cn/itechyou/cms/security/config/ShiroConfiguration.java
fileConfiguration.getResourceDir() + system.getUploaddir() + attachment.getFilepath();
import java.nio.file.Path;
import java.nio.file.Paths;
public class FileUtil {
private static final String BASE_DIRECTORY = "/path/to/base_directory/";
public static Path getSafeFilePath(String userProvidedPath) {
String sanitizedPath = filter(userProvidedPath);
Path basePath = Paths.get(BASE_DIRECTORY);
Path filePath = basePath.resolve(sanitizedPath);
// Verify that the resulting path is within the allowed base directory
if (!isPathWithinBaseDirectory(filePath, basePath)) {
throw new SecurityException("Invalid file path");
}
return filePath;
}
public static String filter(String data) {
if (data.contains("../") || data.contains("..\") || data.contains("..%5c") || data.contains("..%2f")) {
throw new SecurityException("Invalid file path");
}
String sanitizedData = data.replaceAll("[\s\\/:\*\?"<>\|]", "");
return sanitizedData;
}
private static boolean isPathWithinBaseDirectory(Path path, Path baseDirectory) {
Path relativePath = baseDirectory.relativize(path);
return !relativePath.startsWith("..");
}
}
/xxxx/dreamer_cms-v3.0.1/src/main/resources/db/dreamer-cms/templates/
/xxxx/dreamer_cms-v3.0.1/src/main/resources/db/dreamer-cms/templates/../../../../../../1.txt
1.验证压缩文件路径,在解压缩之前,验证压缩文件中的每个文件的路径,确保它们仅位于预期的目录中。
2.使用安全的解压缩库或API,确保你使用的解压缩库或API已经修复了Zip Slip漏洞,并提供了安全的路径处理。
3.限制解压缩文件的大小和路径深度。在解压缩之前,检查文件的大小和路径的深度,如果超过你所设定的限制,则拒绝解压缩文件。
安全开发建议
1、关于使用第三方开源组件:
1.版本控制:
确保您的应用程序使用的是组件的稳定版本,并及时更新到新的稳定版本。避免使用不稳定的或已弃用的版本。
2.源代码审查:
如果可能,审查组件的源代码,确保其质量和安全性。这可以帮助您了解组件的实现方式,并确保没有隐藏的安全问题。
3.使用哈希值或包管理工具:
下载组件时,验证其哈希值以确保完整性。对于现代开发,使用包管理工具(如npm、Maven、NuGet等)来管理依赖项会更加方便和安全。
4.持续监控:
定期检查组件的安全公告和更新,及时应用新版本和修复。
5.最小依赖:
只使用您真正需要的组件,避免不必要的依赖,减少潜在的安全风险和复杂性。
2、关于文件操作类的功能:
1.输入验证和过滤:
对于上传的文件,始终进行严格的输入验证和数据过滤。验证文件类型、大小和扩展名,确保只接受合法的文件格式和大小。
2.文件存储安全:
将上传的文件存储在安全的位置,并确保访问权限受到限制。避免将上传的文件保存在Web根目录下,以防止直接访问。
3.防止文件执行:
上传的文件名不应该包含任何可执行的代码,以防止恶意代码执行。最好将上传的文件重命名为随机生成的名称,而不要使用用户提供的文件名。
4.文件类型检查:
通过文件头部检查或使用文件类型库来验证文件类型,而不是仅依赖于文件扩展名。攻击者可能伪造文件扩展名,因此仅仅依赖扩展名不是安全的做法。
5.文件下载时的权限控制:
确保在文件下载时,只有经过授权的用户可以访问和下载文件。对下载使用身份验证和授权机制来限制用户对敏感文件的访问。对下载的文件名做严格过滤避免出现跨目录下载的情况。
6.限制文件大小:
限制上传文件的大小,以防止用户上传过大的文件导致服务器资源耗尽或拒绝服务(DoS)攻击。
7.日志记录:
记录所有的文件上传和下载操作,以便在出现问题时进行审计和跟踪。
参考文章及推荐阅读:
Dreamer CMS 代码审计
https://forum.butian.net/share/2183
原文始发于微信公众号(安恒信息安全服务):九维团队-绿队(改进)| 梦想家CMS审计记录
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论