某开源cms 0day挖掘

admin 2025年4月6日20:11:18评论16 views字数 9064阅读30分12秒阅读模式

 

原文链接:https://xz.aliyun.com/news/17601

作者:用户9528

0x1 简介

简介CicadasCMS是用springboot+mybatis+beetl开发的一款CMS,支持自定义内容模型、模板标签、全站静态化等功能。某开源cms 0day挖掘某开源cms 0day挖掘

0x2 漏洞挖掘

sql注入(成功)漏洞发生位置在com.zhiliao.module.web.cms.ContentController#save:

@RequiresPermissions("content:save")
@RequestMapping("/save")
@ResponseBody
publicStringsave(TCmsContentcontent, HttpServletRequestrequest,
@RequestParam(value="tag",required=false) String[] tags
                      ) throwsSQLException {
TCmsCategorycategory=categoryService.findById(content.getCategoryId());
TCmsModelcmsModel=modelService.findById(category.getModelId());
List<TCmsModelFiled>cmsModelFileds=modelFiledService.findModelFiledListByModelId(category.getModelId());
UserVouserVo=UserUtil.getSysUserVo();
content.setSiteId(userVo.getSiteId());
content.setUserId(userVo.getUserId());
content.setInputdate(newDate());
content.setModelId(category.getModelId());
/*Jin:使用Map接收:遍历获取自定义模型字段*/
Map<String, Object>formParam=Maps.newHashMap();
for (TCmsModelFiledfiled : cmsModelFileds) {
if(filed.getFiledClass().equals("checkbox")||filed.getFiledClass().equals("image")) {
String[] filedValue=request.getParameterValues(filed.getFiledName());
if (filedValue!=null) {
formParam.put(filed.getFiledName(), filedValue);
                }
            }else {
StringfiledValue=request.getParameter(filed.getFiledName());
if (!StrUtil.isBlank(filedValue)) {
formParam.put(filed.getFiledName(), filedValue);
                }
            }
        }
if(content.getContentId()!=null)
returncontentService.update(content,cmsModel.getTableName(),cmsModelFileds,formParam,tags);
returncontentService.save(content,cmsModel.getTableName(),formParam,tags);
    }

save方法接受了一个content对象为参数,这个content对象包含主键contentId等信息,formParam对象为一个新建的hashMap,用于保存表单数据的键值对,表示了一些扩展字段和其对应值,那么在这个逻辑中,如果contentId不为空,则调用com.zhiliao.module.web.cms.service.ContentService#update更新数据,否则调用com.zhiliao.module.web.cms.service.ContentService#save进行数据保存:某开源cms 0day挖掘com.zhiliao.module.web.cms.service.impl.ContentServiceImpl#update(com.zhiliao.mybatis.model.TCmsContent, java.lang.String, java.util.List<com.zhiliao.mybatis.model.TCmsModelFiled>, java.util.Map<java.lang.String,java.lang.Object>, java.lang.String[]):

@CacheEvict(cacheNames="cms-content-cache",allEntries=true)
@Transactional(transactionManager="masterTransactionManager",rollbackFor=Exception.class)
@Override
publicStringupdate(TCmsContentcontent, StringtableName, List<TCmsModelFiled>cmsModelFileds, Map<String, Object>formParam, String[] tags) throwsSQLException {
/*初始化文章的推荐和顶置为false*/
content.setRecommend(CmsUtil.isNullOrEmpty(content.getRecommend())?false:true);
content.setTop(CmsUtil.isNullOrEmpty(content.getTop())?false:true);
content.setUpdatedate(newDate());
if(contentMapper.updateByPrimaryKeySelective(content)>0) {
/*创建lucene索引*/
if(categoryService.findById(content.getCategoryId()).getAllowSearch()) {
IndexObjectindexObject=newIndexObject();
indexObject.setId(content.getContentId().toString());
indexObject.setTitle(content.getTitle());
indexObject.setKeywords(content.getKeywords());
indexObject.setDescription(content.getDescription());
indexObject.setPostDate(DateUtil.formatDateTime(content.getInputdate()));
indexObject.setUrl(this.httpProtocol+"://"+ControllerUtil.getDomain() +"/front/"+content.getSiteId() +"/"+content.getCategoryId() +"/"+content.getContentId());
luceneService.update(indexObject);
            }
/*保存和文章管理的Tag*/
if (tags!=null)
for (Stringtag : tags) {
tagService.save(content.getContentId(), tag);
                }
this.SaveModelFiledParam(formParam,content,tableName,cmsModelFileds);
returnJsonUtil.toSUCCESS("操作成功", "", true);
        }
returnJsonUtil.toERROR("操作失败");
    }

又调用了com.zhiliao.module.web.cms.service.impl.ContentServiceImpl#SaveModelFiledParam进行数据保存:某开源cms 0day挖掘接着跟进com.zhiliao.module.web.cms.service.impl.ContentServiceImpl#SaveModelFiledParam方法:

@Transactional(transactionManager="masterTransactionManager",rollbackFor=Exception.class)
publicintSaveModelFiledParam(Map<String, Object>formParam,TCmsContentcontent,StringtableName,List<TCmsModelFiled>cmsModelFileds) throwsSQLException {
if(!CmsUtil.isNullOrEmpty(formParam)) {
StringexeSql;
Connectionconnection=dataSource.getConnection();
Statementstatement=connection.createStatement();
StringselectSql="select * from t_cms_content_"+tableName+" where content_id="+content.getContentId();
ResultSetresultSet=statement.executeQuery(selectSql);
/*判断内容扩展表是否存在数据*/
if(!resultSet.next()) {
exeSql="insert into t_cms_content_"+tableName.trim() +" set ";
exeSql+="`content_id`="+content.getContentId() +", ";
for (Map.Entry<String, Object>entry : formParam.entrySet()) {
exeSql+="`"+entry.getKey() +"`";
if(CmsUtil.isNullOrEmpty(entry.getValue()))
return0;
if (entry.getValue() instanceofInteger) {
exeSql+="="+entry.getValue() +", ";
                    } elseif (entry.getValue().getClass().isArray()) {
/*遍历字符数组,数组来源为checkbox和多图上传*/
String[] result= (String[]) entry.getValue();
if (result!=null) {
Stringtmp="";
for (Stringvalue : result) {
if(StrUtil.isBlank(value))
continue;
tmp+=value+"#";
                            }
exeSql+="='"+tmp.substring(0, tmp.length() -1) +"', ";
                        }
                    } else {
exeSql+="='"+entry.getValue() +"', ";
                    }
                }
/*执勤Sql前截取最后面的空格和英文逗号,并加上‘;’*/
exeSql=exeSql.substring(0, exeSql.length() -2) +";";
intstatus=statement.executeUpdate(exeSql);
statement.close();
connection.close();
returnstatus;
            }else {
exeSql="UPDATE t_cms_content_"+tableName.trim() +" set ";
/*遍历Map保存扩展数据表,代码有些复杂*/
for (TCmsModelFiledfiled : cmsModelFileds) {
if(CmsUtil.isNullOrEmpty(formParam.get(filed.getFiledName()))) continue;
exeSql+="`"+filed.getFiledName() +"`";
if (formParam.get(filed.getFiledName()) instanceofInteger) {
exeSql+="="+formParam.get(filed.getFiledName()) +", ";
                    } elseif (!CmsUtil.isNullOrEmpty(formParam.get(filed.getFiledName())) &&formParam.get(filed.getFiledName()).getClass().isArray()) {
/*遍历字符数组,数组来源为checkbox和多图上传*/
String[] result= (String[]) formParam.get(filed.getFiledName());
if (result!=null) {
Stringtmp="";
for (Stringvalue : result) {
tmp+=value+"#";
                            }
exeSql+="='"+tmp.substring(0, tmp.length() -1) +"', ";
                        }
                    } else {
exeSql+="='"+formParam.get(filed.getFiledName()) +"', ";
                    }
                }
/*截取最后面的空格和英文逗号,并加上‘;’*/
exeSql=exeSql.substring(0, exeSql.length() -2) +"where `content_id`="+content.getContentId() +";";
intstatus=statement.executeUpdate(exeSql);
statement.close();
connection.close();
returnstatus;
            }
        }
return0;
    }

那么这里执行的逻辑是:首先进行非空判断,接着遍历表单数据并且动态拼接到sql执行语句中,最后进行执行,显然这里是存在sql注入漏洞:某开源cms 0day挖掘文件上传(失败):com.zhiliao.common.upload.UploadComponent#uploadFile:

publicUploadBeanuploadFile(MultipartFilemultipartFile, HttpServletRequestrequest){
TSysAttachmentattachment=newTSysAttachment();
/** 获取用户会话 **/
UserVouserVo= (UserVo)request.getSession().getAttribute(CmsConst.SITE_USER_SESSION_KEY);
if(userVo!=null) {
attachment.setUserId(userVo.getUserId().toString());
attachment.setUsername(userVo.getUsername());
        }
attachment.setUploadDate(newDate());
attachment.setUploadIp(ControllerUtil.getRemoteAddress(request));
attachment.setFileSize(Float.valueOf(multipartFile.getSize())/1024);
attachment.setFileExtname(multipartFile.getContentType());
attachment.setFileKey(UUID.randomUUID().toString().replace("-",""));
attachment.setOriginalFilename(multipartFile.getOriginalFilename());
/*创建uploadBean*/
UploadBeanresult=newUploadBean();
StringfileType=this.getFileType(attachment.getOriginalFilename());
StringfileName=this.getFileName(fileType) ;
StringnewName=this.getNewFileName(fileName);

if (!multipartFile.isEmpty()) {
if (!Boolean.parseBoolean(qiniuUpload)) {
Filefile=newFile(this.getUploadPath() +newName);
/*如果不存在就创建*/
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
                }
try {
this.writeFile(multipartFile.getBytes(), file);
attachment.setFilePath(newName);
attachment.setFileName(fileName);
if(Boolean.parseBoolean(enableFullPath)) {
result.setFileUrl(request.getScheme() +"://"+ControllerUtil.getDomain() +"/res/"+attachment.getFileKey() +"."+fileType);
                    }else {
result.setFileUrl("/res/"+attachment.getFileKey() +"."+fileType);

                    }
attachmentService.save(attachment);
                } catch (Exceptione) {
thrownewApiException(e.getMessage());
                }
            }else {
StringqiniuFileResult=QiniuUtil.upload(accessKey, secretKey, bucketname, multipartFile);
if (!StrUtil.isBlank(qiniuFileResult)) {
StringfileKey=JSON.parseObject(qiniuFileResult).getString("key");
StringfileUrl=domain+"/"+fileKey;
if (StrUtil.getExtensionName(fileName).equals("jpg") ||StrUtil.getExtensionName(fileName).equals("JPG") ||StrUtil.getExtensionName(fileName).equals("png") ||StrUtil.getExtensionName(fileName).equals("PNG") ||StrUtil.getExtensionName(fileName).equals("jpeg") ||StrUtil.getExtensionName(fileName).equals("JPEG")) {
fileUrl+="?imageslim";
                    }
result.setFileUrl(fileUrl);
                }
            }
returnresult;
        }else{
thrownewApiException("上传文件不能为空!");
        }
    }

这里的newName是从this.getNewFileName(fileName);得到的,fileName又是通过this.getFileName(fileType) ;获得的:某开源cms 0day挖掘最开始fileType又是通过this.getFileType(attachment.getOriginalFilename());获得的:某开源cms 0day挖掘那么跟进com.zhiliao.common.upload.UploadComponent#getFileType:

publicStringgetFileType(StringfileName) {
Stringtype=fileName.substring(fileName.lastIndexOf(".") +1);
returntype;
    }

那么这里是使用了lastIndexof函数,这样的话看上去后文件的类型是不可控的。

0x3 总结

这套源码是很老了,整体难度不大,非常适合新手进行学习。

0x4

原文始发于微信公众号(神农Sec):某开源cms 0day挖掘

 

 

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月6日20:11:18
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   某开源cms 0day挖掘https://cn-sec.com/archives/3918407.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息