Skip to content

Commit 84c0e62

Browse files
committed
Land rapid7#5493, update OWA scanner creds persistence
2 parents 15f9fc5 + 63f584c commit 84c0e62

File tree

1 file changed

+66
-40
lines changed

1 file changed

+66
-40
lines changed

modules/auxiliary/scanner/http/owa_login.rb

Lines changed: 66 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ def initialize
7676
}
7777
)
7878

79-
8079
register_options(
8180
[
8281
OptInt.new('RPORT', [ true, "The target port", 443]),
@@ -128,21 +127,29 @@ def run
128127
each_user_pass do |user, pass|
129128
next if (user.blank? or pass.blank?)
130129
vprint_status("#{msg} Trying #{user} : #{pass}")
131-
try_user_pass({"user" => user, "domain"=>domain, "pass"=>pass, "auth_path"=>auth_path, "inbox_path"=>inbox_path, "login_check"=>login_check, "vhost"=>vhost})
130+
try_user_pass({
131+
user: user,
132+
domain: domain,
133+
pass: pass,
134+
auth_path: auth_path,
135+
inbox_path: inbox_path,
136+
login_check: login_check,
137+
vhost: vhost
138+
})
132139
end
133140
rescue ::Rex::ConnectionError, Errno::ECONNREFUSED
134141
print_error("#{msg} HTTP Connection Error, Aborting")
135142
end
136143
end
137144

138145
def try_user_pass(opts)
139-
user = opts["user"]
140-
pass = opts["pass"]
141-
auth_path = opts["auth_path"]
142-
inbox_path = opts["inbox_path"]
143-
login_check = opts["login_check"]
144-
vhost = opts["vhost"]
145-
domain = opts["domain"]
146+
user = opts[:user]
147+
pass = opts[:pass]
148+
auth_path = opts[:auth_path]
149+
inbox_path = opts[:inbox_path]
150+
login_check = opts[:login_check]
151+
vhost = opts[:vhost]
152+
domain = opts[:domain]
146153

147154
user = domain + '\\' + user if domain
148155

@@ -199,16 +206,13 @@ def try_user_pass(opts)
199206
# Check if the password needs to be changed.
200207
if res.headers['location'] =~ /expiredpassword/
201208
print_good("#{msg} SUCCESSFUL LOGIN. #{elapsed_time} '#{user}' : '#{pass}': NOTE password change required")
202-
report_hash = {
203-
:host => datastore['RHOST'],
204-
:port => datastore['RPORT'],
205-
:sname => 'owa',
206-
:user => user,
207-
:pass => pass,
208-
:active => true,
209-
:type => 'password'}
210-
211-
report_auth_info(report_hash)
209+
report_cred(
210+
ip: datastore['RHOST'],
211+
port: datastore['RPORT'],
212+
service_name: 'owa',
213+
user: user,
214+
password: pass
215+
)
212216
return :next_user
213217
end
214218

@@ -263,17 +267,13 @@ def try_user_pass(opts)
263267

264268
if res.body =~ login_check
265269
print_good("#{msg} SUCCESSFUL LOGIN. #{elapsed_time} '#{user}' : '#{pass}'")
266-
267-
report_hash = {
268-
:host => datastore['RHOST'],
269-
:port => datastore['RPORT'],
270-
:sname => 'owa',
271-
:user => user,
272-
:pass => pass,
273-
:active => true,
274-
:type => 'password'}
275-
276-
report_auth_info(report_hash)
270+
report_cred(
271+
ip: datastore['RHOST'],
272+
port: datastore['RPORT'],
273+
service_name: 'owa',
274+
user: user,
275+
password: pass
276+
)
277277
return :next_user
278278
else
279279
vprint_error("#{msg} FAILED LOGIN. #{elapsed_time} '#{user}' : '#{pass}' (response body did not match)")
@@ -282,14 +282,14 @@ def try_user_pass(opts)
282282
end
283283

284284
def get_ad_domain
285-
urls = ["aspnet_client",
286-
"Autodiscover",
287-
"ecp",
288-
"EWS",
289-
"Microsoft-Server-ActiveSync",
290-
"OAB",
291-
"PowerShell",
292-
"Rpc"]
285+
urls = ['aspnet_client',
286+
'Autodiscover',
287+
'ecp',
288+
'EWS',
289+
'Microsoft-Server-ActiveSync',
290+
'OAB',
291+
'PowerShell',
292+
'Rpc']
293293

294294
domain = nil
295295

@@ -299,7 +299,7 @@ def get_ad_domain
299299
'encode' => true,
300300
'uri' => "/#{url}",
301301
'method' => 'GET',
302-
'headers' => {"Authorization" => "NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw=="}
302+
'headers' => {'Authorization' => 'NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw=='}
303303
})
304304
rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT
305305
vprint_error("#{msg} HTTP Connection Failed")
@@ -314,14 +314,40 @@ def get_ad_domain
314314
if res && res.code == 401 && res.headers.has_key?('WWW-Authenticate') && res.headers['WWW-Authenticate'].match(/^NTLM/i)
315315
hash = res['WWW-Authenticate'].split('NTLM ')[1]
316316
domain = Rex::Proto::NTLM::Message.parse(Rex::Text.decode_base64(hash))[:target_name].value().gsub(/\0/,'')
317-
print_good("Found target domain: " + domain)
317+
print_good("Found target domain: #{domain}")
318318
return domain
319319
end
320320
end
321321

322322
return domain
323323
end
324324

325+
def report_cred(opts)
326+
service_data = {
327+
address: opts[:ip],
328+
port: opts[:port],
329+
service_name: opts[:service_name],
330+
protocol: 'tcp',
331+
workspace_id: myworkspace_id
332+
}
333+
334+
credential_data = {
335+
origin_type: :service,
336+
module_fullname: fullname,
337+
username: opts[:user],
338+
private_data: opts[:password],
339+
private_type: :password
340+
}.merge(service_data)
341+
342+
login_data = {
343+
core: create_credential(credential_data),
344+
last_attempted_at: DateTime.now,
345+
status: Metasploit::Model::Login::Status::SUCCESSFUL,
346+
}.merge(service_data)
347+
348+
create_credential_login(login_data)
349+
end
350+
325351
def msg
326352
"#{vhost}:#{rport} OWA -"
327353
end

0 commit comments

Comments
 (0)