From 0ef278ad8d59e374bb189e18ea478c9c8929c64d Mon Sep 17 00:00:00 2001 From: Quentin Lemaire Date: Thu, 29 Nov 2018 07:05:32 +0100 Subject: [PATCH] Add "/password" support to "asktgt" command Generate kerberos keys from user's password to request a tgt. The "/enctype" parameter can be used to select "AES256" enctype (aes256_cts_hmac_sha1). Default is RC4 (rc4_hmac). --- Rubeus/Commands/Asktgt.cs | 25 ++++++++++++- Rubeus/Domain/Info.cs | 8 ++-- Rubeus/lib/Crypto.cs | 78 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 7 deletions(-) diff --git a/Rubeus/Commands/Asktgt.cs b/Rubeus/Commands/Asktgt.cs index 90d429c..f7abadb 100755 --- a/Rubeus/Commands/Asktgt.cs +++ b/Rubeus/Commands/Asktgt.cs @@ -12,6 +12,7 @@ namespace Rubeus.Commands { string user = ""; string domain = ""; + string password = ""; string hash = ""; string dc = ""; bool ptt = false; @@ -39,6 +40,28 @@ namespace Rubeus.Commands { dc = arguments["/dc"]; } + if (arguments.ContainsKey("/password")) + { + password = arguments["/password"]; + if (arguments.ContainsKey("/enctype") && arguments["/enctype"].ToUpper().Equals("AES256")) + { + encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + + // compute AES key from pwd + byte[] password_bytes = System.Text.Encoding.UTF8.GetBytes(password); + byte[] salt = System.Text.Encoding.UTF8.GetBytes(domain.ToUpper() + user); + + byte[] aes256_key = Crypto.ComputeAES256KerberosKey(password_bytes, salt); + hash = System.BitConverter.ToString(aes256_key).Replace("-", ""); + } + else // default is RC4 + { + // compute NTLM from pwd + encType = Interop.KERB_ETYPE.rc4_hmac; + byte[] ntlm = Crypto.ComputeRC4KerberosKey(password); // a.k.a NTLM + hash = System.BitConverter.ToString(ntlm).Replace("-", ""); + } + } if (arguments.ContainsKey("/rc4")) { hash = arguments["/rc4"]; @@ -104,7 +127,7 @@ namespace Rubeus.Commands } if (String.IsNullOrEmpty(hash)) { - Console.WriteLine("\r\n[X] You must supply a /rc4 or /aes256 hash!\r\n"); + Console.WriteLine("\r\n[X] You must supply a /password or /rc4 hash or /aes256 hash!\r\n"); return; } diff --git a/Rubeus/Domain/Info.cs b/Rubeus/Domain/Info.cs index b3e69e7..34f89c3 100755 --- a/Rubeus/Domain/Info.cs +++ b/Rubeus/Domain/Info.cs @@ -19,11 +19,11 @@ namespace Rubeus.Domain { Console.WriteLine("\r\n Rubeus usage:"); - Console.WriteLine("\r\n Retrieve a TGT based on a user hash, optionally applying to the current logon session or a specific LUID:"); - Console.WriteLine(" Rubeus.exe asktgt /user:USER [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ptt] [/luid]"); + Console.WriteLine("\r\n Retrieve a TGT based on a user password/hash, optionally applying to the current logon session or a specific LUID:"); + Console.WriteLine(" Rubeus.exe asktgt /user:USER [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ptt] [/luid]"); - Console.WriteLine("\r\n Retrieve a TGT based on a user hash, start a /netonly process, and to apply the ticket to the new process/logon session:"); - Console.WriteLine(" Rubeus.exe asktgt /user:USER /createnetonly:C:\\Windows\\System32\\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER]"); + Console.WriteLine("\r\n Retrieve a TGT based on a user password/hash, start a /netonly process, and to apply the ticket to the new process/logon session:"); + Console.WriteLine(" Rubeus.exe asktgt /user:USER /createnetonly:C:\\Windows\\System32\\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER]"); Console.WriteLine("\r\n Retrieve a service ticket for one or more SPNs, optionally applying the ticket:"); Console.WriteLine(" Rubeus.exe asktgs [/dc:DOMAIN_CONTROLLER] [/ptt]"); diff --git a/Rubeus/lib/Crypto.cs b/Rubeus/lib/Crypto.cs index 046c36a..01ed551 100755 --- a/Rubeus/lib/Crypto.cs +++ b/Rubeus/lib/Crypto.cs @@ -1,6 +1,5 @@ using System; -using Asn1; -using System.Text; +using System.Linq; using System.Collections.Generic; using System.Runtime.InteropServices; using System.ComponentModel; @@ -9,6 +8,81 @@ namespace Rubeus { public class Crypto { + // Adapted from Kevin-Robertson powershell Get-KerberosAESKey: https://gist.github.com/Kevin-Robertson/9e0f8bfdbf4c1e694e6ff4197f0a4372 + // References: + // * [MS-KILE] open spec' https://msdn.microsoft.com/library/cc233855.aspx?f=255&MSPPError=-2147217396 + // * RFC regarding AES in Kerberos https://www.rfc-editor.org/rfc/pdfrfc/rfc3962.txt.pdf + public static byte[] ComputeAES256KerberosKey(byte[] password, byte[] salt) + { + byte[] AES256_CONSTANT = { 0x6B, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6F, 0x73, 0x7B, 0x9B, 0x5B, 0x2B, 0x93, 0x13, 0x2B, 0x93, 0x5C, 0x9B, 0xDC, 0xDA, 0xD9, 0x5C, 0x98, 0x99, 0xC4, 0xCA, 0xE4, 0xDE, 0xE6, 0xD6, 0xCA, 0xE4 }; + const int rounds = 4096; + var pbkdf2 = new System.Security.Cryptography.Rfc2898DeriveBytes(password, salt, rounds); + byte[] pbkdf2_aes256_key = pbkdf2.GetBytes(32); + + string pbkdf2_aes256_key_string = System.BitConverter.ToString(pbkdf2_aes256_key).Replace("-", ""); + + var aes = new System.Security.Cryptography.AesManaged(); + aes.Mode = System.Security.Cryptography.CipherMode.CBC; + aes.Padding = System.Security.Cryptography.PaddingMode.None; + aes.KeySize = 256; + aes.Key = pbkdf2_aes256_key; + aes.IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + var encryptor = aes.CreateEncryptor(); + byte[] aes256_key_part_1 = encryptor.TransformFinalBlock(AES256_CONSTANT, 0, AES256_CONSTANT.Length); + byte[] aes256_key_part_2 = encryptor.TransformFinalBlock(aes256_key_part_1, 0, aes256_key_part_1.Length); + byte[] aes256_key = aes256_key_part_1.Take(16).Concat(aes256_key_part_2.Take(16)).ToArray(); + return aes256_key; + } + + public static byte[] ComputeRC4KerberosKey(string password) + { + // RC4 Kerberos Key is NTLM + byte[] password_byte = System.Text.Encoding.Unicode.GetBytes(password); + return ComputeMD4(password_byte); + } + + // source: https://rosettacode.org/wiki/MD4#C.23 + private static byte[] ComputeMD4(byte[] plain) + { + // get padded uints from bytes + List bytes = plain.ToList(); + uint bitCount = (uint)(bytes.Count) * 8; + bytes.Add(128); + while (bytes.Count % 64 != 56) bytes.Add(0); + var uints = new List(); + for (int i = 0; i + 3 < bytes.Count; i += 4) + uints.Add(bytes[i] | (uint)bytes[i + 1] << 8 | (uint)bytes[i + 2] << 16 | (uint)bytes[i + 3] << 24); + uints.Add(bitCount); + uints.Add(0); + + // run rounds + uint a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476; + Func rol = (x, y) => x << (int)y | x >> 32 - (int)y; + for (int q = 0; q + 15 < uints.Count; q += 16) + { + var chunk = uints.GetRange(q, 16); + uint aa = a, bb = b, cc = c, dd = d; + Action, uint[]> round = (f, y) => + { + foreach (uint i in new[] { y[0], y[1], y[2], y[3] }) + { + a = rol(a + f(b, c, d) + chunk[(int)(i + y[4])] + y[12], y[8]); + d = rol(d + f(a, b, c) + chunk[(int)(i + y[5])] + y[12], y[9]); + c = rol(c + f(d, a, b) + chunk[(int)(i + y[6])] + y[12], y[10]); + b = rol(b + f(c, d, a) + chunk[(int)(i + y[7])] + y[12], y[11]); + } + }; + round((x, y, z) => (x & y) | (~x & z), new uint[] { 0, 4, 8, 12, 0, 1, 2, 3, 3, 7, 11, 19, 0 }); + round((x, y, z) => (x & y) | (x & z) | (y & z), new uint[] { 0, 1, 2, 3, 0, 4, 8, 12, 3, 5, 9, 13, 0x5a827999 }); + round((x, y, z) => x ^ y ^ z, new uint[] { 0, 2, 1, 3, 0, 8, 4, 12, 3, 9, 11, 15, 0x6ed9eba1 }); + a += aa; b += bb; c += cc; d += dd; + } + + // return hex encoded string + byte[] outBytes = new[] { a, b, c, d }.SelectMany(BitConverter.GetBytes).ToArray(); + return outBytes; + } + // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" public static byte[] KerberosChecksum(byte[] key, byte[] data) {