diff --git a/modules/auxiliary/scanner/http/owa_login.rb b/modules/auxiliary/scanner/http/owa_login.rb index 45f5cbc944..598a5b7df0 100644 --- a/modules/auxiliary/scanner/http/owa_login.rb +++ b/modules/auxiliary/scanner/http/owa_login.rb @@ -4,6 +4,7 @@ ## require 'msf/core' +require 'rex/proto/ntlm/message' class Metasploit3 < Msf::Auxiliary @@ -23,7 +24,8 @@ class Metasploit3 < Msf::Auxiliary 'Vitor Moreira', 'Spencer McIntyre', 'SecureState R&D Team', - 'sinn3r' + 'sinn3r', + 'Brandon Knight' ], 'License' => MSF_LICENSE, 'Actions' => @@ -67,6 +69,7 @@ class Metasploit3 < Msf::Auxiliary register_advanced_options( [ OptString.new('AD_DOMAIN', [ false, "Optional AD domain to prepend to usernames", '']), + OptBool.new('ENUM_DOMAIN', [ true, "Automatically enumerate AD domain using NTLM authentication", false]), OptBool.new('SSL', [ true, "Negotiate SSL for outgoing connections", true]) ], self.class) @@ -111,18 +114,37 @@ class Metasploit3 < Msf::Auxiliary inbox_path = action.opts['InboxPath'] login_check = action.opts['InboxCheck'] + domain = nil + + if datastore['AD_DOMAIN'] and not datastore['AD_DOMAIN'].empty? + domain = datastore['AD_DOMAIN'] + end + + if ((datastore['AD_DOMAIN'].nil? or datastore['AD_DOMAIN'] == '') and datastore['ENUM_DOMAIN']) + domain = get_ad_domain + end + begin each_user_pass do |user, pass| vprint_status("#{msg} Trying #{user} : #{pass}") - try_user_pass(user, pass, auth_path, inbox_path, login_check, vhost) + try_user_pass({"user" => user, "domain"=>domain, "pass"=>pass, "auth_path"=>auth_path, "inbox_path"=>inbox_path, "login_check"=>login_check, "vhost"=>vhost}) end rescue ::Rex::ConnectionError, Errno::ECONNREFUSED print_error("#{msg} HTTP Connection Error, Aborting") end end - def try_user_pass(user, pass, auth_path, inbox_path, login_check, vhost) - user = datastore['AD_DOMAIN'] + '\\' + user if datastore['AD_DOMAIN'] != '' + def try_user_pass(opts) + user = opts["user"] + pass = opts["pass"] + auth_path = opts["auth_path"] + inbox_path = opts["inbox_path"] + login_check = opts["login_check"] + vhost = opts["vhost"] + domain = opts["domain"] + + user = domain + '\\' + user if domain + headers = { 'Cookie' => 'PBack=0' } @@ -140,7 +162,7 @@ class Metasploit3 < Msf::Auxiliary 'method' => 'POST', 'headers' => headers, 'data' => data - }, 25) + }) rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT print_error("#{msg} HTTP Connection Failed, Aborting") @@ -204,8 +226,50 @@ class Metasploit3 < Msf::Auxiliary end end + def get_ad_domain + urls = ["aspnet_client", + "Autodiscover", + "ecp", + "EWS", + "Microsoft-Server-ActiveSync", + "OAB", + "PowerShell", + "Rpc"] + + domain = nil + + urls.each do |url| + begin + res = send_request_cgi({ + 'encode' => true, + 'uri' => "/#{url}", + 'method' => 'GET', + 'headers' => {"Authorization" => "NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw=="} + }) + rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT + vprint_error("#{msg} HTTP Connection Failed") + next + end + + if not res + vprint_error("#{msg} HTTP Connection Timeout") + next + end + + if res and res.code == 401 and res['WWW-Authenticate'].match(/^NTLM/i) + hash = res['WWW-Authenticate'].split('NTLM ')[1] + domain = Rex::Proto::NTLM::Message.parse(Rex::Text.decode_base64(hash))[:target_name].value().gsub(/\0/,'') + print_good("Found target domain: " + domain) + return domain + end + end + + return domain + end + def msg "#{vhost}:#{rport} OWA -" end end +