decrypt laps password

main
mpgn 2023-05-17 16:39:11 -04:00
parent 2c282beeef
commit 7a9102b2ee
4 changed files with 50 additions and 213 deletions

View File

@ -1,178 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from impacket.ldap import ldap as ldap_impacket
from impacket.krb5.kerberosv5 import KerberosError
from cme.logger import CMEAdapter
ldap_error_status = {
"1": "STATUS_NOT_SUPPORTED",
"533": "STATUS_ACCOUNT_DISABLED",
"701": "STATUS_ACCOUNT_EXPIRED",
"531": "STATUS_ACCOUNT_RESTRICTION",
"530": "STATUS_INVALID_LOGON_HOURS",
"532": "STATUS_PASSWORD_EXPIRED",
"773": "STATUS_PASSWORD_MUST_CHANGE",
"775": "USER_ACCOUNT_LOCKED",
"50": "LDAP_INSUFFICIENT_ACCESS",
"KDC_ERR_CLIENT_REVOKED": "KDC_ERR_CLIENT_REVOKED",
"KDC_ERR_PREAUTH_FAILED": "KDC_ERR_PREAUTH_FAILED",
}
class LDAPConnect:
def __init__(self, host, port, hostname):
self.logger = None
self.proto_logger(host, port, hostname)
def proto_logger(self, host, port, hostname):
self.logger = CMEAdapter(extra={"protocol": "LDAP", "host": host, "port": port, "hostname": hostname})
def kerberos_login(
self,
domain,
username,
password="",
ntlm_hash="",
aesKey="",
kdcHost="",
useCache=False,
):
lmhash = ""
nthash = ""
if kdcHost is None:
kdcHost = domain
# This checks to see if we didn't provide the LM Hash
if ntlm_hash and ntlm_hash.find(":") != -1:
lmhash, nthash = ntlm_hash.split(":")
elif ntlm_hash:
nthash = ntlm_hash
# Create the baseDN
baseDN = ""
domainParts = domain.split(".")
for i in domainParts:
baseDN += f"dc={i},"
# Remove last ','
baseDN = baseDN[:-1]
try:
ldapConnection = ldap_impacket.LDAPConnection(f"ldap://{kdcHost}", baseDN)
ldapConnection.kerberosLogin(
username,
password,
domain,
lmhash,
nthash,
aesKey,
kdcHost=kdcHost,
useCache=False,
)
# Connect to LDAP
out = f"{domain}{username}:{password if password else ntlm_hash}"
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "389"
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
if str(e).find("strongerAuthRequired") >= 0:
# We need to try SSL
try:
ldapConnection = ldap_impacket.LDAPConnection(f"ldaps://{kdcHost}", baseDN)
ldapConnection.login(
username,
password,
domain,
lmhash,
nthash,
aesKey,
kdcHost=kdcHost,
useCache=False,
)
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
# self.logger.success(out)
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
errorCode = str(e).split()[-2][:-1]
self.logger.fail(
f"{domain}\\{username}:{password if password else ntlm_hash} {ldap_error_status[errorCode] if errorCode in ldap_error_status else ''}",
color="magenta" if errorCode in ldap_error_status else "red",
)
else:
errorCode = str(e).split()[-2][:-1]
self.logger.fail(
f"{domain}\\{username}:{password if password else ntlm_hash} {ldap_error_status[errorCode] if errorCode in ldap_error_status else ''}",
color="magenta" if errorCode in ldap_error_status else "red",
)
return False
except OSError as e:
self.logger.debug(f"{domain}\\{username}:{password if password else ntlm_hash} {'Error connecting to the domain, please add option --kdcHost with the FQDN of the domain controller'}")
return False
except KerberosError as e:
self.logger.fail(
f"{domain}\\{username}:{password if password else ntlm_hash} {str(e)}",
color="red",
)
return False
def auth_login(self, domain, username, password, ntlm_hash):
lmhash = ""
nthash = ""
# This checks to see if we didn't provide the LM Hash
if ntlm_hash and ntlm_hash.find(":") != -1:
lmhash, nthash = ntlm_hash.split(":")
elif ntlm_hash:
nthash = ntlm_hash
# Create the baseDN
baseDN = ""
domainParts = domain.split(".")
for i in domainParts:
baseDN += f"dc={i},"
# Remove last ','
baseDN = baseDN[:-1]
try:
ldapConnection = ldap_impacket.LDAPConnection(f"ldap://{domain}", baseDN, domain)
ldapConnection.login(username, password, domain, lmhash, nthash)
# Connect to LDAP
out = "{domain}\\{username}:{password if password else ntlm_hash}"
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "389"
# self.logger.success(out)
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
if str(e).find("strongerAuthRequired") >= 0:
# We need to try SSL
try:
ldapConnection = ldap_impacket.LDAPConnection(f"ldaps://{domain}", baseDN, domain)
ldapConnection.login(username, password, domain, lmhash, nthash)
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
# self.logger.success(out)
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
errorCode = str(e).split()[-2][:-1]
self.logger.fail(
f"{domain}\\{username}:{password if password else ntlm_hash} {ldap_error_status[errorCode] if errorCode in ldap_error_status else ''}",
color="magenta" if errorCode in ldap_error_status else "red",
)
else:
errorCode = str(e).split()[-2][:-1]
self.logger.fail(
f"{domain}\\{username}:{password if password else ntlm_hash} {ldap_error_status[errorCode] if errorCode in ldap_error_status else ''}",
color="magenta" if errorCode in ldap_error_status else "red",
)
return False
except OSError as e:
self.logger.debug(f"{domain}\\{username}:{password if password else ntlm_hash} {'Error connecting to the domain, please add option --kdcHost with the FQDN of the domain controller'}")
return False

View File

@ -41,7 +41,7 @@ from cme.protocols.smb.smbspider import SMBSpider
from cme.protocols.smb.passpol import PassPolDump
from cme.protocols.smb.samruser import UserSamrDump
from cme.protocols.smb.samrfunc import SamrFunc
from cme.protocols.ldap.smbldap import LDAPConnect
from cme.protocols.ldap.laps import LDAPConnect, LAPSv2Extract
from cme.helpers.logger import highlight
from cme.helpers.misc import *
from cme.helpers.bloodhound import add_user_bh
@ -568,23 +568,34 @@ class smb(connection):
msMCSAdmPwd = ""
sAMAccountName = ""
username = ""
username_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"]}
values = {str(attr["type"]).lower(): attr["vals"][0] for attr in host["attributes"]}
if "mslaps-encryptedpassword" in values:
self.logger.fail("LAPS password is encrypted and currently CrackMapExec doesn't support the decryption...")
return False
elif "mslaps-password" in values:
r = loads(values["mslaps-password"])
msMCSAdmPwd = values["mslaps-encryptedpassword"]
d = LAPSv2Extract(
bytes(msMCSAdmPwd),
username[0] if username else "",
password[0] if password else "",
domain,
ntlm_hash[0] if ntlm_hash else "",
self.args.kerberos,
339)
data = d.run()
r = loads(data)
msMCSAdmPwd = r["p"]
username = r["n"]
username_laps = r["n"]
elif "mslaps-password" in values:
r = loads(str(values["mslaps-password"]))
msMCSAdmPwd = r["p"]
username_laps = r["n"]
elif "ms-mcs-admpwd" in values:
msMCSAdmPwd = values["ms-mcs-admpwd"]
msMCSAdmPwd = str(values["ms-mcs-admpwd"])
else:
self.logger.fail("No result found with attribute ms-MCS-AdmPwd or msLAPS-Password")
logging.debug(f"Host: {sAMAccountName:<20} Password: {msMCSAdmPwd} {self.hostname}")
@ -593,7 +604,7 @@ class smb(connection):
return False
self.username = self.args.laps if not username else username
self.username = self.args.laps if not username_laps else username_laps
self.password = msMCSAdmPwd
if msMCSAdmPwd == "":
@ -615,16 +626,7 @@ class smb(connection):
return self.laps_search(self.args.username, self.args.password, self.args.hash, self.domain)
return True
def kerberos_login(
self,
domain,
username,
password="",
ntlm_hash="",
aesKey="",
kdcHost="",
useCache=False,
):
def kerberos_login( self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
logging.getLogger("impacket").disabled = True
# Re-connect since we logged off
if not self.no_ntlm:

View File

@ -2,19 +2,20 @@
# -*- coding: utf-8 -*-
import binascii
import hashlib
from datetime import datetime
import os
import requests
from datetime import datetime
from pypsrp.client import Client
from impacket.smbconnection import SMBConnection
from impacket.examples.secretsdump import LocalOperations, LSASecrets, SAMHashes
from cme.config import process_secret
from cme.connection import *
from cme.helpers.bloodhound import add_user_bh
from cme.protocols.ldap.smbldap import LDAPConnect
from cme.protocols.ldap.laps import LDAPConnect, LAPSv2Extract
from cme.logger import CMEAdapter
from pypsrp.client import Client
from impacket.examples.secretsdump import LocalOperations, LSASecrets, SAMHashes
class winrm(connection):
def __init__(self, args, db, host):
@ -213,25 +214,36 @@ class winrm(connection):
msMCSAdmPwd = ""
sAMAccountName = ""
username = ""
username_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"]}
values = {str(attr["type"]).lower(): attr["vals"][0] for attr in host["attributes"]}
if "mslaps-encryptedpassword" in values:
self.logger.fail("LAPS password is encrypted and currently CrackMapExec doesn't" " support the decryption...")
return False
from json import loads
msMCSAdmPwd = values["mslaps-encryptedpassword"]
d = LAPSv2Extract(
bytes(msMCSAdmPwd),
username[0] if username else "",
password[0] if password else "",
domain,
ntlm_hash[0] if ntlm_hash else "",
self.args.kerberos,
339)
data = d.run()
r = loads(data)
msMCSAdmPwd = r["p"]
username_laps = r["n"]
elif "mslaps-password" in values:
from json import loads
r = loads(values["mslaps-password"])
r = loads(str(values["mslaps-password"]))
msMCSAdmPwd = r["p"]
username = r["n"]
username_laps = r["n"]
elif "ms-mcs-admpwd" in values:
msMCSAdmPwd = values["ms-mcs-admpwd"]
msMCSAdmPwd = str(values["ms-mcs-admpwd"])
else:
self.logger.fail("No result found with attribute ms-MCS-AdmPwd or" " msLAPS-Password")
self.logger.debug("Host: {:<20} Password: {} {}".format(sAMAccountName, msMCSAdmPwd, self.hostname))
@ -239,7 +251,7 @@ class winrm(connection):
self.logger.fail("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.username = self.args.laps if not username_laps else username_laps
self.password = msMCSAdmPwd
if msMCSAdmPwd == "":

View File

@ -35,7 +35,7 @@ neo4j = "^4.1.1"
pylnk3 = "^0.4.2"
pypsrp = "^0.7.0"
paramiko = "^2.7.2"
impacket = { git = "https://github.com/mpgn/impacket.git", branch = "master" }
impacket = { git = "https://github.com/mpgn/impacket.git", branch = "gkdi" }
dsinternals = "^1.2.4"
xmltodict = "^0.12.0"
terminaltables = "^3.1.0"
@ -51,6 +51,7 @@ masky = "^0.2.0"
sqlalchemy = "^2.0.4"
aiosqlite = "^0.18.0"
pytest = "^7.2.2"
pyasn1-modules = "^0.3.0"
[tool.poetry.dev-dependencies]
flake8 = "*"