decrypt laps password
parent
2c282beeef
commit
7a9102b2ee
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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 == "":
|
||||
|
|
|
@ -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 = "*"
|
||||
|
|
Loading…
Reference in New Issue