From 909d1fc677a5ba01da9e98aaef8fed24fc857953 Mon Sep 17 00:00:00 2001 From: gabrielg5 Date: Thu, 9 Oct 2025 20:00:19 -0300 Subject: [PATCH 1/5] Checking CommonName before setting in the CSR. Enhancing certificate name when username is empty --- .../attacks/httpattacks/adcsattack.py | 72 +++++++++++++++---- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py index ce7e7ba53..2aee98352 100644 --- a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py +++ b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py @@ -20,9 +20,11 @@ import os from OpenSSL import crypto +from cryptography import x509 from cryptography.hazmat.primitives.serialization import pkcs12 from cryptography.hazmat.primitives.serialization import NoEncryption -from cryptography.x509 import load_pem_x509_certificate, load_der_x509_certificate +from cryptography.x509 import ExtensionNotFound, load_pem_x509_certificate, load_der_x509_certificate +from cryptography.x509.oid import NameOID, ObjectIdentifier from cryptography.hazmat.backends import default_backend @@ -34,6 +36,7 @@ class ADCSAttack: + UPN_OID = ObjectIdentifier("1.3.6.1.4.1.311.20.2.3") def _run(self): key = crypto.PKey() @@ -85,17 +88,20 @@ def _run(self): LOG.info("GOT CERTIFICATE! ID %s" % certificate_id) certificate = response.read().decode() - certificate_store = self.generate_pfx(key.to_cryptography_key(), certificate) - LOG.info("Writing PKCS#12 certificate to %s/%s.pfx" % (self.config.lootdir, self.username)) + cert_obj = load_pem_x509_certificate(certificate.encode(), backend=default_backend()) + pfx_filename = self._sanitize_filename(self.username or self._extract_certificate_identity(cert_obj)) + certificate_store = self.generate_pfx(key.to_cryptography_key(), cert_obj) + output_path = os.path.join(self.config.lootdir, "{}.pfx".format(pfx_filename)) + LOG.info("Writing PKCS#12 certificate to %s" % output_path) try: if not os.path.isdir(self.config.lootdir): os.mkdir(self.config.lootdir) - with open("%s/%s.pfx" % (self.config.lootdir, self.username), 'wb') as f: + with open(output_path, 'wb') as f: f.write(certificate_store) LOG.info("Certificate successfully written to file") except Exception as e: LOG.info("Unable to write certificate to file, printing B64 of certificate to console instead") - LOG.info("Base64-encoded PKCS#12 certificate of user %s: \n%s" % (self.username, base64.b64encode(certificate_store).decode())) + LOG.info("Base64-encoded PKCS#12 certificate (%s): \n%s" % (pfx_filename, base64.b64encode(certificate_store).decode())) pass if self.config.altName: @@ -105,12 +111,13 @@ def _run(self): def generate_csr(key, CN, altName, csr_type = crypto.FILETYPE_PEM): LOG.info("Generating CSR...") req = crypto.X509Req() - req.get_subject().CN = CN + + 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") @@ -118,11 +125,13 @@ def generate_csr(key, CN, altName, csr_type = crypto.FILETYPE_PEM): @staticmethod def generate_pfx(key, certificate, cert_type=crypto.FILETYPE_PEM): - - if cert_type == crypto.FILETYPE_PEM: - cert=load_pem_x509_certificate(certificate.encode(), backend=default_backend()) - else: #ASN1/DER - cert=load_der_x509_certificate(certificate.encode(), backend=default_backend()) + if isinstance(certificate, x509.Certificate): + cert = certificate + else: + if cert_type == crypto.FILETYPE_PEM: + cert = load_pem_x509_certificate(certificate.encode(), backend=default_backend()) + else: # ASN1/DER + cert = load_der_x509_certificate(certificate.encode(), backend=default_backend()) pfx_data = pkcs12.serialize_key_and_certificates( name=b"", @@ -139,3 +148,42 @@ def generate_certattributes(template, altName): if altName: return "CertificateTemplate:{}%0d%0aSAN:upn={}".format(template, altName) return "CertificateTemplate:{}".format(template) + + @classmethod + def _extract_certificate_identity(cls, cert): + try: + common_names = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME) + for attribute in common_names: + value = attribute.value.strip() + if value: + return value + except Exception: + pass + + try: + san_extension = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName) + san = san_extension.value + for other_name in san.get_values_for_type(x509.OtherName): + if other_name.type_id == cls.UPN_OID: + value = other_name.value + if isinstance(value, bytes): + value = value.decode('utf-8', errors='ignore') + value = value.strip() + if value: + return value + for dns_name in san.get_values_for_type(x509.DNSName): + value = dns_name.strip() + if value: + return value + except ExtensionNotFound: + pass + except Exception: + pass + + return None + + @staticmethod + def _sanitize_filename(name): + sanitized = re.sub(r'[^A-Za-z0-9._-]', '_', name) + sanitized = sanitized.strip("._") + return sanitized From e0f44f0cee1c82d428440782d613e9ae2774b88b Mon Sep 17 00:00:00 2001 From: gabrielg5 Date: Thu, 9 Oct 2025 20:10:30 -0300 Subject: [PATCH 2/5] Adding fallback to certificate name --- impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py index 2aee98352..e2d64466d 100644 --- a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py +++ b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py @@ -89,7 +89,7 @@ def _run(self): certificate = response.read().decode() cert_obj = load_pem_x509_certificate(certificate.encode(), backend=default_backend()) - pfx_filename = self._sanitize_filename(self.username or self._extract_certificate_identity(cert_obj)) + pfx_filename = self._sanitize_filename(self.username or self._extract_certificate_identity(cert_obj) or "certificate_{0}".format(certificate_id)) certificate_store = self.generate_pfx(key.to_cryptography_key(), cert_obj) output_path = os.path.join(self.config.lootdir, "{}.pfx".format(pfx_filename)) LOG.info("Writing PKCS#12 certificate to %s" % output_path) From 7570f8c24bb7e741a7b84cfc101bbd7755e7a4f6 Mon Sep 17 00:00:00 2001 From: gabrielg5 Date: Mon, 13 Oct 2025 16:29:38 -0400 Subject: [PATCH 3/5] Fixing ICPRRPCAttack #2042 --- impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py | 2 +- impacket/examples/ntlmrelayx/attacks/rpcattack.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py index e2d64466d..de2ca62d8 100644 --- a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py +++ b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py @@ -131,7 +131,7 @@ def generate_pfx(key, certificate, cert_type=crypto.FILETYPE_PEM): if cert_type == crypto.FILETYPE_PEM: cert = load_pem_x509_certificate(certificate.encode(), backend=default_backend()) else: # ASN1/DER - cert = load_der_x509_certificate(certificate.encode(), backend=default_backend()) + cert = load_der_x509_certificate(certificate, backend=default_backend()) pfx_data = pkcs12.serialize_key_and_certificates( name=b"", diff --git a/impacket/examples/ntlmrelayx/attacks/rpcattack.py b/impacket/examples/ntlmrelayx/attacks/rpcattack.py index ca66633f5..330e52380 100644 --- a/impacket/examples/ntlmrelayx/attacks/rpcattack.py +++ b/impacket/examples/ntlmrelayx/attacks/rpcattack.py @@ -156,7 +156,7 @@ def _run(self): ELEVATED.append(self.username) - certificate_store = ADCSAttack.generate_pfx(key, certificate, crypto.FILETYPE_ASN1) + certificate_store = ADCSAttack.generate_pfx(key.to_cryptography_key(), certificate, crypto.FILETYPE_ASN1) LOG.info("Writing PKCS#12 certificate to %s/%s.pfx" % (self.config.lootdir, self.username)) try: if not os.path.isdir(self.config.lootdir): From 5bb9471b8e2626d80aa9a3696502c03426e5d642 Mon Sep 17 00:00:00 2001 From: gabrielg5 Date: Mon, 13 Oct 2025 16:43:17 -0400 Subject: [PATCH 4/5] Aligning ICPRRPCAttack with ADCSAttack --- .../attacks/httpattacks/adcsattack.py | 14 +++----------- .../examples/ntlmrelayx/attacks/rpcattack.py | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py index de2ca62d8..27cb78414 100644 --- a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py +++ b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py @@ -23,7 +23,7 @@ from cryptography import x509 from cryptography.hazmat.primitives.serialization import pkcs12 from cryptography.hazmat.primitives.serialization import NoEncryption -from cryptography.x509 import ExtensionNotFound, load_pem_x509_certificate, load_der_x509_certificate +from cryptography.x509 import ExtensionNotFound, load_pem_x509_certificate from cryptography.x509.oid import NameOID, ObjectIdentifier from cryptography.hazmat.backends import default_backend @@ -124,19 +124,11 @@ def generate_csr(key, CN, altName, csr_type = crypto.FILETYPE_PEM): return crypto.dump_certificate_request(csr_type, req) @staticmethod - def generate_pfx(key, certificate, cert_type=crypto.FILETYPE_PEM): - if isinstance(certificate, x509.Certificate): - cert = certificate - else: - if cert_type == crypto.FILETYPE_PEM: - cert = load_pem_x509_certificate(certificate.encode(), backend=default_backend()) - else: # ASN1/DER - cert = load_der_x509_certificate(certificate, backend=default_backend()) - + def generate_pfx(key, certificate): pfx_data = pkcs12.serialize_key_and_certificates( name=b"", key=key, - cert=cert, + cert=certificate, cas=None, encryption_algorithm=NoEncryption() ) diff --git a/impacket/examples/ntlmrelayx/attacks/rpcattack.py b/impacket/examples/ntlmrelayx/attacks/rpcattack.py index 330e52380..7eecf314a 100644 --- a/impacket/examples/ntlmrelayx/attacks/rpcattack.py +++ b/impacket/examples/ntlmrelayx/attacks/rpcattack.py @@ -20,6 +20,8 @@ import random from OpenSSL import crypto +from cryptography.x509 import load_der_x509_certificate +from cryptography.hazmat.backends import default_backend from impacket import LOG from impacket.dcerpc.v5 import tsch, icpr @@ -28,6 +30,7 @@ from impacket.examples.ntlmrelayx.attacks import ProtocolAttack from impacket.examples.ntlmrelayx.attacks.httpattacks.adcsattack import ADCSAttack + PROTOCOL_ATTACK_CLASS = "RPCAttack" # cache already attacked clients @@ -156,19 +159,25 @@ def _run(self): ELEVATED.append(self.username) - certificate_store = ADCSAttack.generate_pfx(key.to_cryptography_key(), certificate, crypto.FILETYPE_ASN1) - LOG.info("Writing PKCS#12 certificate to %s/%s.pfx" % (self.config.lootdir, self.username)) + cert_obj = load_der_x509_certificate(certificate, backend=default_backend()) + pfx_filename = ADCSAttack._sanitize_filename(self.username or ADCSAttack._extract_certificate_identity(cert_obj) or "certificate") + certificate_store = ADCSAttack.generate_pfx(key.to_cryptography_key(), cert_obj) + output_path = os.path.join(self.config.lootdir, "{}.pfx".format(pfx_filename)) + LOG.info("Writing PKCS#12 certificate to %s" % output_path) try: if not os.path.isdir(self.config.lootdir): os.mkdir(self.config.lootdir) - with open("%s/%s.pfx" % (self.config.lootdir, self.username), 'wb') as f: + with open(output_path, 'wb') as f: f.write(certificate_store) LOG.info("Certificate successfully written to file") except Exception as e: LOG.info("Unable to write certificate to file, printing B64 of certificate to console instead") - LOG.info("Base64-encoded PKCS#12 certificate of user %s: \n%s" % (self.username, base64.b64encode(certificate_store).decode())) + LOG.info("Base64-encoded PKCS#12 certificate (%s): \n%s" % (pfx_filename, base64.b64encode(certificate_store).decode())) pass + if self.config.altName: + LOG.info("This certificate can also be used for user : {}".format(self.config.altName)) + class RPCAttack(ProtocolAttack, TSCHRPCAttack): PLUGIN_NAMES = ["RPC"] From 49ea12cb493b828cb1fc19cbe59d1da7c220d475 Mon Sep 17 00:00:00 2001 From: Gabriel Gonzalez Date: Mon, 13 Oct 2025 18:02:46 -0300 Subject: [PATCH 5/5] Update rpcattack.py --- impacket/examples/ntlmrelayx/attacks/rpcattack.py | 1 - 1 file changed, 1 deletion(-) diff --git a/impacket/examples/ntlmrelayx/attacks/rpcattack.py b/impacket/examples/ntlmrelayx/attacks/rpcattack.py index 7eecf314a..3ec88297b 100644 --- a/impacket/examples/ntlmrelayx/attacks/rpcattack.py +++ b/impacket/examples/ntlmrelayx/attacks/rpcattack.py @@ -30,7 +30,6 @@ from impacket.examples.ntlmrelayx.attacks import ProtocolAttack from impacket.examples.ntlmrelayx.attacks.httpattacks.adcsattack import ADCSAttack - PROTOCOL_ATTACK_CLASS = "RPCAttack" # cache already attacked clients