前言
学了一两个月的Java代码审计,对一些审计有了一定了解了。所以决定审计一下JavaWeb CMS,随便申请一下CVE。
认真严肃的挑选了一波之后,我选择了这个CMS,可能是缘分,也可能是好玩吧。主要看的是这个项目有QQ群,可以加群讨论一下问题,方便更好的研究。先加群不说别的。gitee地址,GitHub地址。
环境搭建
环境的搭建很简单,几种方式可以选择。第一种直接git项目的源码,idea打开项目,然后idea会自动导入下载maven。
第二种方式是去GitHub或者Gitee上下载发行版。
gitee下载地址
github下载地址
任意文件上传漏洞
Arbitrary file upload vulnerability文件上传漏洞存在于管理员后台中的模板管理。
漏洞分析
断点调试,断点设置在E:\Soures\jfinal_cms\src\main\java\com\jflyfox\modules\filemanager\FileManagerController.java
模板页面的操作的都是由FileManangerController.java控制。
HttpServletRequest request = getRequest();
有点Java知识的人都认识这个,所以第一个断点设置在这里。
- 第二个断点,审计的上传漏洞,肯定设置在上传方法里。
漏洞源码
判断是否为空的操作
public JSONObject add() {
Iterator<?> it = this.files.iterator();
if (!it.hasNext()) {
this.error(lang("INVALID_FILE_UPLOAD"));
return null;
}
项目主说这里修改一下就好了,但默认是这样子的,可见开发者自以为是可以防止任意上传文件漏洞,但其实这里默认是这样子设置。
默认设置是一次最多上传5个文件,文件大小不超过16MB。
long maxSize = NumberUtils.parseLong(MAX_SIZE);
if (getConfig("upload-size") != null) {
maxSize = Integer.parseInt(getConfig("upload-size"));
if (maxSize != 0 && item.getSize() > (maxSize * 1024 * 1024)) {
this.error(sprintf(lang("UPLOAD_FILES_SMALLER_THAN"), maxSize + "Mb"));
error = true;
}
}
这里maxSize是默认为0。
下面的一段代码是判断是否只能上传图片,在配置文件E:\Soures\jfinal_cms\src\main\resources\conf\filemanager.properties
下可以看到文件复写和上传文件大小设置是为0的(0代表的是没有限制
),默认是可以上传其他文件(upload-imagesonly=false
)。
if (!isImage(item.getName())
&& (getConfig("upload-imagesonly") != null && getConfig("upload-imagesonly").equals("true") || this.params
.get("type") != null && this.params.get("type").equals("Image"))) {
this.error(lang("UPLOAD_IMAGES_ONLY"));
error = true;
}
if (error) {
break;
}
创建临时文件,后面会用到。作用是先将上传的文件以临时文件的存放着,然后把复制到上传目录下,重新命名删除临时文件。
tmpFile = new File(this.fileRoot + TMP_PATH + "filemanager_" + System.currentTimeMillis() + ".tmp");
File filePath = tmpFile.getParentFile();
if (!filePath.exists()) {
filePath.mkdirs();
}
item.write(tmpFile);
}
}
} catch (Exception e) {
logger.error("INVALID_FILE_UPLOAD", e);
this.error(lang("INVALID_FILE_UPLOAD"));
}
文件上传后的操作,也就是上面说到的复制重命名,最后将临时文件删除。
// file rename
try {
if (!error && tmpFile != null) {
String allowed[] = {".", "-"};
if ("add".equals(params.get("mode"))) {
fileInfo = new JSONObject();
String respPath = "";
String currentPath = "";
String fileName = params.get("_fileName");
String filePath = "";
try {
currentPath = params.get("currentpath");
respPath = currentPath;
currentPath = new String(currentPath.getBytes("ISO8859-1"), "UTF-8"); // 中文转码
currentPath = getFilePath(currentPath);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
filePath = FileManagerUtils.rebulid(this.fileRoot + currentPath);
LinkedHashMap<String, String> strList = new LinkedHashMap<String, String>();
strList.put("fileName", fileName);
fileName = (String) cleanString(strList, allowed).get("fileName");
if (getConfig("upload-overwrite").equals("false")) {
fileName = this.checkFilename(filePath, fileName, 0);
}