Metasploit Module - ora-enum
From Rory.wiki
Oracle enumerator Module
This module is designed to be used post-exploitation to quickly gather information about the database being reviewed. It depends on mc's Oracle Mixin being installed and working properly to function. The code is currently here
#!/usr/bin/env ruby require 'msf/core' require 'msf/core/exploit/oracle' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::ORACLE include Msf::Exploit::FILEFORMAT def initialize(info = {}) super(update_info(info, 'Name' => 'Oracle Enumerator', 'Description' => %q{ This module seeks to get security relevant information from an Oracle Database. Intended to be useful post exploitation, when DBA creds are available. }, 'Author' => [ 'rorym@nmrconsult.net' ], 'License' => MSF_LICENSE, 'Version' => '$Revision:$', 'References' => [ [ 'URL', 'http://www.mccune.org.uk' ], ], 'DisclosureDate' => 'TBD')) register_options( [ OptString.new('FILENAME', [ false, 'File to output results to','oracle_enum.txt' ]), ], self.class) end def run #This first section does the basic queries that don't require any analysis. Then follow-on queries that require some analysis are broken out below. queries = [ ['Oracle User List','select username,account_status,password from dba_users'], ['Oracle Password Policy',"select PROFILE,RESOURCE_NAME,LIMIT from dba_profiles WHERE resource_type = 'PASSWORD'"], ['Oracle version Numbers', "select * from v$version"], ['Oracle Patch Level',"select * from dba_registry_history"] ] output = '' output << "Output from Oracle Enumerator\n------------------\n\n" queries.each do |query| query_title = query[0] query_sql = query[1] print_status("query is " + query_sql) scan_test = connect.prepare(query_sql) scan_test.execute output << "#{query_title}\n-----------\n" output << "Executed query was : #{query_sql} \n-----------\n" scan_test.each do |result| output << "#{result.join(",").to_s}\n" end output << "\n============\n\n" end output << tab_privs_rights_scan output << audit_priv_profile_scan output << sys_privs_rights_scan output << vparameter_scan file_create(output) print_status("output is " + output.length.to_s + " characters long ") end def tab_privs_rights_scan tab_privs_rights_scan_sql = 'select grantee,table_name,privilege from dba_tab_privs' result = '' result << "\nTable Access Rights Scan\n" result << "------------------------\n" result << "Executed query was : #{tab_privs_rights_scan_sql}\n" result << "----------------------\n" tab_privs_test = connect.prepare(tab_privs_rights_scan_sql) tab_rights_array = Array.new print_status("query is " + tab_privs_rights_scan_sql) tab_privs_test.execute tab_privs_test.each do |r| #This next line should remove any grants to the DBA role or the SYS user from the report as they already have full privilege to the system unless r[0] == 'SYS' || r[0] == 'DBA' || r[0] == 'SELECT_CATALOG_ROLE' #This is the list of 'sensitive' tables from the CIS standard #TODO: Need to find some way to clean up this line as it's really ugly if r[1] == 'USER$' || r[1] == 'AUD$' || r[1] == 'USER_HISTORY$' || r[1] == 'LINK$' || r[1] == 'SOURCE$' || r[1] == 'STATS$SQLTEXT' || r[1] == 'STATS$SQL_SUM' || r[1] =~ /^X\$/ || r[1] == 'DBA_ROLES' || r[1] =~ /^V_\$/ || r[1] == 'ALL_SOURCE' || r[1] == 'DBA_SYS_PRIVS' || r[1] == 'DBA_TAB_PRIVS' || r[1] == 'DBA_ROLE_PRIVS' || r[1] == 'DBA_USERS' || r[1] == 'ROLE_ROLE_PRIVS' || r[1] == 'USER_TAB_PRIVS' || r[1] == 'USER_ROLE_PRIVS' tab_rights_array << [r[0], r[1], r[2]] end end end tab_rights_array.sort! tab_rights_array.each do |line| result << "#{line[0]}, #{line[1]},#{line[2]}\n" end return result end def audit_priv_profile_scan audit_priv_profile_scan_sql = 'select * from dba_priv_audit_opts' result = '' result << "\nSystem Privilege Auditing Scan\n" result << "-------------------------\n" result << "Executed query was : #{audit_priv_profile_scan_sql}\n" result << "-------------------------\n" result << "Result Columns are Privilege, User, Success Audited?, Failure Audited?\n" print_status("query is " + audit_priv_profile_scan_sql) audit_privs_test = connect.prepare(audit_priv_profile_scan_sql) audit_privs_test.execute audit_priv_profile_scan_results = Array.new priv_audit_results = Hash.new #Retrieve the contents of the audit table audit_privs_test.each do |r| if !priv_audit_results[r[2]] priv_audit_results[r[2]] = Array.new priv_audit_results[r[2]] << [r[0], r[3], r[4]] else priv_audit_results[r[2]] << [r[0], r[3], r[4]] end end privs_to_audit = ['CREATE SESSION', 'ALTER ANY TABLE', 'ALTER USER','CREATE ROLE', 'CREATE USER','DROP ANY PROCEDURE','DROP ANY TABLE','GRANT ANY PRIVILEGE','GRANT ANY ROLE'] #Compare against system privileges. If the table is empty one result of "not set", "not set" will come back for each privilege privs_to_audit.each do |priv| if !priv_audit_results[priv] priv_audit_results[priv] = Array.new priv_audit_results[priv] << [' ','NOT SET','NOT SET'] end end priv_audit_results.each do |key, value| value.each do |val| result << "#{key}, #{val[0]}, #{val[1]}, #{val[2]}\n" end end return result end def sys_privs_rights_scan sys_privs_rights_scan_sql = "select privilege,grantee from dba_sys_privs where privilege like '%ANY%' or privilege = 'EXEMPT ACCESS POLICY' " result = '' result << "\nSystem Privileges Scan\n" result << "----------------------\n" result << "Executed query was : #{sys_privs_rights_scan_sql}" result << "----------------------\n" access_rights_hash = Hash.new print_status("query is " + sys_privs_rights_scan_sql) sys_privs_test = connect.prepare(sys_privs_rights_scan_sql) sys_privs_test.execute sys_privs_test.each do |r| if !access_rights_hash[r[1]] access_rights_hash[r[1]] = r[0] else access_rights_hash[r[1]] = access_rights_hash[r[1]] + ',' + r[0] end end access_rights_hash.each do |key,value| result << "#{key}, #{value}\n" end return result end def vparameter_scan vparameter_sql = "select name,value from v$parameter" result = '' result << "\nv$parameter checks\n" result << "This returns potential security issues from v$parameter and a reference to the relevent CIS standard section\n" result << "--------------------\n" result << "Executed query was : #{vparameter_sql}\n" parameter_results = Hash.new print_status("query is " + vparameter_sql) vparameter_test = connect.prepare(vparameter_sql) vparameter_test.execute vparameter_test.each do |r| parameter_results[r[0]] = r[1] end #Ok now we have a hash of all the parameters from v$parameter its relatively #straightforward to check against the desired values if parameter_results['global_names'] != 'TRUE' result << "CIS 4.02, GLOBAL_NAMES, #{parameter_results['global_names']}\n" end if parameter_results['max_enabled_roles'].to_i > 30 result << "CIS 4.03, MAX_ENABLED_ROLES, #{parameter_results['max_enabled_roles']}" end if parameter_results['remote_os_authent'] != 'FALSE' result << "CIS 4.04, REMOTE_OS_AUTHENT, #{parameter_results['remote_os_authent']}\n" end if parameter_results['remote_os_roles'] != 'FALSE' result << "CIS 4.05, REMOTE_OS_ROLES, #{parameter_results['remote_os_roles']}\n" end if parameter_results['remote_listener'] result << "CIS 4.06, REMOTE_LISTENER, #{parameter_results['remote_listener']}\n" end if parameter_results['audit_trail'] !=~ /OS|DB|TRUE/ result << "CIS 4.07, AUDIT_TRAIL, #{parameter_results['audit_trail']}\n" end if parameter_results['os_authent_prefix'] result << "CIS 4.08, OS_AUTHENT_PREFIX, #{parameter_results['os_authent_prefix']}\n" end if parameter_results['os_roles'] != 'FALSE' result << "CIS 4.09, OS_ROLES, #{parameter_results['os_roles']}\n" end if parameter_results['utl_file_dir'] result << "CIS 4.10, UTL_FILE_DIR, #{parameter_results['utl_file_dir']}\n" end if parameter_results['sql92_security'] != 'TRUE' result << "CIS 4.13, SQL92_SECURITY, #{parameter_results['sql92_security']}\n" end if parameter_results['o7_dictionary_accessibility'] != 'FALSE' result << "CIS 4.18, O7_DICTIONARY_ACCESSIBILITY, #{parameter_results['o7_dictionary_accessibility']}\n" end if parameter_results['audit_sys_operations'] != 'TRUE' result << "CIS 4.20, AUDIT_SYS_OPERATIONS, #{parameter_results['audit_sys_operations']}\n" end if parameter_results['remote_login_passwordfile'] result << "CIS 4.28, REMOTE_LOGIN_PASSWORDFILE, #{parameter_results['remote_login_passwordfile']}\n" end return result end end
