From f24ce184d8ee0ba2daf3c355c175d7f11986f24c Mon Sep 17 00:00:00 2001 From: Martin Sohn Christensen Date: Wed, 22 Jul 2026 15:57:30 +0200 Subject: [PATCH] feat: collect msDS-aADObjectID for users and groups --- src/CommonLib/Enums/LDAPProperties.cs | 1 + src/CommonLib/LdapQueries/CommonProperties.cs | 4 ++-- src/CommonLib/Processors/LdapPropertyProcessor.cs | 10 ++++++++++ test/unit/LdapPropertyTests.cs | 10 ++++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/CommonLib/Enums/LDAPProperties.cs b/src/CommonLib/Enums/LDAPProperties.cs index 0bf6b726e..d8884da33 100644 --- a/src/CommonLib/Enums/LDAPProperties.cs +++ b/src/CommonLib/Enums/LDAPProperties.cs @@ -32,6 +32,7 @@ public static class LDAPProperties public const string UserPassword = "userpassword"; public const string SIDHistory = "sidhistory"; public const string AllowedToActOnBehalfOfOtherIdentity = "msds-allowedtoactonbehalfofotheridentity"; + public const string AADObjectID = "msds-aadobjectid"; public const string OperatingSystem = "operatingsystem"; public const string ServicePack = "operatingsystemservicepack"; public const string DNSHostName = "dnshostname"; diff --git a/src/CommonLib/LdapQueries/CommonProperties.cs b/src/CommonLib/LdapQueries/CommonProperties.cs index 508b5490c..13e8363a7 100644 --- a/src/CommonLib/LdapQueries/CommonProperties.cs +++ b/src/CommonLib/LdapQueries/CommonProperties.cs @@ -54,7 +54,7 @@ public static class CommonProperties LDAPProperties.DomainFunctionalLevel, LDAPProperties.ObjectGUID, LDAPProperties.Name, LDAPProperties.GroupPolicyOptions, LDAPProperties.AllowedToDelegateTo, LDAPProperties.AllowedToActOnBehalfOfOtherIdentity, LDAPProperties.WhenCreated, - LDAPProperties.HostServiceAccount, LDAPProperties.UnixUserPassword, LDAPProperties.MsSFU30Password, + LDAPProperties.AADObjectID, LDAPProperties.HostServiceAccount, LDAPProperties.UnixUserPassword, LDAPProperties.MsSFU30Password, LDAPProperties.UnicodePassword, LDAPProperties.ProfilePath, LDAPProperties.ScriptPath, LDAPProperties.ExpirePasswordsOnSmartCardOnlyAccounts, LDAPProperties.MachineAccountQuota, LDAPProperties.SupportedEncryptionTypes, LDAPProperties.DSHeuristics, @@ -99,4 +99,4 @@ public static class CommonProperties LDAPProperties.HomeDirectory, LDAPProperties.ScriptPath, LDAPProperties.ProfilePath }; } -} \ No newline at end of file +} diff --git a/src/CommonLib/Processors/LdapPropertyProcessor.cs b/src/CommonLib/Processors/LdapPropertyProcessor.cs index 2625b62f0..19b3df40e 100644 --- a/src/CommonLib/Processors/LdapPropertyProcessor.cs +++ b/src/CommonLib/Processors/LdapPropertyProcessor.cs @@ -16,6 +16,7 @@ namespace SharpHoundCommonLib.Processors { public class LdapPropertyProcessor { + private const string AADObjectIDProperty = "aadobjectid"; private static readonly HashSet ReservedAttributes = new(); public delegate Task ComputerStatusDelegate(CSVComputerStatus status); public event ComputerStatusDelegate ComputerStatusEvent; @@ -71,6 +72,13 @@ private static Dictionary GetCommonProps(IDirectoryObject entry) return ret; } + private static void AddAADObjectIDProperty(Dictionary props, IDirectoryObject entry) { + if (entry.TryGetProperty(LDAPProperties.AADObjectID, out var aadObjectID) && + !string.IsNullOrWhiteSpace(aadObjectID)) { + props[AADObjectIDProperty] = aadObjectID.Trim().ToUpperInvariant(); + } + } + /// /// Reads specific LDAP properties related to Domains /// @@ -212,6 +220,7 @@ public async Task ReadGroupPropertiesAsync(IDirectoryObject ent { var groupProperties = new GroupProperties(); var props = GetCommonProps(entry); + AddAADObjectIDProperty(props, entry); entry.TryGetLongProperty(LDAPProperties.AdminCount, out var ac); props.Add("admincount", ac != 0); entry.TryGetLongProperty(LDAPProperties.GroupType, out var groupType); @@ -249,6 +258,7 @@ public Task public async Task ReadUserProperties(IDirectoryObject entry, string domain) { var userProps = new UserProperties(); var props = GetCommonProps(entry); + AddAADObjectIDProperty(props, entry); if (entry.TryGetLongProperty(LDAPProperties.UserAccountControl, out var uac)) { var uacFlags = (UacFlags)uac; diff --git a/test/unit/LdapPropertyTests.cs b/test/unit/LdapPropertyTests.cs index 9d3053d13..0fd4ccc05 100644 --- a/test/unit/LdapPropertyTests.cs +++ b/test/unit/LdapPropertyTests.cs @@ -126,6 +126,7 @@ public async Task LDAPPropertyProcessor_ReadGroupProperties_TestGoodData() new Dictionary { {"description", "Test"}, + {"msds-aadobjectid", "b9b08d75-5bca-4fd4-a75d-7de9fdbe0cd3"}, {"admincount", "1"} }, "S-1-5-21-3130019616-2776909439-2417379446-512",""); var processor = new LdapPropertyProcessor(new MockLdapUtils()); @@ -136,6 +137,9 @@ public async Task LDAPPropertyProcessor_ReadGroupProperties_TestGoodData() Assert.Equal("Test", test["description"] as string); Assert.Contains("admincount", test.Keys); Assert.True((bool)test["admincount"]); + Assert.Contains("aadobjectid", test.Keys); + Assert.Equal("B9B08D75-5BCA-4FD4-A75D-7DE9FDBE0CD3", test["aadobjectid"] as string); + Assert.DoesNotContain("msds-aadobjectid", test.Keys); } [Fact] @@ -319,6 +323,7 @@ public async Task LDAPPropertyProcessor_ReadUserProperties_HappyPath() {"lastlogontimestamp", "132670318095676525"}, {"homedirectory", @"\\win10\testdir"}, {"mail", "test@testdomain.com"}, + {"msds-aadobjectid", "3b6ace22-ae1c-49b9-bb40-66b30c5bc9e5"}, { "serviceprincipalname", new[] { @@ -355,6 +360,9 @@ public async Task LDAPPropertyProcessor_ReadUserProperties_HappyPath() Assert.Equal(@"\\win10\testdir", props["homedirectory"] as string); Assert.Contains("email", keys); Assert.Equal("test@testdomain.com", props["email"] as string); + Assert.Contains("aadobjectid", keys); + Assert.Equal("3B6ACE22-AE1C-49B9-BB40-66B30C5BC9E5", props["aadobjectid"] as string); + Assert.DoesNotContain("msds-aadobjectid", keys); //UAC stuff Assert.Contains("sensitive", keys); @@ -1074,6 +1082,7 @@ public void LDAPPropertyProcessor_ParseAllProperties() {"name", "NTAUTHCERTIFICATES@DUMPSTER.FIRE"}, {"domainsid", "S-1-5-21-2697957641-2271029196-387917394"}, {"whencreated", 1683986131}, + {"msds-aadobjectid", "b9b08d75-5bca-4fd4-a75d-7de9fdbe0cd3"}, {LDAPProperties.DSASignature, "jkr"} }, "", "2F9F3630-F46A-49BF-B186-6629994EBCF9"); @@ -1085,6 +1094,7 @@ public void LDAPPropertyProcessor_ParseAllProperties() Assert.DoesNotContain("description", keys); Assert.DoesNotContain("whencreated", keys); Assert.DoesNotContain("name", keys); + Assert.DoesNotContain("msds-aadobjectid", keys); Assert.DoesNotContain(LDAPProperties.DSASignature, keys); Assert.Contains("domainsid", keys);