Skip to content

Commit

Permalink
Merge branch 'fortra:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
p0rtL6 authored Jan 10, 2025
2 parents 3b5dffa + ac02e0e commit 7624bc8
Show file tree
Hide file tree
Showing 8 changed files with 510 additions and 56 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)
39 changes: 38 additions & 1 deletion examples/net.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
# python net.py Administrator:password@targetMachine group -name "Domain Admins"
# python net.py Administrator:password@targetMachine computer -name DC$
# python net.py Administrator:password@targetMachine group -name "Domain Admins" -join EvilUs3r
# python net.py Administrator:password@targetMachine user -enable EvilUs3r
# python net.py Administrator:password@targetMachine user -disable EvilUs3r
#
# Author:
# Alex Romero (@NtAlexio2)
Expand Down Expand Up @@ -215,11 +217,32 @@ def Remove(self, name):
self._close_domain()

def _hEnableAccount(self, user_handle):
user_account_control = samr.hSamrQueryInformationUser2(self._dce, user_handle, samr.USER_INFORMATION_CLASS.UserAllInformation)['Buffer']['All']['UserAccountControl']
buffer = samr.SAMPR_USER_INFO_BUFFER()
buffer['tag'] = samr.USER_INFORMATION_CLASS.UserControlInformation
buffer['Control']['UserAccountControl'] = samr.USER_ALL_ADMINCOMMENT
buffer['Control']['UserAccountControl'] = user_account_control ^ samr.USER_ACCOUNT_DISABLED
samr.hSamrSetInformationUser2(self._dce, user_handle, buffer)

def _hDisableAccount(self, user_handle):
user_account_control = samr.hSamrQueryInformationUser2(self._dce, user_handle, samr.USER_INFORMATION_CLASS.UserAllInformation)['Buffer']['All']['UserAccountControl']
buffer = samr.SAMPR_USER_INFO_BUFFER()
buffer['tag'] = samr.USER_INFORMATION_CLASS.UserControlInformation
buffer['Control']['UserAccountControl'] = samr.USER_ACCOUNT_DISABLED | user_account_control
samr.hSamrSetInformationUser2(self._dce, user_handle, buffer)

def SetUserAccountControl(self, name, action):
info = self.Query(name)
domain_handle = self._open_domain()
try:
user_handle = self._get_user_handle(domain_handle, name)
if action == 'enable':
self._hEnableAccount(user_handle)
else:
self._hDisableAccount(user_handle)
finally:
self._close_domain()



class Computer(User):
def __init__(self, smbConnection):
Expand Down Expand Up @@ -358,6 +381,16 @@ def run(self, remoteName, remoteHost):
actionObject.Remove(self.__options.remove)
print("[+] {} account deleted succesfully!".format(self.__action))

elif self.__is_option_present(self.__options, 'enable'):
print("[*] Enabling {} account '{}'".format(self.__action, self.__options.enable))
actionObject.SetUserAccountControl(self.__options.enable, "enable")
print("[+] {} account enabled succesfully!".format(self.__action))

elif self.__is_option_present(self.__options, 'disable'):
print("[*] Disabling {} account '{}'".format(self.__action, self.__options.disable))
actionObject.SetUserAccountControl(self.__options.disable, "disable")
print("[+] {} account disabled succesfully!".format(self.__action))

elif self.__is_option_present(self.__options, 'join'):
print("[*] Adding user account '{}' to group '{}'".format(self.__options.join,self.__options.name))
actionObject.Join(self.__options.name, self.__options.join)
Expand Down Expand Up @@ -466,12 +499,16 @@ def __is_option_present(self, options, option):
user_parser.add_argument('-create', action="store", metavar = "NAME", help='Add new user account to domain/computer.')
user_parser.add_argument('-remove', action="store", metavar = "NAME", help='Remove existing user account from domain/computer.')
user_parser.add_argument('-newPasswd', action="store", metavar = "PASSWORD", help='New password to set for creating account.')
user_parser.add_argument('-enable', action="store", metavar = "NAME", help='Enables account.')
user_parser.add_argument('-disable', action="store", metavar = "NAME", help='Disables account.')

computer_parser = subparsers.add_parser('computer', help='Enumerate all computers in domain level')
computer_parser.add_argument('-name', action="store", metavar = "NAME", help='Display single computer information.')
computer_parser.add_argument('-create', action="store", metavar = "NAME", help='Add new computer account to domain.')
computer_parser.add_argument('-remove', action="store", metavar = "NAME", help='Remove existing computer account from domain.')
computer_parser.add_argument('-newPasswd', action="store", metavar = "PASSWORD", help='New password to set for creating account.')
computer_parser.add_argument('-enable', action="store", metavar = "NAME", help='Enables account.')
computer_parser.add_argument('-disable', action="store", metavar = "NAME", help='Disables account.')

localgroup_parser = subparsers.add_parser('localgroup', help='Enumerate local groups (aliases) of local computer')
localgroup_parser.add_argument('-name', action="store", metavar = "NAME", help='Operate on single specific domain group account.')
Expand Down
2 changes: 1 addition & 1 deletion impacket/dhcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def unpackOptions(self, options):
# size = self.calcUnpackSize(format, options[i+1:])
size = options[i+1]
# print i, name, format, size
value = self.unpack(format, options[i+2:i+2+size])
value = self.unpack(format, bytes(options[i+2:i+2+size]))
answer.append((name, value))
i += 2+size

Expand Down
8 changes: 8 additions & 0 deletions impacket/examples/ntlmrelayx/clients/ldaprelayclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def sendNegotiate(self, negotiateMessage):
if result['result'] == RESULT_SUCCESS:
challenge = NTLMAuthChallenge()
challenge.fromString(result['server_creds'])
self.sessionData['CHALLENGE_MESSAGE'] = challenge
return challenge
else:
raise LDAPRelayClientException('Server did not offer NTLM authentication!')
Expand Down Expand Up @@ -156,6 +157,13 @@ def create_authenticate_message(self):
def parse_challenge_message(self, message):
pass

def keepAlive(self):
# Basic LDAP query to keep the connection alive
self.session.search(search_base='',
search_filter='(objectClass=*)',
search_scope='BASE',
attributes=['namingContexts'])

class LDAPSRelayClient(LDAPRelayClient):
PLUGIN_NAME = "LDAPS"
MODIFY_ADD = MODIFY_ADD
Expand Down
Loading

0 comments on commit 7624bc8

Please sign in to comment.