“Apache Unomi is a Java Open Source customer data platform, a Java server designed to manage customers, leads and visitors’ data and help personalize customers experiences,” according to its website. Unomi can be used to integrate personalization and profile management within very different systems such as CMSs, CRMs, Issue Trackers, native mobile applications, etc. Unomi was announced to be a Top-Level Apache product
in 2019 and is made with high scalability and ease of integration in mind.
Given that Unomi contains an abundance of data and features tight integrations with other systems, making it a highly desired target for attackers, the Checkmarx Security Research Team analyzed the platform to uncover potential security issues. The findings are detailed below.
Executive Summary: CVE-2020-13942
What We Found
Apache Unomi allowed remote attackers to send malicious requests with MVEL and OGNL expressions that could contain arbitrary classes, resulting in Remote Code Execution (RCE) with the privileges of the Unomi application. MVEL and OGNL expressions are evaluated by different classes inside different internal packages of the Unomi package, making them two separate vulnerabilities. The severity of these vulnerabilities is heightened since they can be exploited through a public endpoint, which should be kept public by design for the application to function correctly, with no authentication, and no prior knowledge on the attacker's part.
Both vulnerabilities, designated as CVE-2020-13942
, have a CVS Score of 10.0 (Critical)
as they lead to complete compromise of the Unomi service's confidentiality, integrity, and accessibility, in addition to allowing access to the underlying OS.
Previous RCE Found in Unomi
Unomi offers a restricted API that allows retrieving and manipulating data, in addition to a public endpoint where applications can upload and retrieve user data. Unomi allows complex conditions in the requests to its endpoints.
Unomi conditions rely on expression languages (EL), such as OGNL or MVEL, to allow users to craft complex and granular queries. The EL-based conditions are evaluated before accessing data in the storage.
In the versions prior to 1.5.1, these expression languages were not restricted at all—leaving Unomi vulnerable to RCE via Expression Language Injection. An attacker was able to execute arbitrary code, and OS commands on the Unomi server by sending a single request. This vulnerability was classified as CVE-2020-11975
and was fixed. However, due to further investigation by the Checkmarx Security Research Team, we discovered that the fix is not sufficient and can be trivially bypassed.
Patch Not Sufficient – New Vulnerabilities Discovered
The patch for CVE-2020-11975 introduced SecureFilteringClassLoader, which checks the classes used in the expressions against an allowlist and a blocklist. The SecureFilteringClassLoader relies on the assumption that every class in both MVEL and OGNL expressions is loaded using the loadClass() method of the ClassLoader class. The SecureFilteringClassLoader overrides the ClassLoader loadClass method and introduces the allowlist and blocklist checks. This assumption happened to be incorrect. There are multiple ways of loading a class other than calling the loadClass() method, which leads to the security control bypass and leaves Unomi open to RCE.
First, the MVEL expressions in some cases use already instantiated classes, like Runtime or System, without calling loadClass(). This results in the latest version of Unomi (1.5.1) allowing the evaluation of MVEL expressions inside the condition, which contains arbitrary classes.
The following HTTP request has a condition with a parameter containing a MVEL expression (script::Runtime r = Runtime.getRuntime(); r.exec(\"touch /tmp/POC\");).
Unomi parses the value and executes the code after script:: as an MVEL expression. The expression in the example below creates a Runtime object and runs a "touch" OS command, which creates an empty file in /tmp directory.
Second, there is a way to load classes inside OGNL expressions without triggering the loadClass() call. The following HTTP request gets Runtime and executes an OS command using Java reflections API.
The payload may look scary but it’s simply Runtime r = Runtime.getRuntime(); r.exec(“touch /tmp/POC”); written using reflection API and wrapped into OGNL syntax.
Both presented approaches successfully bypass the security control introduced in version 1.5.1, making it vulnerable to RCE in two different locations.
Possible Attack Scenarios
Unomi can be integrated with various data storage and data analytics systems that usually reside in the internal network. The vulnerability is triggered through a public endpoint and allows an attacker to run OS commands on the vulnerable server. The vulnerable public endpoint makes Unomi an ideal entry point to corporate networks. Its tight integration with other services also makes it a steppingstone for further lateral movement within an internal network.
Summary of Disclosure and Events
After discovering and validating the vulnerabilities, we notified Apache of our findings and worked with them throughout the remediation process until they informed us everything was appropriately patched.
To learn more about these types of vulnerabilities, OWASP and CWE have descriptions, examples, consequences, and related controls, as shown in the following links:
Additionally, read the code, analyze the fix, and learn how to mitigate similar issues via our interactive CxCodebashing lesson here
Timeline of Disclosure
June 24, 2020 - Vulnerability disclosed to Apache Unomi developers
August 20, 2020 – Code with the mix merged to master branch
November 13, 2020 – version 1.5.2 containing the fixed code is released
November 17, 2020 – public disclosure
The evaluation of user-defined expression language statements is dangerous and hard to constrain. Struts 2 is an excellent example of how hard it is to restrict dynamic OGNL expressions and avoid RCE. These attempts to impose usage restrictions from within/on the EL, rather than restricting tainted EL usage for general purposes, is an iterative approach, rather than a definitive one. Instead, a more reliable means to prevent RCE is to remove the support of arbitrary EL expressions entirely, creating a set of static expressions that rely on dynamic parameters instead.
Static Application Security Testing solutions, like CxSAST
, can detect OGNL injections in source code and prevent this sort of vulnerability from making its way into production. Meanwhile, software composition analysis (SCA) solutions, such as CxSCA
, will have the necessary data about the vulnerable package and will update CxSCA users as soon as the vulnerability is publicly disclosed. To learn how to mitigate similar issues, visit our CxCodebashing lesson here
This type of research is part of the Checkmarx Security Research Team’s ongoing efforts to drive the necessary changes in software security practices among all organizations. Checkmarx is committed to analyzing open source software to help development teams build and deploy more-secure applications. Our database of open source libraries and vulnerabilities is cultivated by the Checkmarx Security Research Team, empowering CxSCA customers with risk details, remediation guidance, and exclusive vulnerabilities that go beyond the NVD
To learn more about this type of RCE vulnerabilities, read our blog
about Struts 2. For more information or to speak to a Checkmarx expert about how to detect, prioritize, and remediate open source risks in your code, contact us