diff --git a/README.md b/README.md index 278f41e..9d9e137 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,11 @@ Masky has been designed as a Python library. Moreover, a command line interface For both usages, you need first to retrieve the FQDN of a `CA server` and its `CA name` deployed via an ADCS. This information can be easily retrieved via the `certipy find` option or via the Microsoft built-in `certutil.exe` tool. Make sure that the default `User` template is enabled on the targeted CA. -Warning: Masky deploys an executable on each target via a modification of the existing `RasAuto` service. Despite the automated roll-back of its intial `ImagePath` value, an unexpected error during Masky runtime could skip the cleanup phase. Therefore, do not forget to manually reset the original value in case of such unwanted stop. +Warning: Masky deploys an executable on each target via a modification of the existing `RasAuto` service. Despite the automated roll-back of its initial `ImagePath` value, an unexpected error during Masky runtime could skip the cleanup phase. Therefore, do not forget to manually reset the original value in case of such unwanted stop. ### Command line -The following demo shows a basic usage of Masky by targeting 4 remote systems. Its execution allows to collect NT hashes, CCACHE and PFX of 3 distincts domain users from the sec.lab testing domain. +The following demo shows a basic usage of Masky by targeting 4 remote systems. Its execution allows to collect NT hashes, CCACHE and PFX of 3 distinct domain users from the sec.lab testing domain.

Masky CLI demo @@ -107,12 +107,12 @@ def dump_nt_hashes(): target = "192.168.23.130" rslts = m.run(target) - # Check if Masky succesfully hijacked at least a user session - # or if an unexpected error occured + # Check if Masky successfully hijacked at least a user session + # or if an unexpected error occurred if not rslts: return False - # Loop on MaskyResult object to display hijacked users and to retreive their NT hashes + # Loop on MaskyResult object to display hijacked users and to retrieve their NT hashes print(f"Results from hostname: {rslts.hostname}") for user in rslts.users: print(f"\t - {user.domain}\{user.name} - {user.nt_hash}") @@ -169,4 +169,4 @@ The default values are the following: - [Pixis](https://twitter.com/HackAndDo) for the tool [Lsassy](https://github.com/Hackndo/Lsassy) - Incognito tool and its [Metasploit implementation](https://github.com/rapid7/metasploit-payloads/blob/master/c/meterpreter/source/extensions/incognito/) - [S3cur3Th1sSh1t](https://twitter.com/ShitSecure) for the tool [SharpImpersonation](https://github.com/S3cur3Th1sSh1t/SharpImpersonation) and the [associated article](https://s3cur3th1ssh1t.github.io/SharpImpersonation-Introduction/) -- McAfee for their article regarding the [token impersonation techniques](https://www.mcafee.com/enterprise/en-us/assets/reports/rp-access-token-theft-manipulation-attacks.pdf) \ No newline at end of file +- McAfee for their article regarding the [token impersonation techniques](https://www.mcafee.com/enterprise/en-us/assets/reports/rp-access-token-theft-manipulation-attacks.pdf) diff --git a/masky/lib/cert/auth.py b/masky/lib/cert/auth.py index 67b3aea..c9cd242 100644 --- a/masky/lib/cert/auth.py +++ b/masky/lib/cert/auth.py @@ -149,7 +149,7 @@ def authenticate(self, is_key_credential=False): try: self.dc_ip = socket.gethostbyname(self.dc_domain) except: - err_msg = "The provided DC IP is invalid / not set and the domain could not been resolved" + err_msg = "The provided DC IP is invalid/unset and the domain could not be resolved" logger.error(err_msg) self.tracker.last_error_msg = err_msg return False @@ -425,7 +425,7 @@ def kerberos_authentication( if not is_key_credential: logger.result( - f"Gathered NT hash for the user '{domain}\{username}': {nt_hash}" + fr"Gathered NT hash for the user '{domain}\{username}': {nt_hash}" ) self.user.lm_hash = lm_hash self.user.nt_hash = nt_hash diff --git a/masky/lib/results.py b/masky/lib/results.py index 498d8a2..83adf59 100644 --- a/masky/lib/results.py +++ b/masky/lib/results.py @@ -34,7 +34,7 @@ def parse_agent_errors(self, data): users += user.split("'")[1] + " " if users: logger.warning( - f"Fail to retrieve a PEM from the provided template name for the following users: {users}" + f"Failed to retrieve a PEM from the provided template name for the following users: {users}" ) if self.json_data: @@ -57,7 +57,7 @@ def parse_agent_errors(self, data): err_msg = f"The Masky agent execution failed due to the following errors:\n{self.errors}" logger.debug(err_msg) self.tracker.last_error_msg = ( - f"The Masky agent execution failed, probably empty certificates" + f"The Masky agent execution failed, probably due to empty certificates" ) else: self.tracker.last_error_msg = ( diff --git a/masky/lib/smb.py b/masky/lib/smb.py index 2ec7070..199f214 100644 --- a/masky/lib/smb.py +++ b/masky/lib/smb.py @@ -97,18 +97,18 @@ def exec_masky(self, target, ca, template): self.__command = f'{self.__masky_remote_path} /ca:"{ca}" /template:"{template}" /output:"{self.__results_remote_path}" /debug:"{self.__errors_remote_path}"' self.__upload_masky(target) logger.debug( - f"Masky agent was successfuly uploaded in: '{self.__masky_remote_path}'" + f"Masky agent was successfully uploaded in: '{self.__masky_remote_path}'" ) except Exception as e: err_msg = None if "STATUS_ACCESS_DENIED" in str(e): - err_msg = f"The user {self.__domain}\{self.__username} is not local administrator on this system" + err_msg = fr"The user {self.__domain}\{self.__username} is not a local administrator on this system" logger.warn(err_msg) elif "STATUS_LOGON_FAILURE" in str(e): - err_msg = f"The provided credentials for the user '{self.__domain}\{self.__username}' are invalids or the user does not exist" + err_msg = fr"The provided credentials for the user '{self.__domain}\{self.__username}' are invalid or the user does not exist" logger.error(err_msg) else: - err_msg = f"Fail to upload the agent ({str(e)})" + err_msg = f"Failed to upload the agent ({str(e)})" logger.error(err_msg) self.__tracker.last_error_msg = err_msg raise Exception @@ -118,14 +118,14 @@ def exec_masky(self, target, ca, template): if self.__stealth: self.__edit_svc() logger.debug( - f"The service '{self.__svc_name}' was successfuly modified" + f"The service '{self.__svc_name}' was successfully modified" ) else: self.__create_svc() - logger.debug(f"The service '{self.__svc_name}' was successfuly created") + logger.debug(f"The service '{self.__svc_name}' was successfully created") except Exception as e: err_msg = ( - f"Fail to edit or create the '{self.__svc_name}' service via DCERPC" + f"Failed to edit or create the '{self.__svc_name}' service via DCERPC" ) logger.error(err_msg) self.__tracker.last_error_msg = err_msg @@ -208,7 +208,7 @@ def __remove_masky(self, target_host): except: self.__tracker.files_cleaning_success = False logger.warn( - f"Fail to remove Masky agent located in: {self.__masky_remote_path}" + f"Failed to remove Masky agent located in: {self.__masky_remote_path}" ) if self.__file_args: @@ -217,7 +217,7 @@ def __remove_masky(self, target_host): except: self.__tracker.files_cleaning_success = False logger.warn( - f"Fail to remove Masky agent arguments file located in: {self.__args_path}" + f"Failed to remove Masky agent arguments file located in: {self.__args_path}" ) smbclient.close() @@ -258,7 +258,7 @@ def __process_results(self, target_host): except: self.__tracker.files_cleaning_success = False logger.warn( - f"Fail to remove Masky agent output file located in: {self.__results_remote_path}" + f"Failed to remove Masky agent output file located in: {self.__results_remote_path}" ) try: @@ -268,7 +268,7 @@ def __process_results(self, target_host): rslt.parse_agent_errors, ) if rslt.errors: - err_msg = f"The Masky agent execution failed, enable the debugging to display the stacktrace" + err_msg = f"The Masky agent execution failed, enable debugging to display the stacktrace" logger.error(err_msg) except: logger.warn("No Masky agent error file was downloaded") @@ -277,7 +277,7 @@ def __process_results(self, target_host): except: self.__tracker.files_cleaning_success = False logger.warn( - f"Fail to remove Masky agent error file located in: {self.__errors_remote_path}" + f"Failed to remove Masky agent error file located in: {self.__errors_remote_path}" ) if rslt.json_data and len(rslt.json_data) == 0: @@ -292,7 +292,7 @@ def __process_results(self, target_host): return rslt def __init_rpc(self, target_host): - np_bind = f"ncacn_np:{target_host}[\pipe\svcctl]" + np_bind = fr"ncacn_np:{target_host}[\pipe\svcctl]" self.__rpc_con = transport.DCERPCTransportFactory(np_bind) self.__rpc_con.set_dport(self.__port) self.__rpc_con.setRemoteHost(target_host) @@ -382,7 +382,7 @@ def __revert_svc(self): except Exception as e: self.__tracker.svc_cleaning_success = False logger.warn( - f"Fail to revert '{self.__svc_name}' service binary path ({str(e)}])" + f"Failed to revert '{self.__svc_name}' service binary path ({str(e)}])" ) def __remove_svc(self): @@ -394,7 +394,7 @@ def __remove_svc(self): ) except Exception as e: self.__tracker.svc_cleaning_success = False - logger.warn(f"Fail to remove '{self.__svc_name}' service ({str(e)}])") + logger.warn(f"Failed to remove '{self.__svc_name}' service ({str(e)}])") def __clean(self, target_host): try: @@ -415,7 +415,7 @@ def __clean(self, target_host): except Exception as e: self.__tracker.svc_cleaning_success = False logger.warning( - f"An unknown error occured while trying to revert or remove '{self.__svc_name}' ({str(e)})" + f"An unknown error occurred while trying to revert or remove '{self.__svc_name}' ({str(e)})" ) try: scmr.hRControlService( @@ -428,4 +428,4 @@ def __clean(self, target_host): self.__remove_masky(target_host) except Exception as e: self.__tracker.files_cleaning_success = False - logger.warn(f"Fail to remove Masky related files on the target ({str(e)}") + logger.warn(f"Failed to remove Masky related files on the target ({str(e)}") diff --git a/masky/ui/console.py b/masky/ui/console.py index 048bf0f..f02c974 100644 --- a/masky/ui/console.py +++ b/masky/ui/console.py @@ -88,7 +88,7 @@ def start(self): cli_parser = get_cli_args() self.__opts = Options(cli_parser) if not self.__opts.process(): - logger.error("The provided options are invalids") + logger.error("The provided options are invalid") return False self.__run() return True @@ -102,7 +102,7 @@ def stop(self): try: self.__process_results(rslt) except Exception as e: - logger.warn(f"Fail to process results ({str(e)})") + logger.warn(f"Failed to process results ({str(e)})") except KeyboardInterrupt: logger.warn( "Multiple interruption signals were triggered, Masky is forced to exit without properly cleaning currently processed servers" diff --git a/masky/ui/main.py b/masky/ui/main.py index 6a72b97..4c70b73 100644 --- a/masky/ui/main.py +++ b/masky/ui/main.py @@ -10,13 +10,13 @@ def print_banner(): print( - f""" + fr""" __ __ _ | \/ | __ _ ___| | ___ _ | |\/| |/ _` / __| |/ / | | | | | | | (_| \__ \ <| |_| | - |_| |_|\__,_|___/_|\_\\__, | - v{VERSION} |___/ + |_| |_|\__,_|___/_|\_\\__, | + v{VERSION:22s}|___/ """ ) diff --git a/masky/ui/options.py b/masky/ui/options.py index 08be7d9..37fc654 100644 --- a/masky/ui/options.py +++ b/masky/ui/options.py @@ -161,7 +161,7 @@ def get_cli_args(): "-ca", "--certificate-authority", action="store", - help="Certificate Authority Name (SERVER\CA_NAME)", + help=r"Certificate Authority Name (SERVER\CA_NAME)", required=True, ) group_connect.add_argument(