From 4f27dba2a5713edbe153b77beb7249cb447f6eb4 Mon Sep 17 00:00:00 2001 From: exploide Date: Wed, 25 Dec 2024 12:26:16 +0100 Subject: [PATCH] changepasswd.py: improved error handling --- examples/changepasswd.py | 67 ++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/examples/changepasswd.py b/examples/changepasswd.py index d18fcfdca2..50cc2f004e 100755 --- a/examples/changepasswd.py +++ b/examples/changepasswd.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # Impacket - Collection of Python classes for working with network protocols. # -# Copyright Fortra, LLC and its affiliated companies +# Copyright Fortra, LLC and its affiliated companies # # All rights reserved. # @@ -126,12 +126,14 @@ from impacket import version from impacket.dcerpc.v5 import transport, samr, epm -from impacket.krb5 import kpasswd +from impacket.krb5 import kerberosv5, kpasswd from impacket.ldap import ldap, ldapasn1 from impacket.examples import logger from impacket.examples.utils import parse_target +import OpenSSL + EMPTY_LM_HASH = "aad3b435b51404eeaad3b435b51404ee" @@ -287,12 +289,12 @@ def _changePassword( aesKey=self.aesKey, kdcHost=self.kdcHost, ) - except kpasswd.KPasswdError as e: + except (kerberosv5.KerberosError, kpasswd.KPasswdError) as e: logging.error(f"Password not changed: {e}") return False - else: - logging.info("Password was changed successfully.") - return True + + logging.info("Password was changed successfully.") + return True def _setPassword(self, targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT): if not newPassword: @@ -312,10 +314,12 @@ def _setPassword(self, targetUsername, targetDomain, newPassword, newPwdHashLM, aesKey=self.aesKey, kdcHost=self.kdcHost, ) - except kpasswd.KPasswdError as e: + except (kerberosv5.KerberosError, kpasswd.KPasswdError) as e: logging.error(f"Password not changed for {targetDomain}\\{targetUsername}: {e}") - else: - logging.info(f"Password was set successfully for {targetDomain}\\{targetUsername}.") + return False + + logging.info(f"Password was set successfully for {targetDomain}\\{targetUsername}.") + return True class SamrPassword(PasswordHandler): @@ -414,6 +418,10 @@ def connect(self, retry_if_expired=False): ) logging.debug(str(e)) return False + elif "STATUS_ACCOUNT_DISABLED" in str(e): + logging.critical("The account is currently disabled.") + logging.debug(str(e)) + return False else: raise e @@ -442,6 +450,10 @@ def hSamrOpenUser(self, username): ) logging.debug(str(e)) return False + elif "STATUS_ACCESS_DENIED" in str(e): + logging.critical("Access denied") + logging.debug(str(e)) + return False else: raise e @@ -541,7 +553,7 @@ def _changePassword( targetUsername, oldPassword, "", oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ) if res: - logging.warning("User will need to change their password on next logging because we are using hashes.") + logging.warning("User might need to change their password at next logon because we set hashes (unless password never expires is set).") return res def _setPassword(self, targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT): @@ -569,7 +581,7 @@ def _changePassword( logging.warning( "MS-RPC transport requires new password in plaintext in default Active Directory configuration. Trying anyway." ) - super()._changePassword( + return super()._changePassword( targetUsername, targetDomain, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ) @@ -577,7 +589,7 @@ def _setPassword(self, targetUsername, targetDomain, newPassword, newPwdHashLM, logging.warning( "MS-RPC transport does not allow password reset in default Active Directory configuration. Trying anyway." ) - super()._setPassword(targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT) + return super()._setPassword(targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT) class SmbPassword(SamrPassword): @@ -615,7 +627,7 @@ def connect(self, targetDomain): self.aesKey, kdcHost=self.kdcHost, ) - except ldap.LDAPSessionError as e: + except (ldap.LDAPSessionError, OpenSSL.SSL.SysCallError) as e: logging.error(f"Cannot connect to {ldapURI} as {self.domain}\\{self.username}: {e}") return False @@ -872,7 +884,12 @@ def parse_args(): elif options.no_pass: logging.info("Current password not given: will use KRB5CCNAME") else: - oldPassword = getpass("Current password: ") + try: + oldPassword = getpass("Current password: ") + except KeyboardInterrupt: + print() + logging.warning("Cancelled") + sys.exit(130) if options.newhashes is not None: newPassword = "" @@ -887,10 +904,15 @@ def parse_args(): newPwdHashLM = "" newPwdHashNT = "" if options.newpass is None: - newPassword = getpass("New password: ") - if newPassword != getpass("Retype new password: "): - logging.critical("Passwords do not match, try again.") - sys.exit(1) + try: + newPassword = getpass("New password: ") + if newPassword != getpass("Retype new password: "): + logging.critical("Passwords do not match, try again.") + sys.exit(1) + except KeyboardInterrupt: + print() + logging.warning("Cancelled") + sys.exit(130) else: newPassword = options.newpass @@ -949,7 +971,7 @@ def parse_args(): # Attempt the password change/reset if options.reset: - handler.setPassword(targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT) + ret = handler.setPassword(targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT) else: if (authDomain, authUsername) != (targetDomain, targetUsername): logging.warning( @@ -957,6 +979,11 @@ def parse_args(): "You may want to use '-reset' to *reset* the password of the target." ) - handler.changePassword( + ret = handler.changePassword( targetUsername, targetDomain, oldPassword, newPassword, oldPwdHashLM, oldPwdHashNT, newPwdHashLM, newPwdHashNT ) + + if ret: + sys.exit(0) + else: + sys.exit(1)