public static List <Dictionary <string, string> > ListKerberosTicketsAllUsers() { List <Dictionary <string, string> > results = new List <Dictionary <string, string> >(); // adapted partially from Vincent LE TOUX' work // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2939-L2950 // and https://www.dreamincode.net/forums/topic/135033-increment-memory-pointer-issue/ // also Jared Atkinson's work at https://github.com/Invoke-IR/ACE/blob/master/ACE-Management/PS-ACE/Scripts/ACE_Get-KerberosTicketCache.ps1 IntPtr hLsa = Helpers.LsaRegisterLogonProcessHelper(); int totalTicketCount = 0; // if the original call fails then it is likely we don't have SeTcbPrivilege // to get SeTcbPrivilege we can Impersonate a NT AUTHORITY\SYSTEM Token if (hLsa == IntPtr.Zero) { Helpers.GetSystem(); // should now have the proper privileges to get a Handle to LSA hLsa = Helpers.LsaRegisterLogonProcessHelper(); // we don't need our NT AUTHORITY\SYSTEM Token anymore so we can revert to our original token Advapi32.RevertToSelf(); } try { // first return all the logon sessions DateTime systime = new DateTime(1601, 1, 1, 0, 0, 0, 0); //win32 systemdate UInt64 count; IntPtr luidPtr = IntPtr.Zero; IntPtr iter = luidPtr; uint ret = Secur32.LsaEnumerateLogonSessions(out count, out luidPtr); // get an array of pointers to LUIDs for (ulong i = 0; i < count; i++) { IntPtr sessionData; ret = Secur32.LsaGetLogonSessionData(luidPtr, out sessionData); SECURITY_LOGON_SESSION_DATA data = (SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(sessionData, typeof(SECURITY_LOGON_SESSION_DATA)); // if we have a valid logon if (data.PSiD != IntPtr.Zero) { // user session data string username = Marshal.PtrToStringUni(data.Username.Buffer).Trim(); System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(data.PSiD); string domain = Marshal.PtrToStringUni(data.LoginDomain.Buffer).Trim(); string authpackage = Marshal.PtrToStringUni(data.AuthenticationPackage.Buffer).Trim(); SECURITY_LOGON_TYPE logonType = (SECURITY_LOGON_TYPE)data.LogonType; DateTime logonTime = systime.AddTicks((long)data.LoginTime); string logonServer = Marshal.PtrToStringUni(data.LogonServer.Buffer).Trim(); string dnsDomainName = Marshal.PtrToStringUni(data.DnsDomainName.Buffer).Trim(); string upn = Marshal.PtrToStringUni(data.Upn.Buffer).Trim(); // now we want to get the tickets for this logon ID string name = "kerberos"; LSA_STRING_IN LSAString; LSAString.Length = (ushort)name.Length; LSAString.MaximumLength = (ushort)(name.Length + 1); LSAString.Buffer = name; IntPtr ticketPointer = IntPtr.Zero; IntPtr ticketsPointer = IntPtr.Zero; DateTime sysTime = new DateTime(1601, 1, 1, 0, 0, 0, 0); int authPack; int returnBufferLength = 0; int protocalStatus = 0; int retCode; KERB_QUERY_TKT_CACHE_REQUEST tQuery = new KERB_QUERY_TKT_CACHE_REQUEST(); KERB_QUERY_TKT_CACHE_RESPONSE tickets = new KERB_QUERY_TKT_CACHE_RESPONSE(); KERB_TICKET_CACHE_INFO ticket; // obtains the unique identifier for the kerberos authentication package. retCode = Secur32.LsaLookupAuthenticationPackage(hLsa, ref LSAString, out authPack); // input object for querying the ticket cache for a specific logon ID LUID userLogonID = new LUID(); userLogonID.LowPart = data.LoginID.LowPart; userLogonID.HighPart = 0; tQuery.LogonId = userLogonID; tQuery.MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheMessage; // query LSA, specifying we want the ticket cache retCode = Secur32.LsaCallAuthenticationPackage(hLsa, authPack, ref tQuery, Marshal.SizeOf(tQuery), out ticketPointer, out returnBufferLength, out protocalStatus); /*Console.WriteLine("\r\n UserName : {0}", username); * Console.WriteLine(" Domain : {0}", domain); * Console.WriteLine(" LogonId : {0}", data.LoginID.LowPart); * Console.WriteLine(" UserSID : {0}", sid.AccountDomainSid); * Console.WriteLine(" AuthenticationPackage : {0}", authpackage); * Console.WriteLine(" LogonType : {0}", logonType); * Console.WriteLine(" LogonType : {0}", logonTime); * Console.WriteLine(" LogonServer : {0}", logonServer); * Console.WriteLine(" LogonServerDNSDomain : {0}", dnsDomainName); * Console.WriteLine(" UserPrincipalName : {0}\r\n", upn);*/ if (ticketPointer != IntPtr.Zero) { // parse the returned pointer into our initial KERB_QUERY_TKT_CACHE_RESPONSE structure tickets = (KERB_QUERY_TKT_CACHE_RESPONSE)Marshal.PtrToStructure((System.IntPtr)ticketPointer, typeof(KERB_QUERY_TKT_CACHE_RESPONSE)); int count2 = tickets.CountOfTickets; if (count2 != 0) { Console.WriteLine(" [*] Enumerated {0} ticket(s):\r\n", count2); totalTicketCount += count2; // get the size of the structures we're iterating over Int32 dataSize = Marshal.SizeOf(typeof(KERB_TICKET_CACHE_INFO)); for (int j = 0; j < count2; j++) { // iterate through the structures IntPtr currTicketPtr = (IntPtr)(long)((ticketPointer.ToInt64() + (int)(8 + j * dataSize))); // parse the new ptr to the appropriate structure ticket = (KERB_TICKET_CACHE_INFO)Marshal.PtrToStructure(currTicketPtr, typeof(KERB_TICKET_CACHE_INFO)); // extract our fields string serverName = Marshal.PtrToStringUni(ticket.ServerName.Buffer, ticket.ServerName.Length / 2); string realmName = Marshal.PtrToStringUni(ticket.RealmName.Buffer, ticket.RealmName.Length / 2); DateTime startTime = DateTime.FromFileTime(ticket.StartTime); DateTime endTime = DateTime.FromFileTime(ticket.EndTime); DateTime renewTime = DateTime.FromFileTime(ticket.RenewTime); string encryptionType = ((KERB_ENCRYPTION_TYPE)ticket.EncryptionType).ToString(); string ticketFlags = ((KERB_TICKET_FLAGS)ticket.TicketFlags).ToString(); results.Add(new Dictionary <string, string>() { { "UserPrincipalName", upn }, { "serverName", serverName }, { "RealmName", realmName }, { "StartTime", string.Format("{0}", startTime) }, { "EndTime", string.Format("{0}", endTime) }, { "RenewTime", string.Format("{0}", renewTime) }, { "EncryptionType", encryptionType }, { "TicketFlags", ticketFlags }, }); } } } } // move the pointer forward luidPtr = (IntPtr)((long)luidPtr.ToInt64() + Marshal.SizeOf(typeof(LUID))); Secur32.LsaFreeReturnBuffer(sessionData); } Secur32.LsaFreeReturnBuffer(luidPtr); // disconnect from LSA Secur32.LsaDeregisterLogonProcess(hLsa); } catch (Exception ex) { Beaprint.PrintException(ex.Message); } return(results); }
public static List <Dictionary <string, string> > ListKerberosTicketsCurrentUser() { List <Dictionary <string, string> > results = new List <Dictionary <string, string> >(); // adapted partially from Vincent LE TOUX' work // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2939-L2950 // and https://www.dreamincode.net/forums/topic/135033-increment-memory-pointer-issue/ // also Jared Atkinson's work at https://github.com/Invoke-IR/ACE/blob/master/ACE-Management/PS-ACE/Scripts/ACE_Get-KerberosTicketCache.ps1 try { string name = "kerberos"; LSA_STRING_IN LSAString; LSAString.Length = (ushort)name.Length; LSAString.MaximumLength = (ushort)(name.Length + 1); LSAString.Buffer = name; IntPtr ticketPointer = IntPtr.Zero; IntPtr ticketsPointer = IntPtr.Zero; DateTime sysTime = new DateTime(1601, 1, 1, 0, 0, 0, 0); int authPack; int returnBufferLength = 0; int protocalStatus = 0; IntPtr lsaHandle; int retCode; // If we want to look at tickets from a session other than our own // then we need to use LsaRegisterLogonProcess instead of LsaConnectUntrusted retCode = Secur32.LsaConnectUntrusted(out lsaHandle); KERB_QUERY_TKT_CACHE_REQUEST tQuery = new KERB_QUERY_TKT_CACHE_REQUEST(); KERB_QUERY_TKT_CACHE_RESPONSE tickets = new KERB_QUERY_TKT_CACHE_RESPONSE(); KERB_TICKET_CACHE_INFO ticket; // obtains the unique identifier for the kerberos authentication package. retCode = Secur32.LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out authPack); // input object for querying the ticket cache (https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/ns-ntsecapi-_kerb_query_tkt_cache_request) tQuery.LogonId = new LUID(); tQuery.MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbQueryTicketCacheMessage; // query LSA, specifying we want the ticket cache retCode = Secur32.LsaCallAuthenticationPackage(lsaHandle, authPack, ref tQuery, Marshal.SizeOf(tQuery), out ticketPointer, out returnBufferLength, out protocalStatus); // parse the returned pointer into our initial KERB_QUERY_TKT_CACHE_RESPONSE structure tickets = (KERB_QUERY_TKT_CACHE_RESPONSE)Marshal.PtrToStructure((System.IntPtr)ticketPointer, typeof(KERB_QUERY_TKT_CACHE_RESPONSE)); int count = tickets.CountOfTickets; // get the size of the structures we're iterating over Int32 dataSize = Marshal.SizeOf(typeof(KERB_TICKET_CACHE_INFO)); for (int i = 0; i < count; i++) { // iterate through the structures IntPtr currTicketPtr = (IntPtr)(long)((ticketPointer.ToInt64() + (int)(8 + i * dataSize))); // parse the new ptr to the appropriate structure ticket = (KERB_TICKET_CACHE_INFO)Marshal.PtrToStructure(currTicketPtr, typeof(KERB_TICKET_CACHE_INFO)); // extract our fields string serverName = Marshal.PtrToStringUni(ticket.ServerName.Buffer, ticket.ServerName.Length / 2); string realmName = Marshal.PtrToStringUni(ticket.RealmName.Buffer, ticket.RealmName.Length / 2); DateTime startTime = DateTime.FromFileTime(ticket.StartTime); DateTime endTime = DateTime.FromFileTime(ticket.EndTime); DateTime renewTime = DateTime.FromFileTime(ticket.RenewTime); string encryptionType = ((KERB_ENCRYPTION_TYPE)ticket.EncryptionType).ToString(); string ticketFlags = ((KERB_TICKET_FLAGS)ticket.TicketFlags).ToString(); results.Add(new Dictionary <string, string>() { { "serverName", serverName }, { "RealmName", realmName }, { "StartTime", string.Format("{0}", startTime) }, { "EndTime", string.Format("{0}", endTime) }, { "RenewTime", string.Format("{0}", renewTime) }, { "EncryptionType", encryptionType }, { "TicketFlags", ticketFlags }, }); } // disconnect from LSA Secur32.LsaDeregisterLogonProcess(lsaHandle); } catch (Exception ex) { Beaprint.PrintException(ex.Message); } return(results); }