From 586571470c707bc8133bde358e94df583c7c7468 Mon Sep 17 00:00:00 2001 From: Lee Christensen Date: Fri, 20 Dec 2024 00:45:23 -0800 Subject: [PATCH] add Registry collection --- Properties/launchSettings.json | 8 ++++++++ README.md | 2 +- Sharphound.csproj | 14 +++++++++----- Sharphound.sln | 20 ++++++++++++++++++-- src/Client/Enums.cs | 1 + src/Options.cs | 9 ++++++++- src/Runtime/CollectionTask.cs | 9 +++++++-- src/Runtime/LDAPConsumer.cs | 17 ++++++++++++----- src/Runtime/ObjectProcessors.cs | 12 +++++++++++- 9 files changed, 75 insertions(+), 17 deletions(-) create mode 100644 Properties/launchSettings.json diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..bc7a3f1 --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Sharphound": { + "commandName": "Project", + "commandLineArgs": "-C All" + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 1f50512..b69e449 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ dotnet build The listing below details the CLI arguments SharpHound supports. Additional details about these options can be found in the [BloodHound CE Collection documentation](https://support.bloodhoundenterprise.io/hc/en-us/articles/17481375424795-All-SharpHound-Community-Edition-Flags-Explained). ``` -c, --collectionmethods (Default: Default) Collection Methods: Container, Group, LocalGroup, GPOLocalGroup, - Session, LoggedOn, ObjectProps, ACL, ComputerOnly, Trusts, Default, RDP, DCOM, DCOnly, UserRights, CARegistry, DCRegistry, CertServices + Session, LoggedOn, ObjectProps, ACL, ComputerOnly, Trusts, Default, RDP, DCOM, DCOnly, UserRights, CARegistry, DCRegistry, CertServices, Registry -d, --domain Specify domain to enumerate diff --git a/Sharphound.csproj b/Sharphound.csproj index ceb3265..66ffc5f 100644 --- a/Sharphound.csproj +++ b/Sharphound.csproj @@ -2,7 +2,7 @@ Exe - net462 + net472 latest full favicon.ico @@ -24,14 +24,18 @@ - - - + + + - + + + + diff --git a/Sharphound.sln b/Sharphound.sln index 003eedc..df34092 100644 --- a/Sharphound.sln +++ b/Sharphound.sln @@ -1,9 +1,17 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31321.278 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34928.147 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sharphound", "Sharphound.csproj", "{90A6822C-4336-433D-923F-F54CE66BA98F}" + ProjectSection(ProjectDependencies) = postProject + {B837B250-80BC-4086-B0DC-E43A956C3D1D} = {B837B250-80BC-4086-B0DC-E43A956C3D1D} + {FDC2F19A-5B0A-4F64-99E0-4CB18D7CD186} = {FDC2F19A-5B0A-4F64-99E0-4CB18D7CD186} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpHoundRPC", "..\sharphoundcommon\src\SharpHoundRPC\SharpHoundRPC.csproj", "{FDC2F19A-5B0A-4F64-99E0-4CB18D7CD186}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpHoundCommonLib", "..\sharphoundcommon\src\CommonLib\SharpHoundCommonLib.csproj", "{B837B250-80BC-4086-B0DC-E43A956C3D1D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +23,14 @@ Global {90A6822C-4336-433D-923F-F54CE66BA98F}.Debug|Any CPU.Build.0 = Debug|Any CPU {90A6822C-4336-433D-923F-F54CE66BA98F}.Release|Any CPU.ActiveCfg = Release|Any CPU {90A6822C-4336-433D-923F-F54CE66BA98F}.Release|Any CPU.Build.0 = Release|Any CPU + {FDC2F19A-5B0A-4F64-99E0-4CB18D7CD186}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDC2F19A-5B0A-4F64-99E0-4CB18D7CD186}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDC2F19A-5B0A-4F64-99E0-4CB18D7CD186}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDC2F19A-5B0A-4F64-99E0-4CB18D7CD186}.Release|Any CPU.Build.0 = Release|Any CPU + {B837B250-80BC-4086-B0DC-E43A956C3D1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B837B250-80BC-4086-B0DC-E43A956C3D1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B837B250-80BC-4086-B0DC-E43A956C3D1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B837B250-80BC-4086-B0DC-E43A956C3D1D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Client/Enums.cs b/src/Client/Enums.cs index 5dc2af2..2ea8abc 100644 --- a/src/Client/Enums.cs +++ b/src/Client/Enums.cs @@ -27,6 +27,7 @@ public enum CollectionMethodOptions CARegistry, DCRegistry, CertServices, + Registry, All } } \ No newline at end of file diff --git a/src/Options.cs b/src/Options.cs index 9b4ca53..069528f 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, All")] + "Collection Methods: Group, LocalGroup, LocalAdmin, RDP, DCOM, PSRemote, Session, Trusts, ACL, Container, ComputerOnly, GPOLocalGroup, LoggedOn, ObjectProps, SPNTargets, UserRights, Default, DCOnly, CARegistry, DCRegistry, CertServices, Registry, All")] public IEnumerable CollectionMethods { get; set; } [Option('d', "domain", Default = null, HelpText = "Specify domain to enumerate")] @@ -204,6 +204,7 @@ internal bool ResolveCollectionMethods(ILogger logger, out CollectionMethod reso CollectionMethodOptions.CARegistry => CollectionMethod.CARegistry, CollectionMethodOptions.DCRegistry => CollectionMethod.DCRegistry, CollectionMethodOptions.CertServices => CollectionMethod.CertServices, + CollectionMethodOptions.Registry => CollectionMethod.Registry, CollectionMethodOptions.All => CollectionMethod.All, CollectionMethodOptions.None => CollectionMethod.None, _ => throw new ArgumentOutOfRangeException() @@ -262,6 +263,12 @@ internal bool ResolveCollectionMethods(ILogger logger, out CollectionMethod reso updates.Add("[-] Removed DCRegistry Collection"); } + if ((resolved & CollectionMethod.Registry) != 0) + { + resolved ^= CollectionMethod.Registry; + updates.Add("[-] Removed Registry Collection"); + } + if (localGroupRemoved) { resolved |= CollectionMethod.GPOLocalGroup; diff --git a/src/Runtime/CollectionTask.cs b/src/Runtime/CollectionTask.cs index edc5361..d890866 100644 --- a/src/Runtime/CollectionTask.cs +++ b/src/Runtime/CollectionTask.cs @@ -61,8 +61,13 @@ internal async Task StartCollection() { for (var i = 0; i < _context.Threads; i++) { - var consumer = LDAPConsumer.ConsumeSearchResults(_ldapChannel, _compStatusChannel, _outputChannel, - _context, i); + var consumer = LDAPConsumer.ConsumeSearchResults( + _ldapChannel, + _compStatusChannel, + _outputChannel, + _context, + i); + _taskPool.Add(consumer); } diff --git a/src/Runtime/LDAPConsumer.cs b/src/Runtime/LDAPConsumer.cs index 952b0f9..5be8877 100644 --- a/src/Runtime/LDAPConsumer.cs +++ b/src/Runtime/LDAPConsumer.cs @@ -8,25 +8,32 @@ using SharpHoundCommonLib; using SharpHoundCommonLib.Enums; using SharpHoundCommonLib.OutputTypes; +using SharpHoundCommonLib.Processors; namespace Sharphound.Runtime { public static class LDAPConsumer { - internal static async Task ConsumeSearchResults(Channel inputChannel, - Channel computerStatusChannel, Channel outputChannel, IContext context, + internal static async Task ConsumeSearchResults( + Channel inputChannel, + Channel computerStatusChannel, + Channel outputChannel, + IContext context, int id) { var log = context.Logger; - var processor = new ObjectProcessors(context, log); + var portScanner = new PortScanner() { Timeout = context.PortScanTimeout }; + var processor = new ObjectProcessors(context, log, portScanner); var watch = new Stopwatch(); var threadId = Thread.CurrentThread.ManagedThreadId; await foreach (var item in inputChannel.Reader.ReadAllAsync()) try { - if (await LdapUtils.ResolveSearchResult(item, context.LDAPUtils) is not (true, var res) || res == null || res.ObjectType == Label.Base) { - if (item.TryGetDistinguishedName(out var dn)) { + if (await LdapUtils.ResolveSearchResult(item, context.LDAPUtils) is not (true, var res) || res == null || res.ObjectType == Label.Base) + { + if (item.TryGetDistinguishedName(out var dn)) + { log.LogTrace("Consumer failed to resolve entry for {item} or label was Base", dn); } continue; diff --git a/src/Runtime/ObjectProcessors.cs b/src/Runtime/ObjectProcessors.cs index ca134b1..561565c 100644 --- a/src/Runtime/ObjectProcessors.cs +++ b/src/Runtime/ObjectProcessors.cs @@ -12,6 +12,7 @@ using SharpHoundCommonLib.Enums; using SharpHoundCommonLib.OutputTypes; using SharpHoundCommonLib.Processors; +using SharpHoundRPC.PortScanner; using Container = SharpHoundCommonLib.OutputTypes.Container; using Group = SharpHoundCommonLib.OutputTypes.Group; using Label = SharpHoundCommonLib.Enums.Label; @@ -33,11 +34,12 @@ public class ObjectProcessors { private readonly GPOLocalGroupProcessor _gpoLocalGroupProcessor; private readonly UserRightsAssignmentProcessor _userRightsAssignmentProcessor; private readonly LocalGroupProcessor _localGroupProcessor; + private readonly RegistryProcessor _registryProcessor; private readonly ILogger _log; private readonly CollectionMethod _methods; private readonly SPNProcessors _spnProcessor; - public ObjectProcessors(IContext context, ILogger log) { + public ObjectProcessors(IContext context, ILogger log, IPortScanner portScanner) { _context = context; _aclProcessor = new ACLProcessor(context.LDAPUtils); _spnProcessor = new SPNProcessors(context.LDAPUtils); @@ -55,6 +57,7 @@ public ObjectProcessors(IContext context, ILogger log) { _gpoLocalGroupProcessor = new GPOLocalGroupProcessor(context.LDAPUtils); _userRightsAssignmentProcessor = new UserRightsAssignmentProcessor(context.LDAPUtils); _localGroupProcessor = new LocalGroupProcessor(context.LDAPUtils); + _registryProcessor = new RegistryProcessor(_log, context.DomainName); _methods = context.ResolvedCollectionMethods; _cancellationToken = context.CancellationTokenSource.Token; _log = log; @@ -238,6 +241,7 @@ await compStatusChannel.Writer.WriteAsync(availability.GetCSVStatus(resolvedSear // DCRegistry if (resolvedSearchResult.IsDomainController & (_methods & CollectionMethod.DCRegistry) != 0) { + await _context.DoDelay(); DCRegistryData dCRegistryData = new() { CertificateMappingMethods = _dCRegistryProcessor.GetCertificateMappingMethods(apiName), StrongCertificateBindingEnforcement = @@ -298,6 +302,12 @@ await compStatusChannel.Writer.WriteAsync(new CSVComputerStatus { ret.UserRights = await userRights.ToArrayAsync(); } + if ((_methods & CollectionMethod.Registry) != 0) + { + await _context.DoDelay(); + ret.RegistryData = await _registryProcessor.ReadRegistrySettings(apiName); + } + if (!_methods.IsLocalGroupCollectionSet()) return ret;