/// <summary> /// Grabs computers names from the text file specified in the options, and attempts to resolve them to LDAP objects. /// Pushes the corresponding LDAP objects to the queue. /// </summary> /// <param name="queue"></param> /// <returns></returns> protected override async Task ProduceLdap(ITargetBlock <SearchResultEntry> queue) { var computerFile = Options.Instance.ComputerFile; var token = Helpers.GetCancellationToken(); OutputTasks.StartOutputTimer(); //Open the file for reading using (var fileStream = new StreamReader(new FileStream(computerFile, FileMode.Open, FileAccess.Read))) { string computer; // Loop over each line in the file while ((computer = fileStream.ReadLine()) != null) { //If the cancellation token is set, cancel enumeration if (token.IsCancellationRequested) { break; } string sid; if (!computer.StartsWith("S-1-5-21")) { //The computer isn't a SID so try to convert it to one sid = await ResolutionHelpers.ResolveHostToSid(computer, DomainName); } else { //The computer is already a sid, so just store it off sid = computer; } try { //Convert the sid to a hex representation and find the entry in the domain var hexSid = Helpers.ConvertSidToHexSid(sid); var entry = await Searcher.GetOne($"(objectsid={hexSid})", Props, SearchScope.Subtree); if (entry == null) { //We couldn't find the entry for whatever reason Console.WriteLine($"Failed to resolve {computer}"); continue; } //Success! Send the computer to be processed await queue.SendAsync(entry); } catch { Console.WriteLine($"Failed to resolve {computer}"); } } } queue.Complete(); }
/// <summary> /// Produces stealth LDAP targets /// </summary> /// <param name="queue"></param> /// <returns></returns> protected override async Task ProduceLdap(ITargetBlock <SearchResultEntry> queue) { var token = Helpers.GetCancellationToken(); //If we haven't generated our stealth targets, we'll build it now if (!_stealthTargetsBuilt) { Console.WriteLine("[+] Finding Stealth Targets from LDAP Properties"); Console.WriteLine(); var targetSids = await FindPathTargetSids(); SetStealthTargetSids(targetSids); _stealthTargetsBuilt = true; OutputTasks.StartOutputTimer(); //Output our stealth targets to the queue foreach (var searchResult in Searcher.QueryLdap(Query, Props, SearchScope.Subtree, Options.Instance.SearchBase)) { if (token.IsCancellationRequested) { Console.WriteLine("[-] Terminating Producer as cancellation was requested. Waiting for pipeline to finish"); break; } await queue.SendAsync(searchResult); } queue.Complete(); } else { // We've already built our stealth targets, and we're doing a loop OutputTasks.StartOutputTimer(); var targets = new List <SearchResultEntry>(); targets.AddRange(_stealthTargetSids.Values); if (!Options.Instance.ExcludeDomainControllers) { targets.AddRange(DomainControllerSids.Values); } foreach (var searchResult in targets) { if (token.IsCancellationRequested) { break; } await queue.SendAsync(searchResult); } queue.Complete(); } }
public override bool Execute() { var executed = base.Execute(); if (executed) { XmlTasks = OutputTasks.Select(x => XElement.Parse(x.GetMetadata("Xml"))).ToArray(); } test.tasksFile = base.TasksFile; test.inlineTask = base.InlineFile; test.compiledTask = base.InlineFile; return(executed); }
protected override async Task ProduceLdap(ITargetBlock <SearchResultEntry> queue) { var token = Helpers.GetCancellationToken(); OutputTasks.StartOutputTimer(); foreach (var searchResult in Searcher.QueryLdap(Query, Props, SearchScope.Subtree)) { if (token.IsCancellationRequested) { Console.WriteLine("[-] Terminating Producer as cancellation was requested. Waiting for pipeline to finish"); break; } await queue.SendAsync(searchResult); } queue.Complete(); }
/// <summary> /// Uses the LDAP filter and properties specified to grab data from LDAP, and push it to the queue. /// </summary> /// <param name="queue"></param> /// <returns></returns> protected override async Task ProduceLdap(ITargetBlock <SearchResultEntry> queue) { var token = Helpers.GetCancellationToken(); OutputTasks.StartOutputTimer(); //Do a basic LDAP search and grab results foreach (var searchResult in Searcher.QueryLdap(Query, Props, SearchScope.Subtree, Options.Instance.SearchBase)) { //If our cancellation token is set, cancel out of our loop if (token.IsCancellationRequested) { Console.WriteLine("[-] Terminating Producer as cancellation was requested. Waiting for pipeline to finish"); break; } await queue.SendAsync(searchResult); } queue.Complete(); }
/// <summary> /// Entry point for SharpHound. /// </summary> /// <param name="args"></param> /// <returns></returns> private static async Task Main(string[] args) { // Use the wonderful commandlineparser library to build our options. var parser = new Parser(with => { with.CaseInsensitiveEnumValues = true; with.CaseSensitive = false; with.HelpWriter = Console.Error; }); parser.ParseArguments <Options>(args).WithParsed(o => { //We've successfully parsed arguments, lets do some options post-processing. var currentTime = DateTime.Now; var initString = $"Initializing SharpHound at {currentTime.ToShortTimeString()} on {currentTime.ToShortDateString()}"; Console.WriteLine(new string('-', initString.Length)); Console.WriteLine(initString); Console.WriteLine(new string('-', initString.Length)); Console.WriteLine(); // Set the current user name for session collection. if (o.OverrideUserName != null) { o.CurrentUserName = o.OverrideUserName; } else { o.CurrentUserName = WindowsIdentity.GetCurrent().Name.Split('\\')[1]; } //Check some loop options if (o.Loop) { //If loop is set, ensure we actually set options properly if (o.LoopDuration == TimeSpan.Zero) { Console.WriteLine("Loop specified without a duration. Defaulting to 2 hours!"); o.LoopDuration = TimeSpan.FromHours(2); } if (o.LoopInterval == TimeSpan.Zero) { o.LoopInterval = TimeSpan.FromSeconds(30); } } Options.Instance = o; }).WithNotParsed(error => { }); parser.Dispose(); var options = Options.Instance; if (options == null) { return; } // Check to make sure we actually have valid collection methods set if (!options.ResolveCollectionMethods()) { return; } //If the user didn't specify a domain, pull the domain from DirectoryServices if (options.Domain == null) { try { options.Domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name.ToUpper(); } catch (Exception e) { Console.WriteLine(e); Console.WriteLine("Unable to determine user's domain. Please manually specify it with the --domain flag"); return; } } //Check to make sure both LDAP options are set if either is set if ((options.LdapPassword != null && options.LdapUsername == null) || (options.LdapUsername != null && options.LdapPassword == null)) { Console.WriteLine("You must specify both LdapUsername and LdapPassword if using these options!"); return; } //Initial LDAP connection test. Search for the well known administrator SID to make sure we can connect successfully. var searcher = Helpers.GetDirectorySearcher(options.Domain); var result = await searcher.GetOne("(objectclass=domain)", new[] { "objectsid" }, SearchScope.Subtree); //If we get nothing back from LDAP, something is wrong if (result == null) { Console.WriteLine("LDAP Connection Test Failed. Check if you're in a domain context!"); return; } var initialCompleted = false; var needsCancellation = false; Timer timer = null; var loopEnd = DateTime.Now; //If loop is set, set up our timer for the loop now if (options.Loop) { loopEnd = loopEnd.AddMilliseconds(options.LoopDuration.TotalMilliseconds); timer = new Timer(); timer.Elapsed += (sender, eventArgs) => { if (initialCompleted) { Helpers.InvokeCancellation(); } else { needsCancellation = true; } }; timer.Interval = options.LoopDuration.TotalMilliseconds; timer.AutoReset = false; timer.Start(); } //Create our Cache Cache.CreateInstance(); //Start the computer error task (if specified) OutputTasks.StartComputerStatusTask(); //Build our pipeline, and get the initial block to wait for completion. var pipelineCompletionTask = PipelineBuilder.GetBasePipelineForDomain(options.Domain); //Wait for output to complete await pipelineCompletionTask; //Wait for our output tasks to finish. await OutputTasks.CompleteOutput(); //Mark our initial run as complete, signalling that we're now in the looping phase initialCompleted = true; if (needsCancellation) { Helpers.InvokeCancellation(); } //Start looping if specified if (Options.Instance.Loop) { if (Helpers.GetCancellationToken().IsCancellationRequested) { Console.WriteLine("Skipping looping because loop duration has already passed"); } else { Console.WriteLine(); Console.WriteLine("Waiting 30 seconds before starting loops"); try { await Task.Delay(TimeSpan.FromSeconds(30), Helpers.GetCancellationToken()); } catch (TaskCanceledException) { Console.WriteLine("Skipped wait because loop duration has completed!"); } if (!Helpers.GetCancellationToken().IsCancellationRequested) { Console.WriteLine(); Console.WriteLine($"Loop Enumeration Methods: {options.GetLoopCollectionMethods()}"); Console.WriteLine($"Looping scheduled to stop at {loopEnd.ToLongTimeString()} on {loopEnd.ToShortDateString()}"); Console.WriteLine(); } var count = 0; while (!Helpers.GetCancellationToken().IsCancellationRequested) { count++; var currentTime = DateTime.Now; Console.WriteLine($"Starting loop #{count} at {currentTime.ToShortTimeString()} on {currentTime.ToShortDateString()}"); Helpers.StartNewRun(); pipelineCompletionTask = PipelineBuilder.GetLoopPipelineForDomain(Options.Instance.Domain); await pipelineCompletionTask; await OutputTasks.CompleteOutput(); if (!Helpers.GetCancellationToken().IsCancellationRequested) { Console.WriteLine(); Console.WriteLine($"Waiting {options.LoopInterval.TotalSeconds} seconds for next loop"); Console.WriteLine(); try { await Task.Delay(options.LoopInterval, Helpers.GetCancellationToken()); } catch (TaskCanceledException) { Console.WriteLine("Skipping wait as loop duration has expired"); } } } if (count > 0) { Console.WriteLine($"Looping finished! Looped a total of {count} times"); } //Special function to grab all the zip files created by looping and collapse them into a single file await OutputTasks.CollapseLoopZipFiles(); } } timer?.Dispose(); //Program exit started. Save the cache file Cache.Instance.SaveCache(); //And we're done! var currTime = DateTime.Now; Console.WriteLine(); Console.WriteLine($"SharpHound Enumeration Completed at {currTime.ToShortTimeString()} on {currTime.ToShortDateString()}! Happy Graphing!"); Console.WriteLine(); }
private static async Task Main(string[] args) { //TODO: Trusts var parser = new Parser(with => { with.CaseInsensitiveEnumValues = true; with.CaseSensitive = false; with.HelpWriter = Console.Error; }); parser.ParseArguments <Options>(args).WithParsed(o => { var currentTime = DateTime.Now; var initString = $"Initializing SharpHound at {currentTime.ToShortTimeString()} on {currentTime.ToShortDateString()}"; Console.WriteLine(new string('-', initString.Length)); Console.WriteLine(initString); Console.WriteLine(new string('-', initString.Length)); Console.WriteLine(); if (o.OverrideUserName != null) { o.CurrentUserName = o.OverrideUserName; } else { o.CurrentUserName = WindowsIdentity.GetCurrent().Name.Split('\\')[1]; } if (o.Loop) { if (o.LoopDuration == TimeSpan.Zero) { Console.WriteLine("Loop specified without a duration. Defaulting to 2 hours!"); o.LoopDuration = TimeSpan.FromHours(2); } if (o.LoopInterval == TimeSpan.Zero) { o.LoopInterval = TimeSpan.FromSeconds(30); } } Options.Instance = o; }).WithNotParsed(error => { }); parser.Dispose(); var options = Options.Instance; if (options == null) { return; } if (!options.ResolveCollectionMethods()) { return; } var searcher = Helpers.GetDirectorySearcher(options.Domain); var result = await searcher.GetOne($"(objectsid={Helpers.ConvertSidToHexSid("S-1-5-32-544")})", new[] { "objectsid" }, SearchScope.Subtree); if (result == null) { Console.WriteLine("LDAP Connection Test Failed. Check if you're in a domain context!"); return; } var initialCompleted = false; var needsCancellation = false; Timer timer = null; var loopEnd = DateTime.Now; if (options.Loop) { loopEnd = loopEnd.AddMilliseconds(options.LoopDuration.TotalMilliseconds); timer = new Timer(); timer.Elapsed += (sender, eventArgs) => { if (initialCompleted) { Helpers.InvokeCancellation(); } else { needsCancellation = true; } }; timer.Interval = options.LoopDuration.TotalMilliseconds; timer.AutoReset = false; timer.Start(); } Cache.CreateInstance(); OutputTasks.StartComputerStatusTask(); var pipelineCompletionTask = PipelineBuilder.GetBasePipelineForDomain(options.Domain); await pipelineCompletionTask; await OutputTasks.CompleteOutput(); initialCompleted = true; if (needsCancellation) { Helpers.InvokeCancellation(); } if (Options.Instance.Loop) { if (Helpers.GetCancellationToken().IsCancellationRequested) { Console.WriteLine("Skipping looping because loop duration has already passed"); } else { Console.WriteLine(); Console.WriteLine("Waiting 30 seconds before starting loops"); try { await Task.Delay(TimeSpan.FromSeconds(30), Helpers.GetCancellationToken()); } catch (TaskCanceledException) { Console.WriteLine("Skipped wait because loop duration has completed!"); } if (!Helpers.GetCancellationToken().IsCancellationRequested) { Console.WriteLine(); Console.WriteLine($"Loop Enumeration Methods: {options.GetLoopCollectionMethods()}"); Console.WriteLine($"Looping scheduled to stop at {loopEnd.ToLongTimeString()} on {loopEnd.ToShortDateString()}"); Console.WriteLine(); } var count = 0; while (!Helpers.GetCancellationToken().IsCancellationRequested) { count++; var currentTime = DateTime.Now; Console.WriteLine($"Starting loop #{count} at {currentTime.ToShortTimeString()} on {currentTime.ToShortDateString()}"); Helpers.StartNewRun(); pipelineCompletionTask = PipelineBuilder.GetLoopPipelineForDomain(Options.Instance.Domain); await pipelineCompletionTask; await OutputTasks.CompleteOutput(); if (!Helpers.GetCancellationToken().IsCancellationRequested) { Console.WriteLine(); Console.WriteLine($"Waiting {options.LoopInterval.TotalSeconds} seconds for next loop"); Console.WriteLine(); try { await Task.Delay(options.LoopInterval, Helpers.GetCancellationToken()); } catch (TaskCanceledException) { Console.WriteLine("Skipping wait as loop duration has expired"); } } } if (count > 0) { Console.WriteLine($"Looping finished! Looped a total of {count} times"); } await OutputTasks.CollapseLoopZipFiles(); } } timer?.Dispose(); Cache.Instance.SaveCache(); Console.WriteLine(); Console.WriteLine("SharpHound Enumeration Completed! Happy Graphing!"); Console.WriteLine(); }