Skip to content

Commit

Permalink
Merge branch 'v3' into anemeth/ldap-property-map
Browse files Browse the repository at this point in the history
  • Loading branch information
definitelynotagoblin authored Jan 22, 2024
2 parents 0089492 + 0a08d00 commit 01ce4dd
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 11 deletions.
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM mono:6.12.0

# Install .NET SDK
ENV DOTNET_VERSION=5.0

RUN curl -sSL https://dot.net/v1/dotnet-install.sh \
| bash -s -- -Channel $DOTNET_VERSION -InstallDir /usr/share/dotnet \
&& ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet

WORKDIR /build

CMD [ "dotnet", "build" ]

## Build Docker image (one time):
# docker build -t shc-build . --no-cache

## Build solution (every time):
# docker run --rm -v .:/build shc-build
2 changes: 1 addition & 1 deletion src/CommonLib/Cache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ internal static void AddSidToDomain(string key, string value)
/// <returns></returns>
internal static bool GetDomainSidMapping(string key, out string value)
{
if (CacheInstance != null) return CacheInstance.MachineSidCache.TryGetValue(key, out value);
if (CacheInstance != null) return CacheInstance.SIDToDomainCache.TryGetValue(key, out value);
value = null;
return false;
}
Expand Down
24 changes: 24 additions & 0 deletions src/CommonLib/Enums/PKIPrivateKeyFlag.cs
Original file line number Diff line number Diff line change
@@ -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
}
}
1 change: 1 addition & 0 deletions src/CommonLib/LDAPProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion src/CommonLib/LDAPQueries/CommonProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
}
}
24 changes: 18 additions & 6 deletions src/CommonLib/LDAPUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1702,18 +1702,30 @@ private class ResolvedWellKnownPrincipal

public string GetConfigurationPath(string domainName = null)
{
var rootDse = domainName == null
? new DirectoryEntry("LDAP://RootDSE")
: new DirectoryEntry($"LDAP://{NormalizeDomainName(domainName)}/RootDSE");
string path = domainName == null
? "LDAP://RootDSE"
: $"LDAP://{NormalizeDomainName(domainName)}/RootDSE";

DirectoryEntry rootDse;
if (_ldapConfig.Username != null)
rootDse = new DirectoryEntry(path, _ldapConfig.Username, _ldapConfig.Password);
else
rootDse = new DirectoryEntry(path);

return $"{rootDse.Properties["configurationNamingContext"]?[0]}";
}

public string GetSchemaPath(string domainName)
{
var rootDse = domainName == null
? new DirectoryEntry("LDAP://RootDSE")
: new DirectoryEntry($"LDAP://{NormalizeDomainName(domainName)}/RootDSE");
string path = domainName == null
? "LDAP://RootDSE"
: $"LDAP://{NormalizeDomainName(domainName)}/RootDSE";

DirectoryEntry rootDse;
if (_ldapConfig.Username != null)
rootDse = new DirectoryEntry(path, _ldapConfig.Username, _ldapConfig.Password);
else
rootDse = new DirectoryEntry(path);

return $"{rootDse.Properties["schemaNamingContext"]?[0]}";
}
Expand Down
2 changes: 2 additions & 0 deletions src/CommonLib/OutputTypes/Computer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class Computer : OutputBase
public UserRightsAssignmentAPIResult[] UserRights { get; set; } = Array.Empty<UserRightsAssignmentAPIResult>();
public DCRegistryData DCRegistryData { get; set; } = new();
public ComputerStatus Status { get; set; }
public bool IsDC { get; set; }
public string DomainSID { get; set; }
}

public class DCRegistryData
Expand Down
10 changes: 10 additions & 0 deletions src/CommonLib/Processors/GPOLocalGroupProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,11 @@ internal IEnumerable<GroupAction> ProcessGPOXmlFile(string basePath, string gpoD
var domain = s[0];

var res = _utils.ResolveAccountName(name, domain);
if (res == null)
{
_log.LogWarning("Failed to resolve member {memberName}", memberName);
continue;
}
ga.Target = GroupActionTarget.LocalGroup;
ga.TargetSid = res.ObjectIdentifier;
ga.TargetType = res.ObjectType;
Expand All @@ -540,6 +545,11 @@ internal IEnumerable<GroupAction> ProcessGPOXmlFile(string basePath, string gpoD
else
{
var res = _utils.ResolveAccountName(memberName, gpoDomain);
if (res == null)
{
_log.LogWarning("Failed to resolve member {memberName}", memberName);
continue;
}
ga.Target = GroupActionTarget.LocalGroup;
ga.TargetSid = res.ObjectIdentifier;
ga.TargetType = res.ObjectType;
Expand Down
37 changes: 35 additions & 2 deletions src/CommonLib/Processors/LDAPPropertyProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,15 @@ public static Dictionary<string, object> 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;
Expand Down Expand Up @@ -658,6 +664,33 @@ private static Dictionary<T, bool> ReadFlags<T>(T flags)
);
}

/// <summary>
/// Parse CertTemplate attribute msPKI-RA-Application-Policies
/// </summary>
/// <param name="applicationPolicies"></param>
/// <param name="schemaVersion"></param>
/// <param name="hasUseLegacyProvider"></param>
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();
}
}

/// <summary>
/// Does a best guess conversion of the property to a type useable by the UI
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/CommonLib/SharpHoundCommonLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<PackageDescription>Common library for C# BloodHound enumeration tasks</PackageDescription>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
<RepositoryUrl>https://github.com/BloodHoundAD/SharpHoundCommon</RepositoryUrl>
<Version>3.1.1</Version>
<Version>3.1.2</Version>
<AssemblyName>SharpHoundCommonLib</AssemblyName>
<RootNamespace>SharpHoundCommonLib</RootNamespace>
</PropertyGroup>
Expand Down

0 comments on commit 01ce4dd

Please sign in to comment.