@@ -18,8 +18,7 @@ def initialize
18
18
super (
19
19
'Name' => 'Outlook Web App (OWA) Brute Force Utility' ,
20
20
'Description' => %q{
21
- This module tests credentials on OWA 2003, 2007, 2010, 2013 servers. The default
22
- action is set to OWA 2010.
21
+ This module tests credentials on OWA 2003, 2007, 2010, and 2013 servers.
23
22
} ,
24
23
'Author' =>
25
24
[
@@ -70,7 +69,7 @@ def initialize
70
69
}
71
70
]
72
71
] ,
73
- 'DefaultAction' => 'OWA_2010 ' ,
72
+ 'DefaultAction' => 'OWA_2013 ' ,
74
73
'DefaultOptions' => {
75
74
'SSL' => true
76
75
}
@@ -93,20 +92,21 @@ def initialize
93
92
deregister_options ( 'BLANK_PASSWORDS' , 'RHOSTS' , 'PASSWORD' , 'USERNAME' )
94
93
end
95
94
96
- def run
97
-
98
- vhost = datastore [ 'VHOST' ] || datastore [ 'RHOST' ]
99
-
100
- print_status ( "#{ msg } Testing version #{ action . name } " )
101
-
95
+ def setup
102
96
# Here's a weird hack to check if each_user_pass is empty or not
103
97
# apparently you cannot do each_user_pass.empty? or even inspect() it
104
98
isempty = true
105
99
each_user_pass do |user |
106
100
isempty = false
107
101
break
108
102
end
109
- print_error ( "No username/password specified" ) if isempty
103
+ raise ArgumentError , "No username/password specified" if isempty
104
+ end
105
+
106
+ def run
107
+ vhost = datastore [ 'VHOST' ] || datastore [ 'RHOST' ]
108
+
109
+ print_status ( "#{ msg } Testing version #{ action . name } " )
110
110
111
111
auth_path = action . opts [ 'AuthPath' ]
112
112
inbox_path = action . opts [ 'InboxPath' ]
@@ -204,20 +204,31 @@ def try_user_pass(opts)
204
204
end
205
205
206
206
#No password change required moving on.
207
- reason = res . headers [ 'location' ] . split ( 'reason=' ) [ 1 ]
207
+ unless location = res . headers [ 'location' ]
208
+ print_error ( "#{ msg } No HTTP redirect. This is not OWA 2013, aborting." )
209
+ return :abort
210
+ end
211
+ reason = location . split ( 'reason=' ) [ 1 ]
208
212
if reason == nil
209
213
headers [ 'Cookie' ] = 'PBack=0;' << res . get_cookies
210
214
else
211
215
#Login didn't work. no point on going on.
212
- vprint_error ( "#{ msg } FAILED LOGIN. '#{ user } ' : '#{ pass } '" )
216
+ vprint_error ( "#{ msg } FAILED LOGIN. '#{ user } ' : '#{ pass } ' (HTTP redirect with reason #{ reason } ) " )
213
217
return :Skip_pass
214
218
end
215
219
else
216
- # these two lines are the authentication info
220
+ # The authentication info is in the cookies on this response
217
221
cookies = res . get_cookies
218
- sessionid = 'sessionid=' << cookies . split ( 'sessionid=' ) [ 1 ] . split ( '; ' ) [ 0 ]
219
- cadata = 'cadata=' << cookies . split ( 'cadata=' ) [ 1 ] . split ( '; ' ) [ 0 ]
220
- headers [ 'Cookie' ] = 'PBack=0; ' << sessionid << '; ' << cadata
222
+ cookie_header = 'PBack=0'
223
+ %w( sessionid cadata ) . each do |necessary_cookie |
224
+ if cookies =~ /#{ necessary_cookie } =([^;]*)/
225
+ cookie_header << "; #{ Regexp . last_match ( 1 ) } "
226
+ else
227
+ print_error ( "#{ msg } Missing #{ necessary_cookie } cookie. This is not OWA 2010, aborting" )
228
+ return :abort
229
+ end
230
+ end
231
+ headers [ 'Cookie' ] = cookie_header
221
232
end
222
233
223
234
begin
@@ -236,8 +247,8 @@ def try_user_pass(opts)
236
247
return :abort
237
248
end
238
249
239
- if res . code == 302
240
- vprint_error ( "#{ msg } FAILED LOGIN. '#{ user } ' : '#{ pass } '" )
250
+ if res . redirect?
251
+ vprint_error ( "#{ msg } FAILED LOGIN. '#{ user } ' : '#{ pass } ' (response was a #{ res . code } redirect) " )
241
252
return :skip_pass
242
253
end
243
254
@@ -256,7 +267,7 @@ def try_user_pass(opts)
256
267
report_auth_info ( report_hash )
257
268
return :next_user
258
269
else
259
- vprint_error ( "#{ msg } FAILED LOGIN. '#{ user } ' : '#{ pass } '" )
270
+ vprint_error ( "#{ msg } FAILED LOGIN. '#{ user } ' : '#{ pass } ' (response body did not match) " )
260
271
return :skip_pass
261
272
end
262
273
end
@@ -291,7 +302,7 @@ def get_ad_domain
291
302
next
292
303
end
293
304
294
- if res and res . code == 401 and res [ 'WWW-Authenticate' ] . match ( /^NTLM/i )
305
+ if res && res . code == 401 && res . headers . has_key? ( 'WWW-Authenticate' ) && res . headers [ 'WWW-Authenticate' ] . match ( /^NTLM/i )
295
306
hash = res [ 'WWW-Authenticate' ] . split ( 'NTLM ' ) [ 1 ]
296
307
domain = Rex ::Proto ::NTLM ::Message . parse ( Rex ::Text . decode_base64 ( hash ) ) [ :target_name ] . value ( ) . gsub ( /\0 / , '' )
297
308
print_good ( "Found target domain: " + domain )
0 commit comments