From 2d2d017085c14b643edb902e91fbb2d7d980d31b Mon Sep 17 00:00:00 2001 From: Leon Schmidt Date: Fri, 2 Jun 2023 13:06:08 +0200 Subject: [PATCH 1/3] Unclean temporary implementation done --- Rubeus/Commands/Asktgt.cs | 1 + Rubeus/lib/Ask.cs | 47 +++++++++++++++++++++++++++++ Rubeus/lib/krb_structures/AS_REQ.cs | 6 ++-- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Rubeus/Commands/Asktgt.cs b/Rubeus/Commands/Asktgt.cs index f4e88e55..7949a255 100644 --- a/Rubeus/Commands/Asktgt.cs +++ b/Rubeus/Commands/Asktgt.cs @@ -255,6 +255,7 @@ public void Execute(Dictionary arguments) else if (String.IsNullOrEmpty(certificate)) Ask.TGT(user, domain, hash, encType, outfile, ptt, dc, luid, true, opsec, servicekey, changepw, pac, proxyUrl, service); else + // Caution: Ask.TGT is overloaded!!! This is another function implementation than the one above. Ask.TGT(user, domain, certificate, password, encType, outfile, ptt, dc, luid, true, verifyCerts, servicekey, getCredentials, proxyUrl, service, changepw); return; diff --git a/Rubeus/lib/Ask.cs b/Rubeus/lib/Ask.cs index d0e1538b..6e6a8be8 100644 --- a/Rubeus/lib/Ask.cs +++ b/Rubeus/lib/Ask.cs @@ -187,6 +187,31 @@ public static X509Certificate2 FindCertificate(string certificate, string storeP } public static byte[] TGT(string userName, string domain, string certFile, string certPass, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verifyCerts = false, string servicekey = "", bool getCredentials = false, string proxyUrl = null, string service = null, bool changepw = false) { + Console.WriteLine("[TEMP] Called Certificate variant of Ask.TGT"); + + // CUSTOM BEGIN + + bool opsec = true; // TEMP!!!! TODO implement as paramter + + bool preauth = false; + if (opsec) + { + Console.WriteLine("[TEMP] opsec is true, doing nopreauth request!"); + try + { + preauth = NoPreAuthTGT(userName, domain, "", etype, domainController, outfile, ptt, luid, describe, true, proxyUrl); + } + catch (KerberosErrorException err) + { + Console.WriteLine("[TEMP] Got KerberosErrorException, but propably not relevant!"); + Console.WriteLine(err.ToString()); + } + } + + Console.WriteLine("[TEMP] Preauth succes? " + preauth); + + // CUSTOM END + try { X509Certificate2 cert = FindCertificate(certFile, certPass); @@ -207,6 +232,28 @@ public static X509Certificate2 FindCertificate(string certificate, string storeP Console.WriteLine("[*] Building AS-REQ (w/ PKINIT preauth) for: '{0}\\{1}'", domain, userName); AS_REQ pkinitASREQ = AS_REQ.NewASReq(userName, domain, cert, agreement, etype, verifyCerts, service, changepw); + + // CUSTOM BEGIN + + // TODO move this to the overloaded AS_REQ.NewASReq function above!!! (orient on the overload with the keyString param!!!) + // TODO IDEA: Outsource req_body adjustements to an own function (e.g. ApplyOpsecAdjustments(req_body)) + + // opsec stuff + string hostName = System.Net.Dns.GetHostName(); + List addresses = new List(); + addresses.Add(new HostAddress(hostName)); + pkinitASREQ.req_body.addresses = addresses; + pkinitASREQ.req_body.kdcOptions = pkinitASREQ.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.rc2CBC_EnvOID); // 12 + pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.des_ede3_cbc_Env_OID); // 15 + pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); + + // CUSTOM END + return InnerTGT(pkinitASREQ, etype, outfile, ptt, domainController, luid, describe, true, false, servicekey, getCredentials, proxyUrl); } catch (KerberosErrorException ex) { diff --git a/Rubeus/lib/krb_structures/AS_REQ.cs b/Rubeus/lib/krb_structures/AS_REQ.cs index b8f20fb7..85b6619a 100755 --- a/Rubeus/lib/krb_structures/AS_REQ.cs +++ b/Rubeus/lib/krb_structures/AS_REQ.cs @@ -160,6 +160,7 @@ public static AS_REQ NewASReq(string userName, string domain, string keyString, //TODO: Insert DHKeyPair parameter also. public static AS_REQ NewASReq(string userName, string domain, X509Certificate2 cert, KDCKeyAgreement agreement, Interop.KERB_ETYPE etype, bool verifyCerts = false, string service = null, bool changepw = false) { + // TODO implement opsec flag // build a new AS-REQ for the given userName, domain, and etype, w/ PA-ENC-TIMESTAMP // used for "legit" AS-REQs w/ pre-auth @@ -247,14 +248,15 @@ public AS_REQ(string keyString, Interop.KERB_ETYPE etype, bool opsec = false, bo } public AS_REQ(X509Certificate2 pkCert, KDCKeyAgreement agreement, bool verifyCerts = false) { + // TODO add opsec flag // default, for creation pvno = 5; - msg_type = 10; + msg_type = (long)Interop.KERB_MESSAGE_TYPE.AS_REQ; padata = new List(); - req_body = new KDCReqBody(); + req_body = new KDCReqBody(true, true /* TODO implement as paramter */); // add the include-pac == true padata.Add(new PA_DATA()); From ee7ea7aeddd6b701c9cda8f88d1beb6a78d005ff Mon Sep 17 00:00:00 2001 From: Leon Schmidt Date: Mon, 5 Jun 2023 11:28:48 +0200 Subject: [PATCH 2/3] Some changes: - Applied opsec params to necessary overloads - Removed unused opsec flag from InnerTGT function - Added some justification commets to overloaded functions --- Rubeus/Commands/Asktgt.cs | 4 +-- Rubeus/lib/Ask.cs | 54 ++++++----------------------- Rubeus/lib/krb_structures/AS_REQ.cs | 38 +++++++++++++------- 3 files changed, 39 insertions(+), 57 deletions(-) diff --git a/Rubeus/Commands/Asktgt.cs b/Rubeus/Commands/Asktgt.cs index 7949a255..658c651d 100644 --- a/Rubeus/Commands/Asktgt.cs +++ b/Rubeus/Commands/Asktgt.cs @@ -255,8 +255,8 @@ public void Execute(Dictionary arguments) else if (String.IsNullOrEmpty(certificate)) Ask.TGT(user, domain, hash, encType, outfile, ptt, dc, luid, true, opsec, servicekey, changepw, pac, proxyUrl, service); else - // Caution: Ask.TGT is overloaded!!! This is another function implementation than the one above. - Ask.TGT(user, domain, certificate, password, encType, outfile, ptt, dc, luid, true, verifyCerts, servicekey, getCredentials, proxyUrl, service, changepw); + // Ask.TGT is overloaded! This is another function implementation than the one above + Ask.TGT(user, domain, certificate, password, encType, outfile, ptt, dc, luid, true, opsec, verifyCerts, servicekey, getCredentials, proxyUrl, service, changepw); return; } diff --git a/Rubeus/lib/Ask.cs b/Rubeus/lib/Ask.cs index 6e6a8be8..3e82712e 100644 --- a/Rubeus/lib/Ask.cs +++ b/Rubeus/lib/Ask.cs @@ -53,7 +53,7 @@ public class Ask Console.WriteLine("[*] Using {0} hash: {1}", etype, keyString); Console.WriteLine("[*] Building AS-REQ (w/ preauth) for: '{0}\\{1}'", domain, userName); AS_REQ userHashASREQ = AS_REQ.NewASReq(userName, domain, keyString, etype, opsec, changepw, pac, service); - return InnerTGT(userHashASREQ, etype, outfile, ptt, domainController, luid, describe, true, opsec, servicekey, false, proxyUrl); + return InnerTGT(userHashASREQ, etype, outfile, ptt, domainController, luid, describe, true, servicekey, false, proxyUrl); } } catch (KerberosErrorException ex) @@ -186,32 +186,22 @@ public static X509Certificate2 FindCertificate(string certificate, string storeP } } - public static byte[] TGT(string userName, string domain, string certFile, string certPass, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verifyCerts = false, string servicekey = "", bool getCredentials = false, string proxyUrl = null, string service = null, bool changepw = false) { - Console.WriteLine("[TEMP] Called Certificate variant of Ask.TGT"); - - // CUSTOM BEGIN - - bool opsec = true; // TEMP!!!! TODO implement as paramter - + /// + /// Use this overload to request a TGT via PKINIT and a X509 certificate + /// + public static byte[] TGT(string userName, string domain, string certFile, string certPass, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool opsec = false, bool verifyCerts = false, string servicekey = "", bool getCredentials = false, string proxyUrl = null, string service = null, bool changepw = false) + { + // send request without Pre-Auth to emulate genuine traffic bool preauth = false; if (opsec) { - Console.WriteLine("[TEMP] opsec is true, doing nopreauth request!"); try { preauth = NoPreAuthTGT(userName, domain, "", etype, domainController, outfile, ptt, luid, describe, true, proxyUrl); } - catch (KerberosErrorException err) - { - Console.WriteLine("[TEMP] Got KerberosErrorException, but propably not relevant!"); - Console.WriteLine(err.ToString()); - } + catch (KerberosErrorException) { } } - Console.WriteLine("[TEMP] Preauth succes? " + preauth); - - // CUSTOM END - try { X509Certificate2 cert = FindCertificate(certFile, certPass); @@ -231,30 +221,8 @@ public static X509Certificate2 FindCertificate(string certificate, string storeP Console.WriteLine("[*] Using PKINIT with etype {0} and subject: {1} ", etype, cert.Subject); Console.WriteLine("[*] Building AS-REQ (w/ PKINIT preauth) for: '{0}\\{1}'", domain, userName); - AS_REQ pkinitASREQ = AS_REQ.NewASReq(userName, domain, cert, agreement, etype, verifyCerts, service, changepw); - - // CUSTOM BEGIN - - // TODO move this to the overloaded AS_REQ.NewASReq function above!!! (orient on the overload with the keyString param!!!) - // TODO IDEA: Outsource req_body adjustements to an own function (e.g. ApplyOpsecAdjustments(req_body)) - - // opsec stuff - string hostName = System.Net.Dns.GetHostName(); - List addresses = new List(); - addresses.Add(new HostAddress(hostName)); - pkinitASREQ.req_body.addresses = addresses; - pkinitASREQ.req_body.kdcOptions = pkinitASREQ.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; - pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.rc2CBC_EnvOID); // 12 - pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.des_ede3_cbc_Env_OID); // 15 - pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); - pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); - pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); - pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); - pkinitASREQ.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); - - // CUSTOM END - - return InnerTGT(pkinitASREQ, etype, outfile, ptt, domainController, luid, describe, true, false, servicekey, getCredentials, proxyUrl); + AS_REQ pkinitASREQ = AS_REQ.NewASReq(userName, domain, cert, agreement, etype, opsec, verifyCerts, service, changepw); + return InnerTGT(pkinitASREQ, etype, outfile, ptt, domainController, luid, describe, true, servicekey, getCredentials, proxyUrl); } catch (KerberosErrorException ex) { KRB_ERROR error = ex.krbError; @@ -295,7 +263,7 @@ public static int GetKeySize(Interop.KERB_ETYPE etype) { } } - public static byte[] InnerTGT(AS_REQ asReq, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verbose = false, bool opsec = false, string serviceKey = "", bool getCredentials = false, string proxyUrl = null) + public static byte[] InnerTGT(AS_REQ asReq, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verbose = false, string serviceKey = "", bool getCredentials = false, string proxyUrl = null) { if ((ulong)luid != 0) { Console.WriteLine("[*] Target LUID : {0}", (ulong)luid); diff --git a/Rubeus/lib/krb_structures/AS_REQ.cs b/Rubeus/lib/krb_structures/AS_REQ.cs index 85b6619a..c0e66230 100755 --- a/Rubeus/lib/krb_structures/AS_REQ.cs +++ b/Rubeus/lib/krb_structures/AS_REQ.cs @@ -76,7 +76,6 @@ public static AS_REQ NewASReq(string userName, string domain, Interop.KERB_ETYPE req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); - } else { @@ -159,14 +158,12 @@ public static AS_REQ NewASReq(string userName, string domain, string keyString, } //TODO: Insert DHKeyPair parameter also. - public static AS_REQ NewASReq(string userName, string domain, X509Certificate2 cert, KDCKeyAgreement agreement, Interop.KERB_ETYPE etype, bool verifyCerts = false, string service = null, bool changepw = false) { - // TODO implement opsec flag - + public static AS_REQ NewASReq(string userName, string domain, X509Certificate2 cert, KDCKeyAgreement agreement, Interop.KERB_ETYPE etype, bool opsec = false, bool verifyCerts = false, string service = null, bool changepw = false) { // build a new AS-REQ for the given userName, domain, and etype, w/ PA-ENC-TIMESTAMP // used for "legit" AS-REQs w/ pre-auth // set pre-auth - AS_REQ req = new AS_REQ(cert, agreement, verifyCerts); + AS_REQ req = new AS_REQ(cert, agreement, opsec, verifyCerts); // req.padata.Add() @@ -210,8 +207,26 @@ public static AS_REQ NewASReq(string userName, string domain, X509Certificate2 c req.req_body.sname.name_string.Add("changepw"); } - // add in our encryption type - req.req_body.etypes.Add(etype); + // try to build a realistic request + if (opsec) + { + string hostName = Dns.GetHostName(); + List addresses = new List(); + addresses.Add(new HostAddress(hostName)); + req.req_body.addresses = addresses; + req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); + } + else + { + // add in our encryption type + req.req_body.etypes.Add(etype); + } return req; } @@ -247,22 +262,21 @@ public AS_REQ(string keyString, Interop.KERB_ETYPE etype, bool opsec = false, bo this.keyString = keyString; } - public AS_REQ(X509Certificate2 pkCert, KDCKeyAgreement agreement, bool verifyCerts = false) { - // TODO add opsec flag - + public AS_REQ(X509Certificate2 pkCert, KDCKeyAgreement agreement, bool opsec = false, bool verifyCerts = false) { // default, for creation pvno = 5; msg_type = (long)Interop.KERB_MESSAGE_TYPE.AS_REQ; padata = new List(); - req_body = new KDCReqBody(true, true /* TODO implement as paramter */); + req_body = new KDCReqBody(true, opsec); // add the include-pac == true padata.Add(new PA_DATA()); // add the encrypted timestamp - padata.Add(new PA_DATA(pkCert, agreement, req_body, verifyCerts)); + padata.Add(new PA_DATA(pkCert, agreement, req_body, verifyCerts)); + } public AS_REQ(byte[] data) From 3d490e67b203944c1304f12e93ece4744eddaec4 Mon Sep 17 00:00:00 2001 From: Leon Schmidt Date: Mon, 5 Jun 2023 13:18:47 +0200 Subject: [PATCH 3/3] Outsourced opsec adjustements to req body to its own function --- Rubeus/lib/krb_structures/AS_REQ.cs | 79 +++++++++-------------------- 1 file changed, 25 insertions(+), 54 deletions(-) diff --git a/Rubeus/lib/krb_structures/AS_REQ.cs b/Rubeus/lib/krb_structures/AS_REQ.cs index c0e66230..ce6d44d7 100755 --- a/Rubeus/lib/krb_structures/AS_REQ.cs +++ b/Rubeus/lib/krb_structures/AS_REQ.cs @@ -62,26 +62,10 @@ public static AS_REQ NewASReq(string userName, string domain, Interop.KERB_ETYPE req.req_body.sname.name_string.Add(domain); } - // try to build a realistic request if (opsec) - { - string hostName = Dns.GetHostName(); - List addresses = new List(); - addresses.Add(new HostAddress(hostName)); - req.req_body.addresses = addresses; - req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; - req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); - req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); - req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); - req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); - req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); - req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); - } - else - { - // add in our encryption type + ApplyOpsecChanges(req.req_body); + else // add in our encryption type req.req_body.etypes.Add(etype); - } return req; } @@ -133,26 +117,10 @@ public static AS_REQ NewASReq(string userName, string domain, string keyString, req.req_body.sname.name_string.Add("changepw"); } - // try to build a realistic request if (opsec) - { - string hostName = Dns.GetHostName(); - List addresses = new List(); - addresses.Add(new HostAddress(hostName)); - req.req_body.addresses = addresses; - req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; - req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); - req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); - req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); - req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); - req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); - req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); - } - else - { - // add in our encryption type + ApplyOpsecChanges(req.req_body); + else // add in our encryption type req.req_body.etypes.Add(etype); - } return req; } @@ -207,26 +175,10 @@ public static AS_REQ NewASReq(string userName, string domain, X509Certificate2 c req.req_body.sname.name_string.Add("changepw"); } - // try to build a realistic request if (opsec) - { - string hostName = Dns.GetHostName(); - List addresses = new List(); - addresses.Add(new HostAddress(hostName)); - req.req_body.addresses = addresses; - req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; - req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); - req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); - req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); - req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); - req.req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); - req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); - } - else - { - // add in our encryption type + ApplyOpsecChanges(req.req_body); + else // add in our encryption type req.req_body.etypes.Add(etype); - } return req; } @@ -329,6 +281,25 @@ public AS_REQ(byte[] data) } } } + /// + /// Applies opsec changes in-place to an existing, initialized KDCReqBody including + /// common etypes, the optional addresses field and the "canonicalize" kdc_option. + /// + /// + private static void ApplyOpsecChanges(KDCReqBody req_body) + { + string hostName = Dns.GetHostName(); + List addresses = new List(); + addresses.Add(new HostAddress(hostName)); + req_body.addresses = addresses; + req_body.kdcOptions = req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE; + req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); + req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); + req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); + req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); + req_body.etypes.Add(Interop.KERB_ETYPE.old_exp); + req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); + } public AsnElt Encode() {