From 446c0f49b8943b847ab345b22b003fdb3c30419b Mon Sep 17 00:00:00 2001 From: Elad Shamir <8843083+eladshamir@users.noreply.github.com> Date: Thu, 15 Oct 2026 12:29:19 -0500 Subject: [PATCH 1/4] feat: collect Azure VM IDs (#229) Co-authored-by: Michael Cuomo <10276078+mykeelium@users.noreply.github.com> --- src/Client/Enums.cs | 3 ++- src/Options.cs | 6 ++++-- src/PowerShell/Template.ps1 | 1 + src/Runtime/ObjectProcessors.cs | 26 +++++++++++++++++++------- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/Client/Enums.cs b/src/Client/Enums.cs index cec91c5e..410dd539 100644 --- a/src/Client/Enums.cs +++ b/src/Client/Enums.cs @@ -32,8 +32,9 @@ public enum CollectionMethodOptions SmbInfo, NTLMRegistry, Site, + AzureVM, // Re-introduce this when we're ready for Event Log collection // EventLogs, All } -} \ No newline at end of file +} diff --git a/src/Options.cs b/src/Options.cs index 1c22af4d..27b61d32 100644 --- a/src/Options.cs +++ b/src/Options.cs @@ -14,7 +14,7 @@ public class Options // Options that affect what is collected [Option('c', "collectionmethods", Default = new[] { "Default" }, HelpText = - "Collection Methods: Group, LocalGroup, LocalAdmin, RDP, DCOM, PSRemote, Session, Trusts, ACL, Container, ComputerOnly, GPOLocalGroup, LoggedOn, ObjectProps, SPNTargets, UserRights, Default, DCOnly, CARegistry, DCRegistry, CertServices, Site, WebClientService, LdapServices, SmbInfo, NTLMRegistry, All")] + "Collection Methods: Group, LocalGroup, LocalAdmin, RDP, DCOM, PSRemote, Session, Trusts, ACL, Container, ComputerOnly, GPOLocalGroup, LoggedOn, ObjectProps, SPNTargets, UserRights, Default, DCOnly, CARegistry, DCRegistry, CertServices, Site, WebClientService, LdapServices, SmbInfo, NTLMRegistry, AzureVM, All")] public IEnumerable CollectionMethods { get; set; } [Option('d', "domain", Default = null, HelpText = "Specify domain to enumerate")] @@ -211,7 +211,9 @@ internal bool ResolveCollectionMethods(ILogger logger, out CollectionMethod reso CollectionMethodOptions.LdapServices => CollectionMethod.LdapServices, CollectionMethodOptions.SmbInfo => CollectionMethod.SmbInfo, CollectionMethodOptions.NTLMRegistry => CollectionMethod.NTLMRegistry, - CollectionMethodOptions.Site => CollectionMethod.Site, + CollectionMethodOptions.Site => CollectionMethod.Site, + CollectionMethodOptions.AzureVM => CollectionMethod.AzureVM, + // Re-introduce this when we're ready for Event Log collection // CollectionMethodOptions.EventLogs => CollectionMethod.EventLogs, CollectionMethodOptions.All => CollectionMethod.All, diff --git a/src/PowerShell/Template.ps1 b/src/PowerShell/Template.ps1 index 676e7892..a09984e3 100644 --- a/src/PowerShell/Template.ps1 +++ b/src/PowerShell/Template.ps1 @@ -35,6 +35,7 @@ UserRights - Collect User Rights Assignment from domain computers (needs admin) CARegistry - Collect ADCS properties from registry of Certificate Authority servers DCRegistry - Collect properties from registry of Domain Controller servers + AzureVM - Read the Azure guest VmId and add it to each Computer object's properties CertServices - Collect ADCS objects from Certificate Services Site - Collect AD site, site server, and site subnet data All - Collect all data diff --git a/src/Runtime/ObjectProcessors.cs b/src/Runtime/ObjectProcessors.cs index a60e7861..f5af67ee 100644 --- a/src/Runtime/ObjectProcessors.cs +++ b/src/Runtime/ObjectProcessors.cs @@ -385,16 +385,18 @@ await HandleCompStatusEvent(new CSVComputerStatus { if (_methods.HasFlag(CollectionMethod.NTLMRegistry)) { await _context.DoDelay(); - var processor = _registryProcessorMap.GetOrAdd( - resolvedSearchResult.DomainSid, - _ => new Lazy(() => { - var newProcessor = new RegistryProcessor(null, new StrategyExecutor(), resolvedSearchResult.Domain); - newProcessor.ComputerStatusEvent += HandleCompStatusEvent; - return newProcessor; - })).Value; + var processor = GetRegistryProcessor(resolvedSearchResult); ret.NTLMRegistryData = await processor.ReadRegistrySettings(resolvedSearchResult.DisplayName); } + if (_methods.HasFlag(CollectionMethod.AzureVM)) { + await _context.DoDelay(); + var processor = GetRegistryProcessor(resolvedSearchResult); + var vmId = await processor.ReadAzureVmId(apiName); + if (vmId.Collected) + ret.Properties["azvmid"] = vmId.Result; + } + if (_methods.HasFlag(CollectionMethod.WebClientService)) { ret.IsWebClientRunning = await _webClientProcessor.IsWebClientRunning(apiName); } @@ -437,6 +439,16 @@ await HandleCompStatusEvent(new CSVComputerStatus { return ret; } + private RegistryProcessor GetRegistryProcessor(ResolvedSearchResult resolvedSearchResult) { + return _registryProcessorMap.GetOrAdd( + resolvedSearchResult.DomainSid, + _ => new Lazy(() => { + var processor = new RegistryProcessor(null, new StrategyExecutor(), resolvedSearchResult.Domain); + processor.ComputerStatusEvent += HandleCompStatusEvent; + return processor; + })).Value; + } + private async void ProcessDomainController(ResolvedSearchResult resolvedSearchResult, Computer ret, string apiName) { _log.LogDebug("Processing DC: {dc}", apiName); From 2aba3934b0f600c00530016240c36ddc6798201a Mon Sep 17 00:00:00 2001 From: Michael Cuomo Date: Thu, 15 Oct 2026 13:55:39 -0400 Subject: [PATCH 2/4] chore: bring in line with SharpHoundCommon changes - BED-9763 --- src/Client/Enums.cs | 2 +- src/Options.cs | 4 ++-- src/PowerShell/Template.ps1 | 2 +- src/Runtime/ObjectProcessors.cs | 12 ++++++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Client/Enums.cs b/src/Client/Enums.cs index 410dd539..1cd0e61b 100644 --- a/src/Client/Enums.cs +++ b/src/Client/Enums.cs @@ -32,7 +32,7 @@ public enum CollectionMethodOptions SmbInfo, NTLMRegistry, Site, - AzureVM, + AzureRegistry, // Re-introduce this when we're ready for Event Log collection // EventLogs, All diff --git a/src/Options.cs b/src/Options.cs index 27b61d32..75a33850 100644 --- a/src/Options.cs +++ b/src/Options.cs @@ -14,7 +14,7 @@ public class Options // Options that affect what is collected [Option('c', "collectionmethods", Default = new[] { "Default" }, HelpText = - "Collection Methods: Group, LocalGroup, LocalAdmin, RDP, DCOM, PSRemote, Session, Trusts, ACL, Container, ComputerOnly, GPOLocalGroup, LoggedOn, ObjectProps, SPNTargets, UserRights, Default, DCOnly, CARegistry, DCRegistry, CertServices, Site, WebClientService, LdapServices, SmbInfo, NTLMRegistry, AzureVM, All")] + "Collection Methods: Group, LocalGroup, LocalAdmin, RDP, DCOM, PSRemote, Session, Trusts, ACL, Container, ComputerOnly, GPOLocalGroup, LoggedOn, ObjectProps, SPNTargets, UserRights, Default, DCOnly, CARegistry, DCRegistry, CertServices, Site, WebClientService, LdapServices, SmbInfo, NTLMRegistry, AzureRegistry, All")] public IEnumerable CollectionMethods { get; set; } [Option('d', "domain", Default = null, HelpText = "Specify domain to enumerate")] @@ -212,7 +212,7 @@ internal bool ResolveCollectionMethods(ILogger logger, out CollectionMethod reso CollectionMethodOptions.SmbInfo => CollectionMethod.SmbInfo, CollectionMethodOptions.NTLMRegistry => CollectionMethod.NTLMRegistry, CollectionMethodOptions.Site => CollectionMethod.Site, - CollectionMethodOptions.AzureVM => CollectionMethod.AzureVM, + CollectionMethodOptions.AzureRegistry => CollectionMethod.AzureRegistry, // Re-introduce this when we're ready for Event Log collection // CollectionMethodOptions.EventLogs => CollectionMethod.EventLogs, diff --git a/src/PowerShell/Template.ps1 b/src/PowerShell/Template.ps1 index a09984e3..53c8ded3 100644 --- a/src/PowerShell/Template.ps1 +++ b/src/PowerShell/Template.ps1 @@ -35,7 +35,7 @@ UserRights - Collect User Rights Assignment from domain computers (needs admin) CARegistry - Collect ADCS properties from registry of Certificate Authority servers DCRegistry - Collect properties from registry of Domain Controller servers - AzureVM - Read the Azure guest VmId and add it to each Computer object's properties + AzureRegistry - Read Registry for Azure specific values such as the Azure guest VmId and add it to each Computer object's properties CertServices - Collect ADCS objects from Certificate Services Site - Collect AD site, site server, and site subnet data All - Collect all data diff --git a/src/Runtime/ObjectProcessors.cs b/src/Runtime/ObjectProcessors.cs index f5af67ee..b6abc4a1 100644 --- a/src/Runtime/ObjectProcessors.cs +++ b/src/Runtime/ObjectProcessors.cs @@ -389,12 +389,16 @@ await HandleCompStatusEvent(new CSVComputerStatus { ret.NTLMRegistryData = await processor.ReadRegistrySettings(resolvedSearchResult.DisplayName); } - if (_methods.HasFlag(CollectionMethod.AzureVM)) { + if (_methods.HasFlag(CollectionMethod.AzureRegistry)) { await _context.DoDelay(); var processor = GetRegistryProcessor(resolvedSearchResult); - var vmId = await processor.ReadAzureVmId(apiName); - if (vmId.Collected) - ret.Properties["azvmid"] = vmId.Result; + + // Currently these values are added as properties. Future work is need to refine to a more dedicated + // portion of the return object, as more collected values fill this returned object + var azureRegistryData = await processor.ReadAzureRegistryData(apiName); + if (azureRegistryData.Collected) { + ret.Properties["azvmid"] = azureRegistryData.Result.VmId; + } } if (_methods.HasFlag(CollectionMethod.WebClientService)) { From f3d2d46be8f708400b188841223cadfadd957c10 Mon Sep 17 00:00:00 2001 From: Michael Cuomo Date: Thu, 15 Oct 2026 14:40:47 -0400 Subject: [PATCH 3/4] chore: clean up comment - BED-9763 --- src/Runtime/ObjectProcessors.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Runtime/ObjectProcessors.cs b/src/Runtime/ObjectProcessors.cs index b6abc4a1..66c975fd 100644 --- a/src/Runtime/ObjectProcessors.cs +++ b/src/Runtime/ObjectProcessors.cs @@ -393,8 +393,8 @@ await HandleCompStatusEvent(new CSVComputerStatus { await _context.DoDelay(); var processor = GetRegistryProcessor(resolvedSearchResult); - // Currently these values are added as properties. Future work is need to refine to a more dedicated - // portion of the return object, as more collected values fill this returned object + // Currently these values are added as properties. Future work is needed to refine to a more dedicated + // portion of the return object, as more collected values fill this returned object. var azureRegistryData = await processor.ReadAzureRegistryData(apiName); if (azureRegistryData.Collected) { ret.Properties["azvmid"] = azureRegistryData.Result.VmId; From c04a6992238d3f38017a8101e1d93614ecee669a Mon Sep 17 00:00:00 2001 From: Michael Cuomo Date: Thu, 15 Oct 2026 19:53:41 -0400 Subject: [PATCH 4/4] fix: remove AzureRegistry from Stealth collection - BED-9763 --- src/Options.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Options.cs b/src/Options.cs index 75a33850..91e816ed 100644 --- a/src/Options.cs +++ b/src/Options.cs @@ -280,6 +280,11 @@ internal bool ResolveCollectionMethods(ILogger logger, out CollectionMethod reso updates.Add("[-] Removed NTLMRegistry Collection"); } + if ((resolved & CollectionMethod.AzureRegistry) != 0) { + resolved ^= CollectionMethod.AzureRegistry; + updates.Add("[-] Removed AzureRegistry Collection"); + } + if (localGroupRemoved) { resolved |= CollectionMethod.GPOLocalGroup;