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