Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: SpecterOps/SharpHound
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.0.3
Choose a base ref
...
head repository: SpecterOps/SharpHound
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.0.4
Choose a head ref
  • 7 commits
  • 9 files changed
  • 3 contributors

Commits on May 17, 2022

  1. Copy the full SHA
    af71c15 View commit details

Commits on May 19, 2022

  1. Copy the full SHA
    92b4089 View commit details

Commits on Jun 23, 2022

  1. Merge pull request #21 from lap1nou/fb-new-sharphound

    Added an InvokeSharpHound() function to be called by a PS ingestor
    rvazarkar authored Jun 23, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    1cc8bfe View commit details
  2. chore: update CommonLib to 2.0.15

    feat: add flag to skip computer age check
    feat: add powershell auto-generated output
    rvazarkar committed Jun 23, 2022
    1
    Copy the full SHA
    a6770fb View commit details
  3. Copy the full SHA
    fb624f6 View commit details
  4. faet: add the DisableCertVerification options. Thanks to @Augustin-FL

    …for the work on the commonlib and the MR. #24
    rvazarkar committed Jun 23, 2022
    Copy the full SHA
    bf9cdf4 View commit details

Commits on Jun 24, 2022

  1. Copy the full SHA
    a89a4d3 View commit details
Showing with 838 additions and 114 deletions.
  1. +5 −1 README.md
  2. +8 −4 Sharphound.csproj
  3. +1 −0 src/Client/Flags.cs
  4. +11 −5 src/Options.cs
  5. +59 −0 src/PowerShell/Out-CompressedDLL.ps1
  6. +635 −0 src/PowerShell/Template.ps1
  7. +1 −1 src/Producers/BaseProducer.cs
  8. +1 −1 src/Runtime/ObjectProcessors.cs
  9. +117 −102 src/Sharphound.cs
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
SharpHound Open Source Client version: 1.0.2
SharpHound Open Source Client version: 1.0.4
---

# SharpHound
@@ -77,12 +77,16 @@ 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
ATA/ATP)
12 changes: 8 additions & 4 deletions Sharphound.csproj
Original file line number Diff line number Diff line change
@@ -6,8 +6,8 @@
<LangVersion>latest</LangVersion>
<DebugType>full</DebugType>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<Version>1.0.3</Version>
<FileVersion>1.0.3</FileVersion>
<Version>1.0.4</Version>
<FileVersion>1.0.4</FileVersion>
<Company>SpecterOps</Company>
<Product>SharpHound</Product>
<AssemblyName>SharpHound</AssemblyName>
@@ -23,19 +23,23 @@
</PackageReference>
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="SharpHoundCommon" Version="2.0.13" />
<PackageReference Include="SharpHoundCommon" Version="2.0.15" />
<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" />
<PackageReference Include="Utf8Json" Version="1.3.7" />
</ItemGroup>

<ItemGroup>
<!-- <Reference Include="SharpHoundCommonLib, Version=2.0.12.0, Culture=neutral, PublicKeyToken=null">-->
<!-- <Reference Include="SharpHoundCommonLib, Version=2.0.13.0, Culture=neutral, PublicKeyToken=null">-->
<!-- <HintPath>..\SharpHoundCommon\src\CommonLib\bin\Debug\net462\SharpHoundCommonLib.dll</HintPath>-->
<!-- </Reference>-->
<Reference Include="System.DirectoryServices" />
<Reference Include="System.DirectoryServices.Protocols" />
<Reference Include="System.IO.Compression" />
</ItemGroup>
<Target Name="PS1" AfterTargets="Build">
<Message Text="Test" />
<Exec Command="powershell -ep bypass -c &quot;. '$(ProjectDir)src\Powershell\Out-CompressedDLL.ps1';Out-CompressedDll -FilePath '$(TargetPath)' -TemplatePath '$(ProjectDir)src\\Powershell\Template.ps1' | Out-File -Encoding ASCII '$(TargetDir)$(TargetName).ps1'&quot;" />
</Target>
</Project>
1 change: 1 addition & 0 deletions src/Client/Flags.cs
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ public class Flags
public bool SecureLDAP { get; set; }
public bool DisableKerberosSigning { get; set; }
public bool SkipPortScan { get; set; }
public bool SkipPasswordAgeCheck { get; set; }
public bool ExcludeDomainControllers { get; set; }
public bool NoRegistryLoggedOn { get; set; }
public bool DumpComputerStatus { get; set; }
16 changes: 11 additions & 5 deletions src/Options.cs
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ public class Options
// Options that affect what is collected
[Option('c', "collectionmethods", Default = new[] { "Default" },
HelpText =
"Collection Methods: Container, Group, LocalGroup, GPOLocalGroup, Session, LoggedOn, ObjectProps, ACL, ComputerOnly, Trusts, Default, RDP, DCOM, DCOnly")]
"Collection Methods: Group, LocalGroup, LocalAdmin, RDP, DCOM, PSRemote, Session, Trusts, ACL, Container, ComputerOnly, GPOLocalGroup, LoggedOn, ObjectProps, SPNTargets, Default, DCOnly, All")]
public IEnumerable<string> CollectionMethods { get; set; }

[Option('d', "domain", Default = null, HelpText = "Specify domain to enumerate")]
@@ -59,13 +59,13 @@ 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; }

[Option(HelpText = "Adds a CSV tracking requests to computers", Default = false)]
public bool TrackComputerCalls { get; set; }

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

[Option(HelpText = "Pretty print JSON", Default = false)]
public bool PrettyPrint { get; set; }

@@ -85,16 +85,22 @@ 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; }

[Option(HelpText = "Disables Kerberos Signing/Sealing", Default = false)]
public bool DisableSigning { get; set; }

//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; }

[Option(HelpText = "Exclude domain controllers from session/localgroup enumeration (mostly for ATA/ATP)",
Default = false)]
59 changes: 59 additions & 0 deletions src/PowerShell/Out-CompressedDLL.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
function Out-CompressedDll
{
<#
.SYNOPSIS
Creates the powershell in-memory version of SharpHound.
Based entirely off Out-CompressedDll by Matthew Graeber (@mattifestation)
Original script at https://github.com/PowerShellMafia/PowerSploit/blob/master/ScriptModification/Out-CompressedDll.ps1
#>

[CmdletBinding()] Param (
[Parameter(Mandatory = $True)]
[String]
$FilePath,

[Parameter(Mandatory = $True)]
[String]
$TemplatePath
)

$Path = Resolve-Path $FilePath

if (! [IO.File]::Exists($Path))
{
Throw "$Path does not exist."
}

$FileBytes = [System.IO.File]::ReadAllBytes($Path)

if (($FileBytes[0..1] | % {[Char]$_}) -join '' -cne 'MZ')
{
Throw "$Path is not a valid executable."
}

$Length = $FileBytes.Length
$CompressedStream = New-Object IO.MemoryStream
$DeflateStream = New-Object IO.Compression.DeflateStream ($CompressedStream, [IO.Compression.CompressionMode]::Compress)
$DeflateStream.Write($FileBytes, 0, $FileBytes.Length)
$DeflateStream.Dispose()
$CompressedFileBytes = $CompressedStream.ToArray()
$CompressedStream.Dispose()
$EncodedCompressedFile = [Convert]::ToBase64String($CompressedFileBytes)

Write-Verbose "Compression ratio: $(($EncodedCompressedFile.Length/$FileBytes.Length).ToString('#%'))"

$Output = @"
`$EncodedCompressedFile = '$EncodedCompressedFile`'
`$DeflatedStream = New-Object IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String(`$EncodedCompressedFile),[IO.Compression.CompressionMode]::Decompress)
`$UncompressedFileBytes = New-Object Byte[]($Length)
`$DeflatedStream.Read(`$UncompressedFileBytes, 0, $Length) | Out-Null
`$Assembly = [Reflection.Assembly]::Load(`$UncompressedFileBytes)
`$BindingFlags = [Reflection.BindingFlags] "Public,Static"
`$a = @()
`$Assembly.GetType("Costura.AssemblyLoader", `$false).GetMethod("Attach", `$BindingFlags).Invoke(`$Null, @())
`$Assembly.GetType("Sharphound.Program").GetMethod("InvokeSharpHound").Invoke(`$Null, @(,`$passed))
"@

Get-Content $TemplatePath | %{$_ -replace "#ENCODEDCONTENTHERE", $Output}
}
635 changes: 635 additions & 0 deletions src/PowerShell/Template.ps1

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Producers/BaseProducer.cs
Original file line number Diff line number Diff line change
@@ -71,7 +71,7 @@ protected LDAPData CreateLDAPData()
{
if ((methods & ResolvedCollectionMethod.Container) != 0)
{
query = query.AddContainers();
query = query.AddContainers().AddDomains();
props.AddRange(CommonProperties.ContainerProps);
}

2 changes: 1 addition & 1 deletion src/Runtime/ObjectProcessors.cs
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ public ObjectProcessors(IContext context, ILogger log)
_spnProcessor = new SPNProcessors(context.LDAPUtils);
_ldapPropertyProcessor = new LDAPPropertyProcessor(context.LDAPUtils);
_domainTrustProcessor = new DomainTrustProcessor(context.LDAPUtils);
_computerAvailability = new ComputerAvailability(context.PortScanTimeout, context.Flags.SkipPortScan);
_computerAvailability = new ComputerAvailability(context.PortScanTimeout, skipPortScan: context.Flags.SkipPortScan, skipPasswordCheck: context.Flags.SkipPasswordAgeCheck);
_computerSessionProcessor = new ComputerSessionProcessor(context.LDAPUtils);
_groupProcessor = new GroupProcessor(context.LDAPUtils);
_containerProcessor = new ContainerProcessor(context.LDAPUtils);
219 changes: 117 additions & 102 deletions src/Sharphound.cs
Original file line number Diff line number Diff line change
@@ -331,118 +331,133 @@ public class Program
public static async Task Main(string[] args)
{
var logger = new BasicLogger((int)LogLevel.Information);
var parser = new Parser(with =>
try
{
with.CaseInsensitiveEnumValues = true;
with.CaseSensitive = false;
with.HelpWriter = Console.Error;
});
var options = parser.ParseArguments<Options>(args);
var parser = new Parser(with =>
{
with.CaseInsensitiveEnumValues = true;
with.CaseSensitive = false;
with.HelpWriter = Console.Error;
});
var options = parser.ParseArguments<Options>(args);

await options.WithParsedAsync(async options =>
{
if (!options.ResolveCollectionMethods(logger, out var resolved, out var dconly)) return;
await options.WithParsedAsync(async options =>
{
if (!options.ResolveCollectionMethods(logger, out var resolved, out var dconly)) return;

logger = new BasicLogger(options.Verbosity);
logger = new BasicLogger(options.Verbosity);

var flags = new Flags
{
Loop = options.Loop,
DumpComputerStatus = options.TrackComputerCalls,
NoRegistryLoggedOn = options.SkipRegistryLoggedOn,
ExcludeDomainControllers = options.ExcludeDCs,
SkipPortScan = options.SkipPortCheck,
DisableKerberosSigning = options.DisableSigning,
SecureLDAP = options.SecureLDAP,
InvalidateCache = options.RebuildCache,
NoZip = options.NoZip,
NoOutput = false,
Stealth = options.Stealth,
RandomizeFilenames = options.RandomFileNames,
MemCache = options.MemCache,
CollectAllProperties = options.CollectAllProperties,
DCOnly = dconly,
PrettyPrint = options.PrettyPrint,
SearchForest = options.SearchForest
};

var ldapOptions = new LDAPConfig
{
Port = options.LDAPPort,
DisableSigning = options.DisableSigning,
SSL = options.SecureLDAP,
AuthType = AuthType.Negotiate
};
var flags = new Flags
{
Loop = options.Loop,
DumpComputerStatus = options.TrackComputerCalls,
NoRegistryLoggedOn = options.SkipRegistryLoggedOn,
ExcludeDomainControllers = options.ExcludeDCs,
SkipPortScan = options.SkipPortCheck,
SkipPasswordAgeCheck = options.SkipPasswordCheck,
DisableKerberosSigning = options.DisableSigning,
SecureLDAP = options.SecureLDAP,
InvalidateCache = options.RebuildCache,
NoZip = options.NoZip,
NoOutput = false,
Stealth = options.Stealth,
RandomizeFilenames = options.RandomFileNames,
MemCache = options.MemCache,
CollectAllProperties = options.CollectAllProperties,
DCOnly = dconly,
PrettyPrint = options.PrettyPrint,
SearchForest = options.SearchForest
};

var ldapOptions = new LDAPConfig
{
Port = options.LDAPPort,
DisableSigning = options.DisableSigning,
SSL = options.SecureLDAP,
AuthType = AuthType.Negotiate,
DisableCertVerification = options.DisableCertVerification
};

if (options.DomainController != null) ldapOptions.Server = options.DomainController;
if (options.DomainController != null) ldapOptions.Server = options.DomainController;

if (options.LDAPUsername != null)
{
if (options.LDAPPassword == null)
if (options.LDAPUsername != null)
{
logger.LogError("You must specify LDAPPassword if using the LDAPUsername options");
return;
if (options.LDAPPassword == null)
{
logger.LogError("You must specify LDAPPassword if using the LDAPUsername options");
return;
}

ldapOptions.Username = options.LDAPUsername;
ldapOptions.Password = options.LDAPPassword;
}

ldapOptions.Username = options.LDAPUsername;
ldapOptions.Password = options.LDAPPassword;
}
IContext context = new BaseContext(logger, ldapOptions, flags)
{
DomainName = options.Domain,
CacheFileName = options.CacheName,
ZipFilename = options.ZipFilename,
SearchBase = options.DistinguishedName,
StatusInterval = options.StatusInterval,
RealDNSName = options.RealDNSName,
ComputerFile = options.ComputerFile,
OutputPrefix = options.OutputPrefix,
OutputDirectory = options.OutputDirectory,
Jitter = options.Jitter,
Throttle = options.Throttle,
LdapFilter = options.LdapFilter,
PortScanTimeout = options.PortCheckTimeout,
ResolvedCollectionMethods = resolved,
Threads = options.Threads,
LoopDuration = options.LoopDuration,
LoopInterval = options.LoopInterval,
ZipPassword = options.ZipPassword,
IsFaulted = false
};

var cancellationTokenSource = new CancellationTokenSource();
context.CancellationTokenSource = cancellationTokenSource;

// Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs eventArgs)
// {
// eventArgs.Cancel = true;
// cancellationTokenSource.Cancel();
// };

// Create new chain links
Links<IContext> links = new SharpLinks();

// Run our chain
context = links.Initialize(context, ldapOptions);
if (context.Flags.IsFaulted)
return;
context = links.TestConnection(context);
if (context.Flags.IsFaulted)
return;
context = links.SetSessionUserName(options.OverrideUserName, context);
context = links.InitCommonLib(context);
context = links.GetDomainsForEnumeration(context);
if (context.Flags.IsFaulted)
return;
context = links.StartBaseCollectionTask(context);
context = await links.AwaitBaseRunCompletion(context);
context = links.StartLoopTimer(context);
context = links.StartLoop(context);
context = await links.AwaitLoopCompletion(context);
context = links.SaveCacheFile(context);
links.Finish(context);
});
}
catch (Exception ex)
{
logger.LogError($"Error running SharpHound: {ex.Message}\n{ex.StackTrace}");
}
}

IContext context = new BaseContext(logger, ldapOptions, flags)
{
DomainName = options.Domain,
CacheFileName = options.CacheName,
ZipFilename = options.ZipFilename,
SearchBase = options.DistinguishedName,
StatusInterval = options.StatusInterval,
RealDNSName = options.RealDNSName,
ComputerFile = options.ComputerFile,
OutputPrefix = options.OutputPrefix,
OutputDirectory = options.OutputDirectory,
Jitter = options.Jitter,
Throttle = options.Throttle,
LdapFilter = options.LdapFilter,
PortScanTimeout = options.PortCheckTimeout,
ResolvedCollectionMethods = resolved,
Threads = options.Threads,
LoopDuration = options.LoopDuration,
LoopInterval = options.LoopInterval,
ZipPassword = options.ZipPassword,
IsFaulted = false
};

var cancellationTokenSource = new CancellationTokenSource();
context.CancellationTokenSource = cancellationTokenSource;

// Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs eventArgs)
// {
// eventArgs.Cancel = true;
// cancellationTokenSource.Cancel();
// };

// Create new chain links
Links<IContext> links = new SharpLinks();

// Run our chain
context = links.Initialize(context, ldapOptions);
if (context.Flags.IsFaulted)
return;
context = links.TestConnection(context);
if (context.Flags.IsFaulted)
return;
context = links.SetSessionUserName(options.OverrideUserName, context);
context = links.InitCommonLib(context);
context = links.GetDomainsForEnumeration(context);
if (context.Flags.IsFaulted)
return;
context = links.StartBaseCollectionTask(context);
context = await links.AwaitBaseRunCompletion(context);
context = links.StartLoopTimer(context);
context = links.StartLoop(context);
context = await links.AwaitLoopCompletion(context);
context = links.SaveCacheFile(context);
links.Finish(context);
});
// Accessor function for the PS1 to work, do not change or remove
public static void InvokeSharpHound(string[] args)
{
Main(args).Wait();
}
}