diff --git a/src/CommonLib/Enums/PKIPrivateKeyFlag.cs b/src/CommonLib/Enums/PKIPrivateKeyFlag.cs new file mode 100644 index 00000000..41112bc2 --- /dev/null +++ b/src/CommonLib/Enums/PKIPrivateKeyFlag.cs @@ -0,0 +1,24 @@ +using System; + +namespace SharpHoundCommonLib.Enums +{ + // from https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-crtd/f6122d87-b999-4b92-bff8-f465e8949667 + [Flags] + public enum PKIPrivateKeyFlag : uint + { + REQUIRE_PRIVATE_KEY_ARCHIVAL = 0x00000001, + EXPORTABLE_KEY = 0x00000010, + STRONG_KEY_PROTECTION_REQUIRED = 0x00000020, + REQUIRE_ALTERNATE_SIGNATURE_ALGORITHM = 0x00000040, + REQUIRE_SAME_KEY_RENEWAL = 0x00000080, + USE_LEGACY_PROVIDER = 0x00000100, + ATTEST_NONE = 0x00000000, + ATTEST_REQUIRED = 0x00002000, + ATTEST_PREFERRED = 0x00001000, + ATTESTATION_WITHOUT_POLICY = 0x00004000, + EK_TRUST_ON_USE = 0x00000200, + EK_VALIDATE_CERT = 0x00000400, + EK_VALIDATE_KEY = 0x00000800, + HELLO_LOGON_KEY = 0x00200000 + } +} \ No newline at end of file diff --git a/src/CommonLib/LDAPProperties.cs b/src/CommonLib/LDAPProperties.cs index 29ab56b5..f4dcffd8 100644 --- a/src/CommonLib/LDAPProperties.cs +++ b/src/CommonLib/LDAPProperties.cs @@ -59,6 +59,7 @@ public static class LDAPProperties public const string PKINameFlag = "mspki-certificate-name-flag"; public const string ExtendedKeyUsage = "pkiextendedkeyusage"; public const string NumSignaturesRequired = "mspki-ra-signature"; + public const string PKIPrivateKeyFlag = "mspki-private-key-flag"; public const string ApplicationPolicies = "mspki-ra-application-policies"; public const string IssuancePolicies = "mspki-ra-policies"; public const string CertificateApplicationPolicy = "mspki-certificate-application-policy"; diff --git a/src/CommonLib/LDAPQueries/CommonProperties.cs b/src/CommonLib/LDAPQueries/CommonProperties.cs index 9e503b7e..27dba19f 100644 --- a/src/CommonLib/LDAPQueries/CommonProperties.cs +++ b/src/CommonLib/LDAPQueries/CommonProperties.cs @@ -84,7 +84,7 @@ public static class CommonProperties LDAPProperties.PKIEnrollmentFlag, LDAPProperties.DisplayName, LDAPProperties.Name, LDAPProperties.TemplateSchemaVersion, LDAPProperties.CertTemplateOID, LDAPProperties.PKIOverlappedPeriod, LDAPProperties.PKIExpirationPeriod, LDAPProperties.ExtendedKeyUsage, LDAPProperties.NumSignaturesRequired, LDAPProperties.CertificateApplicationPolicy, LDAPProperties.IssuancePolicies, LDAPProperties.CrossCertificatePair, - LDAPProperties.ApplicationPolicies + LDAPProperties.ApplicationPolicies, LDAPProperties.PKIPrivateKeyFlag }; } } \ No newline at end of file diff --git a/src/CommonLib/Processors/LDAPPropertyProcessor.cs b/src/CommonLib/Processors/LDAPPropertyProcessor.cs index 1fe66830..d641a180 100644 --- a/src/CommonLib/Processors/LDAPPropertyProcessor.cs +++ b/src/CommonLib/Processors/LDAPPropertyProcessor.cs @@ -526,9 +526,15 @@ public static Dictionary ReadCertTemplateProperties(ISearchResul if (entry.GetIntProperty(LDAPProperties.NumSignaturesRequired, out var authorizedSignatures)) props.Add("authorizedsignatures", authorizedSignatures); - props.Add("applicationpolicies", entry.GetArrayProperty(LDAPProperties.ApplicationPolicies)); - props.Add("issuancepolicies", entry.GetArrayProperty(LDAPProperties.IssuancePolicies)); + bool hasUseLegacyProvider = false; + if (entry.GetIntProperty(LDAPProperties.PKIPrivateKeyFlag, out var privateKeyFlagsRaw)) + { + var privateKeyFlags = (PKIPrivateKeyFlag)privateKeyFlagsRaw; + hasUseLegacyProvider = privateKeyFlags.HasFlag(PKIPrivateKeyFlag.USE_LEGACY_PROVIDER); + } + props.Add("applicationpolicies", ParseCertTemplateApplicationPolicies(entry.GetArrayProperty(LDAPProperties.ApplicationPolicies), schemaVersion, hasUseLegacyProvider)); + props.Add("issuancepolicies", entry.GetArrayProperty(LDAPProperties.IssuancePolicies)); // Construct effectiveekus string[] effectiveekus = schemaVersion == 1 & ekus.Length > 0 ? ekus : certificateapplicationpolicy; @@ -590,6 +596,33 @@ public Dictionary ParseAllProperties(ISearchResultEntry entry) return props; } + /// + /// Parse CertTemplate attribute msPKI-RA-Application-Policies + /// + /// + /// + /// + private static string[] ParseCertTemplateApplicationPolicies(string[] applicationPolicies, int schemaVersion, bool hasUseLegacyProvider) + { + if (applicationPolicies == null + || applicationPolicies.Length == 0 + || schemaVersion == 1 + || schemaVersion == 2 + || (schemaVersion == 4 && hasUseLegacyProvider)) { + return applicationPolicies; + } else { + // Format: "Name`Type`Value`Name`Type`Value`..." + // (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-crtd/c55ec697-be3f-4117-8316-8895e4399237) + // Return the Value of Name = "msPKI-RA-Application-Policies" entries + string[] entries = applicationPolicies[0].Split('`'); + return Enumerable.Range(0, entries.Length / 3) + .Select(i => entries.Skip(i * 3).Take(3).ToArray()) + .Where(parts => parts.Length == 3 && parts[0].Equals(LDAPProperties.ApplicationPolicies, StringComparison.OrdinalIgnoreCase)) + .Select(parts => parts[2]) + .ToArray(); + } + } + /// /// Does a best guess conversion of the property to a type useable by the UI ///