Skip to content

Commit

Permalink
changepasswd.py: improved error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
exploide committed Dec 25, 2024
1 parent 9c8e408 commit 4f27dba
Showing 1 changed file with 47 additions and 20 deletions.
67 changes: 47 additions & 20 deletions examples/changepasswd.py
Original file line number Diff line number Diff line change
@@ -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.
#
Expand Down Expand Up @@ -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"

Expand Down Expand Up @@ -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:
Expand All @@ -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):
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -569,15 +581,15 @@ 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
)

def _setPassword(self, targetUsername, targetDomain, newPassword, newPwdHashLM, newPwdHashNT):
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):
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 = ""
Expand All @@ -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

Expand Down Expand Up @@ -949,14 +971,19 @@ 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(
f"Attempting to *change* the password of {targetDomain}/{targetUsername} as {authDomain}/{authUsername}. "
"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)

0 comments on commit 4f27dba

Please sign in to comment.