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.
@@ -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(