Skip to content

Commit 631107d

Browse files
committed
Move cert auth logic to helpers function
1 parent c71e52d commit 631107d

2 files changed

Lines changed: 67 additions & 60 deletions

File tree

nxc/connection.py

Lines changed: 2 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import random
2-
import os
32
import sys
43
import contextlib
54

65
from os.path import isfile
76
from threading import BoundedSemaphore
87
from functools import wraps
98
from time import sleep
10-
from datetime import datetime
119
from ipaddress import ip_address
1210
from dns import resolver, rdatatype
1311
from socket import AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME, getaddrinfo
@@ -18,13 +16,8 @@
1816
from nxc.logger import nxc_logger, NXCAdapter
1917
from nxc.context import Context
2018
from nxc.protocols.ldap.laps import laps_search
21-
from nxc.helpers.pfx import myPKINIT, GETPAC
19+
from nxc.helpers.pfx import pfx_auth
2220

23-
from minikerberos.network.clientsocket import KerberosClientSocket
24-
from minikerberos.common.target import KerberosTarget
25-
from minikerberos.common.ccache import CCACHE
26-
27-
from impacket.krb5.ccache import CCache
2821
from impacket.dcerpc.v5 import transport
2922

3023
sem = BoundedSemaphore(1)
@@ -563,57 +556,7 @@ def login(self):
563556
self.logger.fail("You must specify a username when using certificate authentication")
564557
return False
565558
with sem:
566-
# Static DH params because the ones generated by cryptography are considered unsafe by AD for some weird reason
567-
dhparams = {
568-
"p": int("00ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff", 16),
569-
"g": 2
570-
}
571-
self.logger.info("Loading certificate and key from file")
572-
573-
# Load the certificate and key from file
574-
if self.args.pfx_cert or self.args.pfx_base64:
575-
pfx = self.args.pfx_cert if self.args.pfx_cert else self.args.pfx_base64
576-
ini = myPKINIT.from_pfx(pfx, self.args.pfx_pass, dhparams, bool(self.args.pfx_base64))
577-
elif self.args.cert_pem and self.args.key_pem:
578-
ini = myPKINIT.from_pem(self.args.cert_pem, self.args.key_pem, dhparams)
579-
else:
580-
self.logger.fail("You must either specify a PFX file + optional password or a combination of Cert PEM file and Private key PEM file")
581-
return None
582-
583-
username = self.args.username[0]
584-
log_ccache = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}-{username}.ccache".replace(":", "-"))
585-
586-
# Request a TGT with the cert data
587-
req = ini.build_asreq(self.domain, username)
588-
self.logger.info("Requesting TGT")
589-
590-
sock = KerberosClientSocket(KerberosTarget(self.host))
591-
try:
592-
res = sock.sendrecv(req)
593-
except Exception as e:
594-
self.logger.fail(str(e))
595-
return False
596-
597-
encasrep, session_key, cipher, key = ini.decrypt_asrep(res.native)
598-
ccache_minikerberos = CCACHE()
599-
ccache_minikerberos.add_tgt(res.native, encasrep)
600-
ccache_minikerberos.to_file(log_ccache)
601-
self.logger.info(f"Saved TGT to file { log_ccache }")
602-
self.logger.info(f"Using Kerberos Cache { log_ccache }")
603-
ccache = CCache.loadFile(log_ccache)
604-
principal = f"krbtgt/{self.domain.upper()}@{self.domain.upper()}"
605-
creds = ccache.getCredential(principal)
606-
if creds is not None:
607-
tgt = creds.toTGT()
608-
dumper = GETPAC(username, self.domain, self.host, key, tgt)
609-
nthash = dumper.dump()
610-
if not self.kerberos:
611-
self.hash_login(self.domain, username, nthash)
612-
else:
613-
self.kerberos_login(self.domain, username, "", nthash, "", self.kdcHost, False)
614-
615-
self.logger.info("Successfully authenticated using Certificate")
616-
return True
559+
return pfx_auth(self)
617560

618561
if hasattr(self.args, "laps") and self.args.laps:
619562
self.logger.debug("Trying to authenticate using LAPS")

nxc/helpers/pfx.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
# Dirk-jan Mollema (@_dirkjan)
2727
#
2828

29+
import os
2930
import secrets
3031
import hashlib
3132
import datetime
@@ -61,6 +62,14 @@
6162
PAC_CREDENTIAL_DATA, NTLM_SUPPLEMENTAL_CREDENTIAL
6263
from impacket.krb5.types import Principal, KerberosTime, Ticket
6364

65+
# Imports for pfx_auth
66+
from minikerberos.network.clientsocket import KerberosClientSocket
67+
from minikerberos.common.target import KerberosTarget
68+
from minikerberos.common.ccache import CCACHE
69+
70+
from impacket.krb5.ccache import CCache
71+
72+
6473
class myPKINIT(PKINIT):
6574
"""
6675
Copy of minikerberos PKINIT
@@ -475,4 +484,59 @@ def dump(self):
475484
# S4USelf + U2U uses this other key
476485
plainText = cipher.decrypt(sessionKey, 2, cipherText)
477486
specialkey = Key(18, unhexlify(self.__asrep_key))
478-
return self.printPac(plainText, specialkey)
487+
return self.printPac(plainText, specialkey)
488+
489+
490+
def pfx_auth(self):
491+
"""Handles the authentication using a PFX or PEM file"""
492+
# Static DH params because the ones generated by cryptography are considered unsafe by AD for some weird reason
493+
dhparams = {
494+
"p": int("00ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff", 16),
495+
"g": 2
496+
}
497+
self.logger.info("Loading certificate and key from file")
498+
499+
# Load the certificate and key from file
500+
if self.args.pfx_cert or self.args.pfx_base64:
501+
pfx = self.args.pfx_cert if self.args.pfx_cert else self.args.pfx_base64
502+
ini = myPKINIT.from_pfx(pfx, self.args.pfx_pass, dhparams, bool(self.args.pfx_base64))
503+
elif self.args.cert_pem and self.args.key_pem:
504+
ini = myPKINIT.from_pem(self.args.cert_pem, self.args.key_pem, dhparams)
505+
else:
506+
self.logger.fail("You must either specify a PFX file + optional password or a combination of Cert PEM file and Private key PEM file")
507+
return None
508+
509+
username = self.args.username[0]
510+
log_ccache = os.path.expanduser(f"~/.nxc/logs/{self.hostname}_{self.host}_{datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')}-{username}.ccache".replace(":", "-"))
511+
512+
# Request a TGT with the cert data
513+
req = ini.build_asreq(self.domain, username)
514+
self.logger.info("Requesting TGT")
515+
516+
sock = KerberosClientSocket(KerberosTarget(self.host))
517+
try:
518+
res = sock.sendrecv(req)
519+
except Exception as e:
520+
self.logger.fail(str(e))
521+
return False
522+
523+
encasrep, session_key, cipher, key = ini.decrypt_asrep(res.native)
524+
ccache_minikerberos = CCACHE()
525+
ccache_minikerberos.add_tgt(res.native, encasrep)
526+
ccache_minikerberos.to_file(log_ccache)
527+
self.logger.info(f"Saved TGT to file { log_ccache }")
528+
self.logger.info(f"Using Kerberos Cache { log_ccache }")
529+
ccache = CCache.loadFile(log_ccache)
530+
principal = f"krbtgt/{self.domain.upper()}@{self.domain.upper()}"
531+
creds = ccache.getCredential(principal)
532+
if creds is not None:
533+
tgt = creds.toTGT()
534+
dumper = GETPAC(username, self.domain, self.host, key, tgt)
535+
nthash = dumper.dump()
536+
if not self.kerberos:
537+
self.hash_login(self.domain, username, nthash)
538+
else:
539+
self.kerberos_login(self.domain, username, "", nthash, "", self.kdcHost, False)
540+
541+
self.logger.info("Successfully authenticated using Certificate")
542+
return True

0 commit comments

Comments
 (0)