Analysis of CVE-2024-25065: Apache OFBiz Security bypass

Selecting a CERT_IN Empanelled Security Audit Vendor
How to Select a CERT-IN Empanelled Security Audit Vendor
June 19, 2024
Everything You Need to Know About VAPT Reports
June 21, 2024

June 19, 2024

Introduction

CVE-2024-25065 is a vulnerability that exists in Apache OFBiz before version 18.12.12. It is a path traversal vulnerability that allows authentication bypass through the contextPath variable within the hasBasePermission() method.

What is Apache OFBiz ?

Apache OFBiz (Open For Business) is an open-source enterprise resource planning (ERP) and e-commerce system that offers a comprehensive suite of business applications to automate and integrate various business processes. Built using Java and XML, OFBiz is highly customizable and scalable, making it suitable for small to medium-sized enterprises (SMEs) and businesses with complex processes. Its modular design includes ERP modules for accounting, inventory management, manufacturing, order management, and procurement, as well as e-commerce, customer relationship management (CRM), and human resources features. OFBiz supports a robust data model and service-oriented architecture (SOA), allowing for seamless management of business logic and processes. As an Apache Software Foundation project, OFBiz benefits from a large community of developers and extensive documentation, ensuring ongoing support and collaboration. With key technologies like Apache Tomcat, Apache Derby, Freemarker, and Groovy, OFBiz provides an integrated solution for businesses looking to streamline their operations without the need for expensive licensing fees.

Patch Diffing

LoginWorker.java

The commit that patched this vulnerability is in: framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/LoginWorker.java:

The following code block was added:from here, is on

This code aims to ensure that the contextPath variable is a valid, normalized URI string. Normalizing a URI involves processes like removing redundant segments (e.g., . and ..), resolving relative paths, and converting the URI to a standard format.

Clearly, it was a path traversal vulnerability within contextPath as described in the CVE description.

This code aims to ensure that the contextPath variable is a valid, normalized URI string. Normalizing a URI involves processes like removing redundant segments (e.g., . and ..), resolving relative paths, and converting the URI to a standard format.

Clearly, it was a path traversal vulnerability within contextPath as described in the CVE description.

Testing Lab

Since versions before 18.12.12 are vulnerable, most of the previous versions may have issues while building. The one with the fewest issues is 18.12.05, which you can download from here.

  • First we would need OpenJDK-8:
# Download openJDK8

wget https://download.java.net/openjdk/jdk8u41/ri/openjdk-8u41-b04-linux-x64-14_jan_2020.tar.gz

# Extract it

tar -xvf openjdk-8u41-b04-linux-x64-14_jan_2020.tar.gz

# Move it to the JVM folder

sudo mv java-se-8u41-ri /usr/lib/jvm/openjdk-8

# Add it to the PATH

export JAVA_HOME=/usr/lib/jvm/openjdk-8

export PATH=$JAVA_HOME/bin:$PATH

# Set up the update-alternatives system to OpenJDK8

sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/openjdk-8/bin/java 1

sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/openjdk-8/bin/javac 1

# Then choose the OpenJDK8 version

sudo update-alternatives --config java

sudo update-alternatives --config javac

# Verify the installation

java -version

Now, It's time to build our OFBiz version:

#Using the following command

./gradlew cleanAll loadAll

Fix issues

You may face issues during the build process, such as the absence of the gradle-wrapper.jar file.

  • You can solve it using the following command:

gradle wrapper

Or download it manually: 

# Download manually wget https://services.gradle.org/distributions/gradle-6.8.3-all.zip

  • Another issue would be with the ca-certificate:

It can easily be fixed by updating it or re-installation.

sudo apt install –reinstall ca-certificates

Run OFBiz

  • We will run OFBiz in debugging mode:
./gradlew ofbiz --debug-jvm

Another Issue you might face is with the _JAVA_OPTIONS, when we run it in debugging mode:

# So, We will just unset it

unset _JAVA_OPTIONS

or

# Just set it to the default settings

export _JAVA_OPTIONS="-Dawt.useSystemAAFontSettings=on -Dswing.aatext=true"

It’s ready for our analysis & the debugging port is on 5005.

The Analysis

From the patch diffing, we know that the issue existed in LoginWorker.java, specifically under the hasBasePermission() method. Let’s take a look at this method’s code and analyze it:

Static Analysis

Let’s break down the method code:

public static boolean hasBasePermission(GenericValue userLogin, HttpServletRequest request)

This method takes two parameters: GenericValue userLogin, which represents the user’s login information, and HttpServletRequest request, which is the HTTP request object, providing access to request parameters, attributes, and other data.

Security security = (Security) request.getAttribute(“security”);

The method retrieves the Security object from the HttpServletRequest, which will be used for checking permissions.

if (security != null) {

It checks if the Security object is not null. If it’s not null, it will do the following:

ServletContext context = request.getServletContext();

String serverId = (String) context.getAttribute("_serverId");

String contextPath = request.getContextPath();

It retrieves serverId and contextPath. The server ID uniquely identifies the server instance, and the contextPath is necessary for identifying the web application configuration and which endpoint to work on.

if (UtilValidate.isEmpty(contextPath)) {

    contextPath = "/";

}

If the contextPath is empty, it defaults to the root path ("/").

ComponentConfig.WebappInfo info = ComponentConfig.getWebAppInfo(serverId, contextPath);

Here, it will fetch the web application configuration info using the serverId and contextPath for configuration details needed for permission checks.

if (info != null) {

    return hasApplicationPermission(info, security, userLogin);

} else {

    if (Debug.infoOn()) {

        Debug.logInfo("No webapp configuration found for : " + serverId + " / " + contextPath, module);

    }

}

If info is not null, it calls hasApplicationPermission to check if the user has the required permissions for the application. We will discuss the hasApplicationPermission() method later.

} else {

    if (Debug.warningOn()) {

        Debug.logWarning("Received a null Security object from HttpServletRequest", module);

    }

}

Otherwise, it indicates a configuration error, showing that a null Security object was received from HttpServletRequest.

hasApplicationPermission() Method

public static boolean hasApplicationPermission(ComponentConfig.WebappInfo info, Security security, GenericValue userLogin)

This method takes the following parameters: ComponentConfig.WebappInfo info, which holds configuration information for the web application; Security security, the security manager that provides methods to check permissions; and GenericValue userLogin, which holds the user’s login information.

String accessPermission = info.getAccessPermission();

It retrieves the access permission defined in the WebappInfo object. info.getAccessPermission() fetches the permission string that specifies the required access level for the web application.

if (!accessPermission.isEmpty()) {

    return security.hasPermission(accessPermission, userLogin);

}

If accessPermission is not empty, it checks if the user has this specific permission by calling security.hasPermission(accessPermission, userLogin). If the user has the permission, it returns true; otherwise, it returns false.

else {

    String[] var4 = info.getBasePermission();

    int var5 = var4.length;

    for(int var6 = 0; var6 < var5; ++var6) {

        String permission = var4[var6];

            if (!"NONE".equals(permission) && !security.hasEntityPermission(permission, "_VIEW", userLogin)) {

                return false;

            }

        }

If the access permission is empty, info.getBasePermission() returns an array of base permissions, and int var5 = var4.length gets the length of the base permissions array. It iterates over each permission in the base permissions array. Finally, it checks each base permission to determine if the user has the necessary permissions. If the permission is not “NONE” and the user lacks the specific entity permission for the given permission and the action “_VIEW”, it returns false.

Dynamic Analysis

Let’s go ahead and perform the analysis dynamically:

Choose Remote JVM Debug from the Run/Debug Configurations.

Configure it by entering the OFBiz server IP and debugging port, which is 5005.

Run the debugger and set a breakpoint on hasBasePermission().

By visiting https://localhost:8443/webtools/control/checkLogin, We can see the login page, We can use admin& ofbiz as username and password:

When we hit login, we will trigger the breakpoint in our debugger:

When we step over to the next line, we see that it got the Security attribute from the request:

As it will check if it’s null, we notice that it’s not null, so it passes the check.

When we step over to the next steps, it does the following:

  • It got the context: ServletContext context = request.getServletContext();
  • The serverId: String serverId = (String) context.getAttribute(“_serverId”);
  • And the most important to us, which is contextPath: String contextPath = request.getContextPath(); It holds the value of /webtools.

After that, it will check the root path /, and since we already have a path, it will not go through it. It will move to ComponentConfig.WebappInfo info = ComponentConfig.getWebAppInfo(serverId, contextPath); Here it fetches the info:

We see the info after it’s obtained. We see contextRoot holds the webtools endpoint, and the location webapp/webtools, etc.

Since the info is not null, it will do:

return hasApplicationPermission(info, security, userLogin);

When we step over it, we will see the following:

It jumps into doMainLogin() method, specifically on the following line:

else if (userLogin != null && hasBasePermission(userLogin, request))

And completes performing the normal login request. After login, we see:

Check with limited permissions user

Now, We will use the bizadmin  with the password ofbiz, and we will try to access https://localhost:8443/partymgr/control/FindSecurityGroup:

We can clearly see we don’t have permissions to view it. Let’s debug it:

After doing the same steps as above, during debugging our login, when it enters the hasApplicationPermission() method, it starts to retrieve the permissions through String accessPermission = info.getAccessPermission();.

We see that the accessPermission is empty, so it goes through the permissions array. When we step over/into for a while:

It reaches the hasEntityPermission() method, which checks the permission we have and the required permission to access adminPermission:

It keeps iterating through the array:

As our permissions don’t match, it denies our request and doesn’t show us the page.

Exploitation

Now, as there is no normalization on the contextPath, let’s intercept the request in BurpSuite and then check it through the debugger when we provide a traversal path:

Here we will log in to /partymgr/control/login, but we add our path traversal to indicate /webtools/control/login:

As it reaches our breakpoint again, we see the requested contextPath clearly.

We pass this check as it has a valid URI.

When we reach the contextPath, we see that it accepts our traversal path.

When it attempts to get the appInfo, it returns null due to the contextPath.

Then, it returns true and lets us log in to webtools:

  • On the browser

Conclusion

That’s not all, as you may still encounter errors when accessing other endpoints. However, if you perform actions on those endpoints with any low-permissions user while abusing the path traversal, even if you receive an error, the action still gets executed. By exploiting this vulnerability, attackers can gain unauthorized access to various functionalities and data within the application. To mitigate this issue, update to Apache OFBiz version 18.12.12 or later, where the vulnerability has been patched by normalizing the contextPath variable to ensure it is a valid URI.

References

Enable Notifications OK No thanks