Skip to content

Commit

Permalink
Bolster LdapPropertyProcessor tests
Browse files Browse the repository at this point in the history
  • Loading branch information
definitelynotagoblin committed Jan 18, 2024
1 parent 6b9a700 commit 8f226d0
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 4 deletions.
8 changes: 4 additions & 4 deletions src/CommonLib/Processors/LDAPPropertyProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public async Task<ComputerProperties> ReadComputerProperties(ISearchResultEntry
props.AddMany((GetProperties(LDAPProperties.AllowedToDelegateTo, entry)));
compProps.AllowedToDelegate = (await ReadPropertyDelegates(entry)).ToArray();

compProps.AllowedToAct = ReadAllowedToActPrinciples(entry).ToArray();
compProps.AllowedToAct = ReadAllowedToActPrincipals(entry).ToArray();

props.AddMany(GetProperties(LDAPProperties.LastLogon, entry));
props.AddMany(GetProperties(LDAPProperties.LastLogonTimestamp, entry));
Expand All @@ -163,7 +163,7 @@ public async Task<ComputerProperties> ReadComputerProperties(ISearchResultEntry
props.Add(PropertyMap.GetPropertyName(LDAPProperties.SIDHistory), sidHistory.ToArray());
compProps.SidHistory = sidHistory.Select(ssid => ReadSidPrincipal(entry, ssid)).ToArray();

compProps.DumpSMSAPassword = ReadSmsaPrinciples(entry).ToArray();
compProps.DumpSMSAPassword = ReadSmsaPrincipals(entry).ToArray();

compProps.Props = props;

Expand Down Expand Up @@ -248,7 +248,7 @@ public TypedPrincipal ReadSidPrincipal(ISearchResultEntry entry, string sidHisto
/// </summary>
/// <param name="entry"></param>
/// <returns></returns>
public List<TypedPrincipal> ReadAllowedToActPrinciples(ISearchResultEntry entry)
public List<TypedPrincipal> ReadAllowedToActPrincipals(ISearchResultEntry entry)
{
var allowedToActPrincipals = new List<TypedPrincipal>();

Expand All @@ -273,7 +273,7 @@ public List<TypedPrincipal> ReadAllowedToActPrinciples(ISearchResultEntry entry)
/// </summary>
/// <param name="entry"></param>
/// <returns></returns>
public List<TypedPrincipal> ReadSmsaPrinciples(ISearchResultEntry entry)
public List<TypedPrincipal> ReadSmsaPrincipals(ISearchResultEntry entry)
{
var smsaPrincipals = new List<TypedPrincipal>();
var hsa = entry.GetArrayProperty(LDAPProperties.HostServiceAccount);
Expand Down
146 changes: 146 additions & 0 deletions test/unit/LDAPPropertyTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading.Tasks;
using CommonLibTest.Facades;
using FluentAssertions.Specialized;
using Moq;
using SharpHoundCommonLib;
using SharpHoundCommonLib.Enums;
using SharpHoundCommonLib.OutputTypes;
Expand Down Expand Up @@ -914,5 +919,146 @@ public void LDAPPropertyProcessor_ReadSidPrincipal_GetPrincipal()
ObjectType = Label.User
}, principal);
}

// [Fact]
// public void LDAPPropertyProcessor_ReadAllowedToActPrincipals_ReturnsPopulatedList()
// {
// var mock = new MockSearchResultEntry("CN\u003dWIN10,OU\u003dTestOU,DC\u003dtestlab,DC\u003dlocal",
// new Dictionary<string, object>
// {
// {
// "msds-allowedtoactonbehalfofotheridentity",
// Helpers.B64ToBytes("AQUAAAAAAAUVAAAAIE+Qun9GhKV2SBaQUQQAAA==")
// }
// }, "S-1-5-21-3130019616-2776909439-2417379446-1101", Label.Computer);
//
// var mockUtils = new Mock<MockLDAPUtils>();
// var mockSecurityDescriptor = new Mock<ActiveDirectorySecurityDescriptor>();
// var mockRuleDescriptor = new Mock<ActiveDirectoryRuleDescriptor>(MockBehavior.Loose);
// mockRuleDescriptor.Setup(m => m.IdentityReference()).Returns("S-1-5-21-3130019616-2776909439-2417379446-1105");
//
// mockUtils.Setup(x => x.MakeSecurityDescriptor()).Returns(mockSecurityDescriptor.Object);
// mockSecurityDescriptor.Setup(m => m.GetAccessRules(
// It.IsAny<bool>(),
// It.IsAny<bool>(),
// It.IsAny<Type>()))
// .Returns(new List<ActiveDirectoryRuleDescriptor>
// {
// mockRuleDescriptor.Object
// });
//
// var processor = new LDAPPropertyProcessor(mockUtils.Object);
// var principals = processor.ReadAllowedToActPrincipals(mock);
//
// Assert.Contains("S-1-5-21-3130019616-2776909439-2417379446-1105", principals.Select(p => p.ObjectIdentifier));
// Assert.Single(principals);
// }

[Fact]
public void LDAPPropertyProcessor_ReadSmsaPrincipals_ReturnsPopulatedList()
{
var mock = new MockSearchResultEntry("CN\u003dWIN10,OU\u003dTestOU,DC\u003dtestlab,DC\u003dlocal",
new Dictionary<string, object>
{
{
"msds-hostserviceaccount", new[]
{
"CN=dfm,CN=Users,DC=testlab,DC=local",
"CN=krbtgt,CN=Users,DC=testlab,DC=local"
}
}
}, "S-1-5-21-3130019616-2776909439-2417379446-1101", Label.Computer);

var processor = new LDAPPropertyProcessor(new MockLDAPUtils());
var smsaPrincipals = processor.ReadSmsaPrincipals(mock);
var sids = smsaPrincipals.Select(p => p.ObjectIdentifier);

Assert.Single("S-1-5-21-3130019616-2776909439-2417379446-1105", sids);
Assert.Single("S-1-5-21-3130019616-2776909439-2417379446-502", sids);
}

public static IEnumerable<object[]> ServicePrincipalNamesData =>
new List<object[]>
{
new object[]
{
new[]
{
"WSMAN/WIN10",
"WSMAN/WIN10.testlab.local",
"RestrictedKrbHost/WIN10",
"HOST/WIN10",
"RestrictedKrbHost/WIN10.testlab.local",
"HOST/WIN10.testlab.local"
},
true
},
new object[]
{
new string[] { },
false
}
};

[Theory]
[MemberData(nameof(ServicePrincipalNamesData))]
public void LDAPPropertyProcessor_GetProperties_ServicePrincipalNames(object property, bool expectedHasspn)
{
var mock = new MockSearchResultEntry("CN\u003dWIN10,OU\u003dTestOU,DC\u003dtestlab,DC\u003dlocal",
new Dictionary<string, object>
{
{
"serviceprincipalname", property
}
}, "S-1-5-21-3130019616-2776909439-2417379446-1101", Label.Computer);

var props = LDAPPropertyProcessor.GetProperties(LDAPProperties.ServicePrincipalNames, mock);

Assert.Single(props.Keys, "serviceprincipalnames");
var propPrincipals = props["serviceprincipalnames"] as string[];
foreach (var principal in mock.GetArrayProperty("serviceprincipalname"))
{
Assert.Single(propPrincipals, principal);
}

Assert.Single(props.Keys, "hasspn");
Assert.Equal(expectedHasspn, (bool)props["hasspn"]);
}

public static IEnumerable<object[]> UserAccessControlData =>
new List<object[]>
{
new object[]
{
((int)(UacFlags.NotDelegated | UacFlags.AccountDisable)).ToString(),
new Dictionary<string, bool> {{ "sensitive", true }, { "enabled", false }}
},
new object[]
{
((int)(UacFlags.ServerTrustAccount | UacFlags.PasswordNotRequired | UacFlags.TrustedForDelegation)).ToString(),
new Dictionary<string, bool> {{ "isdc", true }, { "passwordnotreqd", true }, { "unconstraineddelegation", true }, { "enabled", true }}
},
};

[Theory]
[MemberData(nameof(UserAccessControlData))]
public void LDAPPropertyProcessor_GetProperties_UserAccountControl(string property, Dictionary<string, bool> expectedFlags)
{
var mock = new MockSearchResultEntry("CN\u003dWIN10,OU\u003dTestOU,DC\u003dtestlab,DC\u003dlocal",
new Dictionary<string, object>
{
{
"useraccountcontrol", property
}
}, "S-1-5-21-3130019616-2776909439-2417379446-1101", Label.User);

var props = LDAPPropertyProcessor.GetProperties(LDAPProperties.UserAccountControl, mock);

foreach (var flag in props)
{
var expectedFlag = expectedFlags.ContainsKey(flag.Key) && expectedFlags[flag.Key];
Assert.Equal(expectedFlag, (bool)flag.Value);
}
}
}
}

0 comments on commit 8f226d0

Please sign in to comment.