Skip to content

Commit

Permalink
Merge pull request #77 from BloodHoundAD/adcs
Browse files Browse the repository at this point in the history
Draft: ADCS ESC1 Early Access Support
  • Loading branch information
rvazarkar authored Dec 6, 2023
2 parents f87713c + c27955e commit e3a5c9a
Show file tree
Hide file tree
Showing 12 changed files with 464 additions and 30 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ dotnet build

SharpHound is designed targeting .Net 4.6.2. SharpHound must be run from the context of a domain user, either directly through a logon or through another method such as RUNAS.


# SharpHound

```csharp
Expand All @@ -28,9 +27,10 @@ dotnet build
```

# CLI

```
-c, --collectionmethods (Default: Default) Collection Methods: Container, Group, LocalGroup, GPOLocalGroup,
Session, LoggedOn, ObjectProps, ACL, ComputerOnly, Trusts, Default, RDP, DCOM, DCOnly
Session, LoggedOn, ObjectProps, ACL, ComputerOnly, Trusts, Default, RDP, DCOM, DCOnly, CARegistry, DCRegistry, CertServices
-d, --domain Specify domain to enumerate
Expand Down Expand Up @@ -75,15 +75,15 @@ dotnet build
--ldapport (Default: 0) Override port for LDAP
--secureldap (Default: false) Connect to LDAP SSL instead of regular LDAP
--disablecertverification (Default: false) Disable certificate verification for secure LDAP
--disablesigning (Default: false) Disables Kerberos Signing/Sealing
--skipportcheck (Default: false) Skip checking if 445 is open
--portchecktimeout (Default: 500) Timeout for port checks in milliseconds
--skippasswordcheck (Default: false) Skip PwdLastSet age check when checking computers
--excludedcs (Default: false) Exclude domain controllers from session/localgroup enumeration (mostly for
Expand Down
4 changes: 2 additions & 2 deletions Sharphound.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="SharpHoundCommon" Version="3.1.0" />
<PackageReference Include="SharpHoundRPC" Version="3.1.0" />
<PackageReference Include="SharpHoundCommon" Version="3.1.1" />
<PackageReference Include="SharpHoundRPC" Version="3.1.1" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="System.Threading.Channels" Version="6.0.0" />
Expand Down
3 changes: 3 additions & 0 deletions src/Client/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public enum CollectionMethodOptions
Default,
DCOnly,
ComputerOnly,
CARegistry,
DCRegistry,
CertServices,
All
}
}
31 changes: 23 additions & 8 deletions src/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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, All")]
"Collection Methods: Group, LocalGroup, LocalAdmin, RDP, DCOM, PSRemote, Session, Trusts, ACL, Container, ComputerOnly, GPOLocalGroup, LoggedOn, ObjectProps, SPNTargets, UserRights, Default, DCOnly, CARegistry, DCRegistry, CertServices, All")]
public IEnumerable<string> CollectionMethods { get; set; }

[Option('d', "domain", Default = null, HelpText = "Specify domain to enumerate")]
Expand Down Expand Up @@ -61,7 +61,7 @@ public class Options

[Option(HelpText = "Don't zip files", Default = false)]
public bool NoZip { get; set; }

[Option(HelpText = "Password protects the zip with the specified password", Default = null)]
public string ZipPassword { get; set; }

Expand Down Expand Up @@ -95,7 +95,7 @@ public class Options

[Option(HelpText = "Connect to LDAP SSL instead of regular LDAP", Default = false)]
public bool SecureLDAP { get; set; }

[Option(HelpText = "Disables certificate verification when using LDAPS", Default = false)]
public bool DisableCertVerification { get; set; }

Expand All @@ -105,10 +105,10 @@ public class Options
//Options that affect how enumeration is performed
[Option(HelpText = "Skip checking if 445 is open", Default = false)]
public bool SkipPortCheck { get; set; }

[Option(HelpText = "Timeout for port checks in milliseconds", Default = 500)]
public int PortCheckTimeout { get; set; }

[Option(HelpText = "Skip check for PwdLastSet when enumerating computers", Default = false)]
public bool SkipPasswordCheck { get; set; }

Expand All @@ -122,7 +122,7 @@ public class Options
[Option(HelpText = "Add jitter to throttle (percent)")]
public int Jitter { get; set; }

[Option('t',"threads", HelpText = "Number of threads to run enumeration with", Default = 50)]
[Option('t', "threads", HelpText = "Number of threads to run enumeration with", Default = 50)]
public int Threads { get; set; }

[Option(HelpText = "Skip registry session enumeration")]
Expand All @@ -141,10 +141,10 @@ public class Options
[Option('l', "Loop", HelpText = "Loop computer collection")]
public bool Loop { get; set; }

[Option(HelpText="Loop duration (hh:mm:ss - 05:00:00 is 5 hours, default: 2 hrs)")]
[Option(HelpText = "Loop duration (hh:mm:ss - 05:00:00 is 5 hours, default: 2 hrs)")]
public TimeSpan LoopDuration { get; set; }

[Option(HelpText="Add delay between loops (hh:mm:ss - 00:03:00 is 3 minutes)")] public TimeSpan LoopInterval { get; set; }
[Option(HelpText = "Add delay between loops (hh:mm:ss - 00:03:00 is 3 minutes)")] public TimeSpan LoopInterval { get; set; }

//Misc Options
[Option(HelpText = "Interval in which to display status in milliseconds", Default = 30000)]
Expand Down Expand Up @@ -195,6 +195,9 @@ internal bool ResolveCollectionMethods(ILogger logger, out ResolvedCollectionMet
CollectionMethodOptions.Default => ResolvedCollectionMethod.Default,
CollectionMethodOptions.DCOnly => ResolvedCollectionMethod.DCOnly,
CollectionMethodOptions.ComputerOnly => ResolvedCollectionMethod.ComputerOnly,
CollectionMethodOptions.CARegistry => ResolvedCollectionMethod.CARegistry,
CollectionMethodOptions.DCRegistry => ResolvedCollectionMethod.DCRegistry,
CollectionMethodOptions.CertServices => ResolvedCollectionMethod.CertServices,
CollectionMethodOptions.All => ResolvedCollectionMethod.All,
CollectionMethodOptions.None => ResolvedCollectionMethod.None,
_ => throw new ArgumentOutOfRangeException()
Expand Down Expand Up @@ -241,6 +244,18 @@ internal bool ResolveCollectionMethods(ILogger logger, out ResolvedCollectionMet
updates.Add("[-] Removed LocalAdmin Collection");
}

if ((resolved & ResolvedCollectionMethod.CARegistry) != 0)
{
resolved ^= ResolvedCollectionMethod.CARegistry;
updates.Add("[-] Removed CARegistry Collection");
}

if ((resolved & ResolvedCollectionMethod.DCRegistry) != 0)
{
resolved ^= ResolvedCollectionMethod.DCRegistry;
updates.Add("[-] Removed DCRegistry Collection");
}

if (localGroupRemoved)
{
resolved |= ResolvedCollectionMethod.GPOLocalGroup;
Expand Down
3 changes: 3 additions & 0 deletions src/PowerShell/Template.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
SPNTargets - Collects SPN targets (currently only MSSQL)
Default - Collects Group Membership, Local Admin, Sessions, Containers, ACLs and Domain Trusts
DcOnly - Collects Group Membership, ACLs, ObjectProps, Trusts, Containers, and GPO Admins
CARegistry - Collect ADCS properties from registry of Certificate Authority servers
DCRegistry - Collect properties from registry of Domain Controller servers
CertServices - Collect ADCS properties from Certificate Services
All - Collect all data
This can be a list of comma separated valued as well to run multiple collection methods!
Expand Down
74 changes: 70 additions & 4 deletions src/Producers/BaseProducer.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
using System.Collections.Generic;

using System.Threading.Channels;

using System.Threading.Tasks;

using Sharphound.Client;

using SharpHoundCommonLib;

using SharpHoundCommonLib.Enums;

using SharpHoundCommonLib.LDAPQueries;

using SharpHoundCommonLib.OutputTypes;

namespace Sharphound.Producers
Expand All @@ -26,8 +33,9 @@ protected BaseProducer(IContext context, Channel<ISearchResultEntry> channel, Ch
}

public abstract Task Produce();
public abstract Task ProduceConfigNC();

protected LDAPData CreateLDAPData()
protected LDAPData CreateDefaultNCData()
{
var query = new LDAPFilter();
var props = new List<string>();
Expand Down Expand Up @@ -59,9 +67,9 @@ protected LDAPData CreateLDAPData()
(methods & ResolvedCollectionMethod.RDP) != 0 ||
(methods & ResolvedCollectionMethod.LoggedOn) != 0 ||
(methods & ResolvedCollectionMethod.Session) != 0 ||
(methods & ResolvedCollectionMethod.ObjectProps) != 0 ||
(methods & ResolvedCollectionMethod.ObjectProps) != 0 ||
(methods & ResolvedCollectionMethod.UserRights) != 0)

props.AddRange(CommonProperties.ComputerMethodProps);

if ((methods & ResolvedCollectionMethod.Trusts) != 0) props.AddRange(CommonProperties.DomainTrustProps);
Expand All @@ -71,6 +79,9 @@ protected LDAPData CreateLDAPData()

if ((methods & ResolvedCollectionMethod.SPNTargets) != 0)
props.AddRange(CommonProperties.SPNTargetProps);

if ((methods & ResolvedCollectionMethod.DCRegistry) != 0)
props.AddRange(CommonProperties.ComputerMethodProps);
}
else
{
Expand All @@ -92,7 +103,7 @@ protected LDAPData CreateLDAPData()
(methods & ResolvedCollectionMethod.RDP) != 0 ||
(methods & ResolvedCollectionMethod.LoggedOn) != 0 ||
(methods & ResolvedCollectionMethod.Session) != 0 ||
(methods & ResolvedCollectionMethod.ObjectProps) != 0 ||
(methods & ResolvedCollectionMethod.ObjectProps) != 0 ||
(methods & ResolvedCollectionMethod.UserRights) != 0)
{
query = query.AddComputers();
Expand All @@ -116,8 +127,63 @@ protected LDAPData CreateLDAPData()
query = query.AddOUs();
props.AddRange(CommonProperties.GPOLocalGroupProps);
}

if ((methods & ResolvedCollectionMethod.DCRegistry) != 0)
{
query = query.AddComputers();
props.AddRange(CommonProperties.ComputerMethodProps);
}
}

if (Context.LdapFilter != null) query.AddFilter(Context.LdapFilter, true);

data.Filter = query;
data.Props = props;
return data;
}

protected LDAPData CreateConfigNCData()
{
var query = new LDAPFilter();
var props = new List<string>();
var data = new LDAPData();
props.AddRange(CommonProperties.BaseQueryProps);
props.AddRange(CommonProperties.TypeResolutionProps);

var methods = Context.ResolvedCollectionMethods;
var allObjectTypesQuery = new LDAPFilter().AddContainers().AddConfiguration().AddCertificateTemplates().AddCertificateAuthorities().AddEnterpriseCertificationAuthorities();

if ((methods & ResolvedCollectionMethod.ObjectProps) != 0)
{
query = allObjectTypesQuery;
props.AddRange(CommonProperties.ObjectPropsProps);
}

if ((methods & ResolvedCollectionMethod.ACL) != 0)
{
query = allObjectTypesQuery;
props.AddRange(CommonProperties.ACLProps);
}

if ((methods & ResolvedCollectionMethod.CertServices) != 0)
{
query = allObjectTypesQuery;
props.AddRange(CommonProperties.CertAbuseProps);
}

if ((methods & ResolvedCollectionMethod.Container) != 0)
{
query = allObjectTypesQuery;
props.AddRange(CommonProperties.ContainerProps);
}

if ((methods & ResolvedCollectionMethod.CARegistry) != 0)
{
query = query.AddEnterpriseCertificationAuthorities();
props.AddRange(CommonProperties.CertAbuseProps);
}


if (Context.LdapFilter != null) query.AddFilter(Context.LdapFilter, true);

data.Filter = query;
Expand Down
7 changes: 6 additions & 1 deletion src/Producers/ComputerFileProducer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public override async Task Produce()
var computerFile = Context.ComputerFile;
var cancellationToken = Context.CancellationTokenSource.Token;

var ldapData = CreateLDAPData();
var ldapData = CreateDefaultNCData();

try
{
Expand Down Expand Up @@ -79,5 +79,10 @@ public override async Task Produce()
Console.WriteLine($"Error opening ComputerFile: {e}");
}
}

public override async Task ProduceConfigNC()
{
// Does not make sense for Computer file
}
}
}
48 changes: 44 additions & 4 deletions src/Producers/LdapProducer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.DirectoryServices.Protocols;
using System.Linq;
Expand Down Expand Up @@ -26,7 +26,7 @@ public override async Task Produce()
{
var cancellationToken = Context.CancellationTokenSource.Token;

var ldapData = CreateLDAPData();
var ldapData = CreateDefaultNCData();

var log = Context.Logger;
var utils = Context.LDAPUtils;
Expand Down Expand Up @@ -67,7 +67,7 @@ await OutputChannel.Writer.WriteAsync(new Domain
{ "collected", true },
}
});

foreach (var searchResult in Context.LDAPUtils.QueryLDAP(ldapData.Filter.GetFilter(), SearchScope.Subtree,
ldapData.Props.Distinct().ToArray(), cancellationToken, domain.Name,
adsPath: Context.SearchBase,
Expand All @@ -83,7 +83,47 @@ await OutputChannel.Writer.WriteAsync(new Domain
Context.Logger.LogTrace("Producer wrote {DistinguishedName} to channel", searchResult.DistinguishedName);
}
}


}

/// <summary>
/// Uses the LDAP filter and properties specified to grab data from LDAP (Configuration NC), and push it to the queue.
/// </summary>
/// <returns></returns>
public override async Task ProduceConfigNC()
{
var cancellationToken = Context.CancellationTokenSource.Token;
var configNcData = CreateConfigNCData();
var configurationNCsCollected = new List<string>();

if (string.IsNullOrEmpty(configNcData.Filter.GetFilter()))
return;

foreach (var domain in Context.Domains)
{
var configAdsPath = Context.LDAPUtils.GetConfigurationPath(domain.Name);
if (!configurationNCsCollected.Contains(configAdsPath))
{
Context.Logger.LogInformation("Beginning LDAP search for {Domain} Configuration NC", domain.Name);

//Do a basic LDAP search and grab results
foreach (var searchResult in Context.LDAPUtils.QueryLDAP(configNcData.Filter.GetFilter(), SearchScope.Subtree,
configNcData.Props.Distinct().ToArray(), cancellationToken, domain.Name,
adsPath: configAdsPath,
includeAcl: (Context.ResolvedCollectionMethods & ResolvedCollectionMethod.ACL) != 0))
{
await Channel.Writer.WriteAsync(searchResult, cancellationToken);
Context.Logger.LogTrace("Producer wrote {DistinguishedName} to channel", searchResult.DistinguishedName);
}

// Ensure we only collect the Configuration NC once per forest
configurationNCsCollected.Add(configAdsPath);
}
else
{
Context.Logger.LogTrace("Skipping already collected config NC '{path}' for domain {Domain}", configAdsPath, domain.Name);
}
}
}
}
}
Loading

0 comments on commit e3a5c9a

Please sign in to comment.