1、背景
根据《java代码审计实战》学习进行记录,java代码审计CMS实战。
2、Ofcms下载
可搜索Ofcms1.1.2版本进行下载。下载连接:ofcms: java 版CMS系统、基于java技术研发的内容管理系统、功能:栏目模板自定义、内容模型自定义、多个站点管理、在线模板页面编辑等功能、代码完全开源、MIT授权协议。QQ 群: (2)185948056 (gitee.com)https://gitee.com/oufu/ofcms
3、漏洞审计
3.1、SQL注入
代码位置:src/main/java/com/ofsoft/cms/admin/controller/system/SystemGenerateController.java”文件可能存在 SQL 注入漏洞。
public void create() {
try {
String sql = getPara("sql");
Db.update(sql);
rendSuccessJson();
} catch (Exception e) {
e.printStackTrace();
rendFailedJson(ErrorCode.get("9999"), e.getMessage());
}
}
}
通过getPara("sql")获取参数sql,在方法Db.update中执行。同样往该程序确定路由:/system/ge erate
@Action(path = "/system/generate", viewPath = "system/generate/")
我们代码的位置处于admin目录下的子文件说明该漏洞处罚条件需要登录后验证我们验证一下:通过路由设置sql=UPDATE%20of_cms_api%20SET%20api_url= updatexml(2,concat(0x7e,(version())),0)
调试处理:该处代码没有进行过滤,预编译等技术处理直接执行sql。
3.2、目录遍历漏洞
代码位置:“src/main/java/com/ofsoft/cms/admin/controller/cms/TemplateController.java”文件存在目录 遍历漏洞。
public void getTemplates() {
//当前目录
String dirName = getPara("dir","");
//上级目录
String upDirName = getPara("up_dir","/");
//类型区分
String resPath = getPara("res_path");
//文件目录
String dir = null;
if(!"/".equals(upDirName)){
dir = upDirName+dirName;
}else{
dir = dirName;
}
File pathFile = null;
if("res".equals(resPath)){
pathFile = new File(SystemUtile.getSiteTemplateResourcePath(),dir);
}else {
pathFile = new File(SystemUtile.getSiteTemplatePath(),dir);
}
File[] dirs = pathFile.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory();
}
});
if(StringUtils.isBlank (dirName)){
upDirName = upDirName.substring(upDirName.indexOf("/"),upDirName.lastIndexOf("/"));
}
setAttr("up_dir_name",upDirName);
setAttr("up_dir","".equals(dir)?"/":dir);
setAttr("dir_name",dirName.equals("")?SystemUtile.getSiteTemplatePathName():dirName);
setAttr("dirs", dirs);
/*if (dirName != null) {
pathFile = new File(pathFile, dirName);
}*/
File[] files = pathFile.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return !file.isDirectory() && (file.getName().endsWith(".html") || file.getName().endsWith(".xml")
|| file.getName().endsWith(".css") || file.getName().endsWith(".js"));
}
});
通过pathFile = new File(SystemUtile.getSiteTemplateResourcePath(),dir);传入dir;dir参数直接从String dirName = getPara("dir","");传入;而这个过程及后面对dir和pathfile并没有做其他的过滤操作直接return,那么我们可以登录后台在路由:@Action(path = "/cms/template")下做操作;(路由确认可通过burpsuite访问模板文件得到;结合代码中满足其他几个参数res_path=res&up_dir=&dir=)来确认。
目录遍历需要在访问的url上禁用../目录回溯符来预防该类型漏洞。
3.3、任意文件上传
代码位置:src/main/java/com/ofsoft/cms/admin/controller/cms/TemplateController.java”文件存在任意文件上传漏洞。
/**
* 保存模板
*/
public void save() {
String resPath = getPara("res_path");
File pathFile = null;
if("res".equals(resPath)){
pathFile = new File(SystemUtile.getSiteTemplateResourcePath());
}else {
pathFile = new File(SystemUtile.getSiteTemplatePath());
}
String dirName = getPara("dirs");
if (dirName != null) {
pathFile = new File(pathFile, dirName);
}
String fileName = getPara("file_name");
// 没有用getPara原因是,getPara因为安全问题会过滤某些html元素。
String fileContent = getRequest().getParameter("file_content");
fileContent = fileContent.replace("<", "<").replace(">", ">");
File file = new File(pathFile, fileName);
FileUtils.writeString(file, fileContent);
rendSuccessJson();
}
通过FileUtils.writeString(file, fileContent);我们可以写入文件内容;路由地址还是处于@Action(path = "/cms/template");满足参数res_path=res&dirs=%2f&file_name=&file_content=可以写入webshell;通过rendSuccessJson可以了解为json输入我们构造请求:成功写入文件
虽然上传成功但我们这执行命令反馈页面找不到,可见该文件没有执行,我们跟进代码全局搜索json跟进“ src/main/java/com/ofsoft/cms/core/handler/ActionHandler.java ”可以发 现,程序将 jsp、html、json 等后缀都进行了置空,而 static 目录则是直接 return,那么,我们只需将 WebShell 上传至 static 目录即可正常访问,这里我们可以通过路径 穿越的方式将文件写入到其他目录,最终的 Payload 如下:
找到文件,重新给cmd传值执行成功。
3.4、模板注入
代码位置:根据官网介绍知道其模板引擎采用的是 freemarker。根据该模板引擎的语法,我们可 以通过以下方式进行任意代码执行:<#assidn ex="freemarker.template.utility.Execute"?new()>${ex("ipconfig")}
直接在对应的模板文件中加入该代码执行语句,访问该文件查看效果。
3.5、存储型XSS
对于一般的 XSS 漏洞,使用黑盒的手法可以快速地发现存在可控输入/输出的位置及 过滤的关键字。在 ofcms 中通过后台添加公告可以测出一个存在首页的存储型 XSS 漏 洞。
我这总是添加不了。
添加成功效果
代码位置:“admin/controller/ComnController.java”文件中的 update 方法插入后直接做了Db.update操作,和sql注入一样没有做过滤操作。
public void update() {
Map<String, Object> params = getParamsMap();
try {
SqlPara sql = Db.getSqlPara(params.get("sqlid").toString(), params);
Db.update(sql);
rendSuccessJson();
} catch (Exception e) {
e.printStackTrace();
rendFailedJson(ErrorCode.get("9999"));
}
}
3.6、CSRF漏洞
观察到们前面的所有 POST 操作似乎都没有进行 token 校验;那么就会产生CSRF漏洞,我们直接用burpsuite生成的poc进行验证。
界面:新增用户
生成测试poc,点击使用浏览器测试
新增成功!!
4、思路
代码审计作为安全SDL中开发阶段不可或缺的一部分,作为企业治理需要每个环节将安全考虑进去才可以降低风险的概率;每个环境都应有相应的指导指南和对应的安全工具以及对应的技术来治理软件生命周期的安全问题。(内容用于学习审计相关知识!!)