Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ The listing below details the CLI arguments SharpHound supports. Additional deta
```
-c, --collectionmethods (Default: Default) Collection Methods: Container, Group, LocalGroup, GPOLocalGroup,
Session, LoggedOn, ObjectProps, ACL, ComputerOnly, Trusts, Default, RDP, DCOM, DCOnly, UserRights,
CARegistry, DCRegistry, CertServices, WebClientService, NTLMRegistry,SMBInfo,LdapServices
CARegistry, DCRegistry, CertServices, Site, WebClientService, NTLMRegistry, SMBInfo, LdapServices

-d, --domain Specify domain to enumerate

Expand Down
1 change: 1 addition & 0 deletions src/Client/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public enum CollectionMethodOptions
LdapServices,
SmbInfo,
NTLMRegistry,
Site,
// Re-introduce this when we're ready for Event Log collection
// EventLogs,
All
Expand Down
3 changes: 2 additions & 1 deletion 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, CARegistry, DCRegistry, CertServices, 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, All")]
public IEnumerable<string> CollectionMethods { get; set; }

[Option('d', "domain", Default = null, HelpText = "Specify domain to enumerate")]
Expand Down Expand Up @@ -211,6 +211,7 @@ internal bool ResolveCollectionMethods(ILogger logger, out CollectionMethod reso
CollectionMethodOptions.LdapServices => CollectionMethod.LdapServices,
CollectionMethodOptions.SmbInfo => CollectionMethod.SmbInfo,
CollectionMethodOptions.NTLMRegistry => CollectionMethod.NTLMRegistry,
CollectionMethodOptions.Site => CollectionMethod.Site,
// Re-introduce this when we're ready for Event Log collection
// CollectionMethodOptions.EventLogs => CollectionMethod.EventLogs,
CollectionMethodOptions.All => CollectionMethod.All,
Expand Down
5 changes: 3 additions & 2 deletions src/PowerShell/Template.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@
LoggedOn - Collect session information using privileged methods (needs admin!)
ObjectProps - Collect node property information for users and computers
SPNTargets - Collect SPN targets (currently only MSSQL)
Default - Collect Group Membership, Local Admin, Sessions, Containers, ACLs, Domain Trusts, and ADCS objects
DcOnly - Collect Group Membership, ACLs, ObjectProps, Trusts, Containers, GPO Admins, and ADCS objects
Default - Collect Group Membership, Local Admin, Sessions, Containers, ACLs, Domain Trusts, ADCS objects, and AD sites
DcOnly - Collect Group Membership, ACLs, ObjectProps, Trusts, Containers, GPO Admins, ADCS objects, and AD sites
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
CertServices - Collect ADCS objects from Certificate Services
Site - Collect AD site, site server, and site subnet data
All - Collect all data

This can be a list of comma separated valued as well to run multiple collection methods!
Expand Down
145 changes: 145 additions & 0 deletions src/Runtime/ObjectProcessors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class ObjectProcessors {
private readonly SPNProcessors _spnProcessor;
private readonly WebClientServiceProcessor _webClientProcessor;
private readonly SmbProcessor _smbProcessor;
private readonly SiteProcessor _siteProcessor;
private readonly ConcurrentDictionary<string, Lazy<RegistryProcessor>> _registryProcessorMap = new();
private readonly Channel<CSVComputerStatus> _compStatusChannel;

Expand All @@ -64,6 +65,7 @@ public ObjectProcessors(IContext context, ILogger log, Channel<CSVComputerStatus
_localGroupProcessor = new LocalGroupProcessor(context.LDAPUtils);
_webClientProcessor = new WebClientServiceProcessor(log);
_smbProcessor = new SmbProcessor(context.PortScanTimeout);
_siteProcessor = new SiteProcessor(context.LDAPUtils);
_methods = context.ResolvedCollectionMethods;
_cancellationToken = context.CancellationTokenSource.Token;
_log = log;
Expand Down Expand Up @@ -129,6 +131,12 @@ internal async Task<OutputBase> ProcessObject(IDirectoryObject entry, ResolvedSe
return await ProcessCertTemplate(entry, resolvedSearchResult);
case Label.IssuancePolicy:
return await ProcessIssuancePolicy(entry, resolvedSearchResult);
case Label.Site:
return await ProcessSiteObject(entry, resolvedSearchResult);
case Label.SiteServer:
return await ProcessSiteServerObject(entry, resolvedSearchResult);
case Label.SiteSubnet:
return await ProcessSiteSubnetObject(entry, resolvedSearchResult);
case Label.Base:
return null;
default:
Expand Down Expand Up @@ -955,5 +963,142 @@ private async Task<IssuancePolicy> ProcessIssuancePolicy(IDirectoryObject entry,

return ret;
}

private async Task<Site> ProcessSiteObject(IDirectoryObject entry,
ResolvedSearchResult resolvedSearchResult)
{
var ret = new Site
{
ObjectIdentifier = resolvedSearchResult.ObjectId
};

ret.Properties = new Dictionary<string, object>(GetCommonProperties(entry, resolvedSearchResult));

if (_methods.HasFlag(CollectionMethod.ACL) || _methods.HasFlag(CollectionMethod.Site))
{
var aces = await _aclProcessor.ProcessACL(resolvedSearchResult, entry, true)
.ToArrayAsync(cancellationToken: _cancellationToken);
ret.Properties.Add("doesanyacegrantownerrights", aces.Any(ace => ace.IsPermissionForOwnerRightsSid));
ret.Properties.Add("doesanyinheritedacegrantownerrights", aces.Any(ace => ace.IsInheritedPermissionForOwnerRightsSid));
ret.Aces = aces;
ret.IsACLProtected = _aclProcessor.IsACLProtected(entry);
ret.Properties.Add("isaclprotected", ret.IsACLProtected);
ret.InheritanceHashes = _aclProcessor.GetInheritedAceHashes(entry, resolvedSearchResult).ToArray();
}

if (_methods.HasFlag(CollectionMethod.ObjectProps) || _methods.HasFlag(CollectionMethod.Site))
{
ret.Properties =
ContextUtils.Merge(LdapPropertyProcessor.ReadSiteProperties(entry), ret.Properties);
if (_context.Flags.CollectAllProperties)
{
ret.Properties = ContextUtils.Merge(_ldapPropertyProcessor.ParseAllProperties(entry),
ret.Properties);
}
}

if (_methods.HasFlag(CollectionMethod.Container) || _methods.HasFlag(CollectionMethod.Site)) {
if (await _containerProcessor.GetContainingObject(entry) is (true, var container)) {
ret.ContainedBy = container;
}
}

if (_methods.HasFlag(CollectionMethod.Site))
{
ret.Links = await _siteProcessor.ReadSiteGPLinks(resolvedSearchResult, entry).ToArrayAsync();
}

return ret;
}

private async Task<SiteServer> ProcessSiteServerObject(IDirectoryObject entry,
ResolvedSearchResult resolvedSearchResult)
{
var ret = new SiteServer
{
ObjectIdentifier = resolvedSearchResult.ObjectId
};

ret.Properties = new Dictionary<string, object>(GetCommonProperties(entry, resolvedSearchResult));

if (_methods.HasFlag(CollectionMethod.ACL) || _methods.HasFlag(CollectionMethod.Site))
{
var aces = await _aclProcessor.ProcessACL(resolvedSearchResult, entry, true)
.ToArrayAsync(cancellationToken: _cancellationToken);
ret.Properties.Add("doesanyacegrantownerrights", aces.Any(ace => ace.IsPermissionForOwnerRightsSid));
ret.Properties.Add("doesanyinheritedacegrantownerrights", aces.Any(ace => ace.IsInheritedPermissionForOwnerRightsSid));
ret.Aces = aces;
ret.IsACLProtected = _aclProcessor.IsACLProtected(entry);
ret.Properties.Add("isaclprotected", ret.IsACLProtected);
}

if (_methods.HasFlag(CollectionMethod.ObjectProps) || _methods.HasFlag(CollectionMethod.Site))
{
ret.Properties =
ContextUtils.Merge(LdapPropertyProcessor.ReadSiteServerProperties(entry), ret.Properties);
if (_context.Flags.CollectAllProperties)
{
ret.Properties = ContextUtils.Merge(_ldapPropertyProcessor.ParseAllProperties(entry),
ret.Properties);
}
}

if (_methods.HasFlag(CollectionMethod.Container) || _methods.HasFlag(CollectionMethod.Site)) {
if (await _siteProcessor.GetContainingSiteForServer(entry) is (true, var container))
{
ret.ContainedBy = container;
}
}

if (_methods.HasFlag(CollectionMethod.Site)) {
if (await _siteProcessor.GetReferencedComputerForServer(entry) is (true, var server))
{
ret.ServerIs = server;
}
}

return ret;
}

private async Task<SiteSubnet> ProcessSiteSubnetObject(IDirectoryObject entry,
ResolvedSearchResult resolvedSearchResult)
{
var ret = new SiteSubnet
{
ObjectIdentifier = resolvedSearchResult.ObjectId
};

ret.Properties = new Dictionary<string, object>(GetCommonProperties(entry, resolvedSearchResult));

if (_methods.HasFlag(CollectionMethod.ACL) || _methods.HasFlag(CollectionMethod.Site))
{
var aces = await _aclProcessor.ProcessACL(resolvedSearchResult, entry, true)
.ToArrayAsync(cancellationToken: _cancellationToken);
ret.Properties.Add("doesanyacegrantownerrights", aces.Any(ace => ace.IsPermissionForOwnerRightsSid));
ret.Properties.Add("doesanyinheritedacegrantownerrights", aces.Any(ace => ace.IsInheritedPermissionForOwnerRightsSid));
ret.Aces = aces;
ret.IsACLProtected = _aclProcessor.IsACLProtected(entry);
ret.Properties.Add("isaclprotected", ret.IsACLProtected);
}

if (_methods.HasFlag(CollectionMethod.ObjectProps) || _methods.HasFlag(CollectionMethod.Container) || _methods.HasFlag(CollectionMethod.Site))
{
ret.Properties =
ContextUtils.Merge(LdapPropertyProcessor.ReadSiteSubnetProperties(entry), ret.Properties);
if (_context.Flags.CollectAllProperties)
{
ret.Properties = ContextUtils.Merge(_ldapPropertyProcessor.ParseAllProperties(entry),
ret.Properties);
}

// Can only deduce containing site for a subnet if we read the object properties, including siteObject
if (await _siteProcessor.GetContainingSiteForSubnet(ret.Properties) is (true, var container))
{
ret.ContainedBy = container;
}
}

return ret;
}
}
}
21 changes: 20 additions & 1 deletion src/Runtime/OutputWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ public class OutputWriter
private readonly JsonDataWriter<NTAuthStore> _nTAuthStoreOutput;
private readonly JsonDataWriter<CertTemplate> _certTemplateOutput;
private readonly JsonDataWriter<IssuancePolicy> _issuancePolicyOutput;
private readonly JsonDataWriter<Site> _siteOutput;
private readonly JsonDataWriter<SiteServer> _siteServerOutput;
private readonly JsonDataWriter<SiteSubnet> _siteSubnetOutput;


private int _completedCount;
Expand All @@ -57,6 +60,9 @@ public OutputWriter(IContext context, Channel<OutputBase> outputChannel)
_nTAuthStoreOutput = new JsonDataWriter<NTAuthStore>(_context, DataType.NTAuthStores);
_certTemplateOutput = new JsonDataWriter<CertTemplate>(_context, DataType.CertTemplates);
_issuancePolicyOutput = new JsonDataWriter<IssuancePolicy>(_context, DataType.IssuancePolicies);
_siteOutput = new JsonDataWriter<Site>(_context, DataType.Sites);
_siteServerOutput = new JsonDataWriter<SiteServer>(_context, DataType.SiteServers);
_siteSubnetOutput = new JsonDataWriter<SiteSubnet>(_context, DataType.SiteSubnets);

_runTimer = new Stopwatch();
_statusTimer = new Timer(_context.StatusInterval);
Expand Down Expand Up @@ -145,6 +151,15 @@ internal async Task<string> StartWriter()
case IssuancePolicy issuancePolicy:
await _issuancePolicyOutput.AcceptObject(issuancePolicy);
break;
case Site site:
await _siteOutput.AcceptObject(site);
break;
case SiteServer siteServer:
await _siteServerOutput.AcceptObject(siteServer);
break;
case SiteSubnet siteSubnet:
await _siteSubnetOutput.AcceptObject(siteSubnet);
break;
default:
throw new ArgumentOutOfRangeException(nameof(item));
}
Expand All @@ -169,6 +184,9 @@ private async Task<string> FlushWriters()
await _nTAuthStoreOutput.FlushWriter();
await _certTemplateOutput.FlushWriter();
await _issuancePolicyOutput.FlushWriter();
await _siteOutput.FlushWriter();
await _siteServerOutput.FlushWriter();
await _siteSubnetOutput.FlushWriter();
CloseOutput();
var fileName = ZipFiles();
return fileName;
Expand Down Expand Up @@ -198,7 +216,8 @@ private string ZipFiles()
_containerOutput.GetFilename(), _domainOutput.GetFilename(), _gpoOutput.GetFilename(),
_ouOutput.GetFilename(), _rootCAOutput.GetFilename(), _aIACAOutput.GetFilename(),
_enterpriseCAOutput.GetFilename(), _nTAuthStoreOutput.GetFilename(),
_certTemplateOutput.GetFilename(),_issuancePolicyOutput.GetFilename()
_certTemplateOutput.GetFilename(), _issuancePolicyOutput.GetFilename(),
_siteOutput.GetFilename(), _siteServerOutput.GetFilename(), _siteSubnetOutput.GetFilename(),
});

foreach (var entry in fileList.Where(x => !string.IsNullOrEmpty(x)))
Expand Down
Loading