Skip to content

Commit

Permalink
Merge branch 'v4' into BED-4928
Browse files Browse the repository at this point in the history
  • Loading branch information
rvazarkar authored Oct 22, 2024
2 parents 1e81e8c + 276237f commit 20dda86
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 25 deletions.
3 changes: 2 additions & 1 deletion src/CommonLib/Enums/LDAPProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public static class LDAPProperties
public const string ServicePack = "operatingsystemservicepack";
public const string DNSHostName = "dnshostname";
public const string LAPSExpirationTime = "mslaps-passwordexpirationtime";
public const string LAPSPassword = "mslaps-password";
public const string LAPSPlaintextPassword = "ms-laps-password";
public const string LAPSEncryptedPassword = "ms-laps-encryptedpassword";
public const string LegacyLAPSExpirationTime = "ms-mcs-admpwdexpirationtime";
public const string LegacyLAPSPassword = "ms-mcs-admpwd";
public const string Members = "member";
Expand Down
1 change: 1 addition & 0 deletions src/CommonLib/OutputTypes/Computer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class Computer : OutputBase
public DCRegistryData DCRegistryData { get; set; } = new();
public ComputerStatus Status { get; set; }
public bool IsDC { get; set; }
public bool UnconstrainedDelegation { get; set; }
public string DomainSID { get; set; }
}

Expand Down
2 changes: 2 additions & 0 deletions src/CommonLib/OutputTypes/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ public class User : OutputBase
public string PrimaryGroupSID { get; set; }
public TypedPrincipal[] HasSIDHistory { get; set; } = Array.Empty<TypedPrincipal>();
public SPNPrivilege[] SPNTargets { get; set; } = Array.Empty<SPNPrivilege>();
public bool UnconstrainedDelegation { get; set; }
public string DomainSID { get; set; }
}
}
64 changes: 41 additions & 23 deletions src/CommonLib/Processors/ACLProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ private async Task BuildGuidCache(string domain) {
}

name = name.ToLower();

string guid;
try
{
Expand All @@ -83,7 +84,7 @@ private async Task BuildGuidCache(string domain) {
continue;
}

if (name is LDAPProperties.LAPSPassword or LDAPProperties.LegacyLAPSPassword) {
if (name is LDAPProperties.LAPSPlaintextPassword or LDAPProperties.LAPSEncryptedPassword or LDAPProperties.LegacyLAPSPassword) {
_log.LogInformation("Found GUID for ACL Right {Name}: {Guid} in domain {Domain}", name, guid, domain);
_guidMap.TryAdd(guid, name);
}
Expand Down Expand Up @@ -145,17 +146,21 @@ internal static string CalculateInheritanceHash(string identityReference, Active
string aceType, string inheritedObjectType) {
var hash = identityReference + rights + aceType + inheritedObjectType;
/*
* We're using MD5 because its fast and this data isn't cryptographically important.
* We're using SHA1 because its fast and this data isn't cryptographically important.
* Additionally, the chances of a collision in our data size is miniscule and irrelevant.
* We cannot use MD5 as it is not FIPS compliant and environments can enforce this setting
*/
using (var md5 = MD5.Create()) {
var bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(hash));
var builder = new StringBuilder();
foreach (var b in bytes) {
builder.Append(b.ToString("x2"));
try
{
using (var sha1 = SHA1.Create())
{
var bytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(hash));
return BitConverter.ToString(bytes).Replace("-", string.Empty).ToUpper();
}

return builder.ToString();
}
catch
{
return "";
}
}

Expand Down Expand Up @@ -219,8 +224,12 @@ public IEnumerable<string> GetInheritedAceHashes(byte[] ntSecurityDescriptor, st
//Lowercase this just in case. As far as I know it should always come back that way anyways, but better safe than sorry
var aceType = ace.ObjectType().ToString().ToLower();
var inheritanceType = ace.InheritedObjectType();

yield return CalculateInheritanceHash(ir, aceRights, aceType, inheritanceType);

var hash = CalculateInheritanceHash(ir, aceRights, aceType, inheritanceType);
if (!string.IsNullOrEmpty(hash))
{
yield return hash;
}
}
}

Expand Down Expand Up @@ -263,15 +272,17 @@ public async IAsyncEnumerable<ACE> ProcessACL(byte[] ntSecurityDescriptor, strin
PrincipalType = resolvedOwner.ObjectType,
PrincipalSID = resolvedOwner.ObjectIdentifier,
RightName = EdgeNames.Owns,
IsInherited = false
IsInherited = false,
InheritanceHash = ""
};
} else {
_log.LogTrace("Failed to resolve owner for {Name}", objectName);
yield return new ACE {
PrincipalType = Label.Base,
PrincipalSID = ownerSid,
RightName = EdgeNames.Owns,
IsInherited = false
IsInherited = false,
InheritanceHash = ""
};
}
}
Expand Down Expand Up @@ -306,8 +317,6 @@ public async IAsyncEnumerable<ACE> ProcessACL(byte[] ntSecurityDescriptor, strin
aceInheritanceHash = CalculateInheritanceHash(ir, aceRights, aceType, ace.InheritedObjectType());
}

_guidMap.TryGetValue(aceType, out var mappedGuid);

_log.LogTrace("Processing ACE with rights {Rights} and guid {GUID} on object {Name}", aceRights,
aceType, objectName);

Expand Down Expand Up @@ -420,14 +429,23 @@ public async IAsyncEnumerable<ACE> ProcessACL(byte[] ntSecurityDescriptor, strin
RightName = EdgeNames.AllExtendedRights,
InheritanceHash = aceInheritanceHash
};
else if (mappedGuid is LDAPProperties.LegacyLAPSPassword or LDAPProperties.LAPSPassword)
yield return new ACE {
PrincipalType = resolvedPrincipal.ObjectType,
PrincipalSID = resolvedPrincipal.ObjectIdentifier,
IsInherited = inherited,
RightName = EdgeNames.ReadLAPSPassword,
InheritanceHash = aceInheritanceHash
};
else if (_guidMap.TryGetValue(aceType, out var lapsAttribute))
{
// Compare the retrieved attribute name against LDAPProperties values
if (lapsAttribute == LDAPProperties.LegacyLAPSPassword ||
lapsAttribute == LDAPProperties.LAPSPlaintextPassword ||
lapsAttribute == LDAPProperties.LAPSEncryptedPassword)
{
yield return new ACE
{
PrincipalType = resolvedPrincipal.ObjectType,
PrincipalSID = resolvedPrincipal.ObjectIdentifier,
IsInherited = inherited,
RightName = EdgeNames.ReadLAPSPassword,
InheritanceHash = aceInheritanceHash
};
}
}
}
} else if (objectType == Label.CertTemplate) {
if (aceType is ACEGuids.AllGuid or "")
Expand Down
6 changes: 6 additions & 0 deletions src/CommonLib/Processors/LdapPropertyProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ public async Task<UserProperties> ReadUserProperties(IDirectoryObject entry, str
props.Add("passwordcantchange", uacFlags.HasFlag(UacFlags.PasswordCantChange));
props.Add("passwordexpired", uacFlags.HasFlag(UacFlags.PasswordExpired));

userProps.UnconstrainedDelegation = uacFlags.HasFlag(UacFlags.TrustedForDelegation);

var comps = new List<TypedPrincipal>();
if (uacFlags.HasFlag(UacFlags.TrustedToAuthForDelegation) &&
entry.TryGetArrayProperty(LDAPProperties.AllowedToDelegateTo, out var delegates)) {
Expand Down Expand Up @@ -342,6 +344,8 @@ public async Task<ComputerProperties> ReadComputerProperties(IDirectoryObject en
props.Add("lockedout", flags.HasFlag(UacFlags.Lockout));
props.Add("passwordexpired", flags.HasFlag(UacFlags.PasswordExpired));

compProps.UnconstrainedDelegation = flags.HasFlag(UacFlags.TrustedForDelegation);

var encryptionTypes = ConvertEncryptionTypes(entry.GetProperty(LDAPProperties.SupportedEncryptionTypes));
props.Add("supportedencryptiontypes", encryptionTypes);

Expand Down Expand Up @@ -929,6 +933,7 @@ public class UserProperties {
public Dictionary<string, object> Props { get; set; } = new();
public TypedPrincipal[] AllowedToDelegate { get; set; } = Array.Empty<TypedPrincipal>();
public TypedPrincipal[] SidHistory { get; set; } = Array.Empty<TypedPrincipal>();
public bool UnconstrainedDelegation { get; set; }
}

public class ComputerProperties {
Expand All @@ -937,6 +942,7 @@ public class ComputerProperties {
public TypedPrincipal[] AllowedToAct { get; set; } = Array.Empty<TypedPrincipal>();
public TypedPrincipal[] SidHistory { get; set; } = Array.Empty<TypedPrincipal>();
public TypedPrincipal[] DumpSMSAPassword { get; set; } = Array.Empty<TypedPrincipal>();
public bool UnconstrainedDelegation { get; set; }
}

public class IssuancePolicyProperties {
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>4.0.7</Version>
<Version>4.0.8</Version>
<AssemblyName>SharpHoundCommonLib</AssemblyName>
<RootNamespace>SharpHoundCommonLib</RootNamespace>
</PropertyGroup>
Expand Down

0 comments on commit 20dda86

Please sign in to comment.