public static void Main(string[] args) { if (args == null) { throw new ArgumentNullException(nameof(args)); } var options = new Options(); if (!Parser.Default.ParseArguments(args, options)) { return; } if (options.Debug) { Console.WriteLine("Debug Mode activated!"); options.Threads = 1; } if (options.MaxLoopTime != null && options.CollectMethod.Equals(SessionLoop)) { var regex = new Regex("[0-9]+[smdh]"); var matches = regex.Matches(options.MaxLoopTime); var numregex = new Regex("[0-9]+"); var timeregex = new Regex("[smdh]"); if (matches.Count == 0) { Console.WriteLine("LoopEndTime does not match required format"); return; } var now = DateTime.Now; var drift = 0; foreach (var match in matches) { var num = int.Parse(numregex.Match(match.ToString()).Value); var spec = timeregex.Match(match.ToString()); switch (spec.Value) { case "s": now = now.AddSeconds(num); drift += num; break; case "m": now = now.AddMinutes(num); drift += num * 60; break; case "h": now = now.AddHours(num); drift += num * 60 * 60; break; case "d": now = now.AddDays(num); drift += num * 60 * 60 * 24; break; } } options.LoopEnd = now; if (drift == 0) { Console.WriteLine("LoopEndTime is zero! Specify a real value"); return; } } options.CurrentUser = WindowsIdentity.GetCurrent().Name.Split('\\')[1]; var nowtime = DateTime.Now; Console.WriteLine($"Initializing BloodHound at {nowtime.ToShortTimeString()} on {nowtime.ToShortDateString()}"); Cache.CreateInstance(options); Utils.CreateInstance(options); if (!Utils.CheckWritePrivs()) { Console.WriteLine("Unable to write in chosen directory. Please check privs"); return; } SessionHelpers.Init(options); LocalAdminHelpers.Init(); GroupHelpers.Init(); AclHelpers.Init(); DomainTrustEnumeration.Init(); ContainerHelpers.Init(); if (options.Test != null) { Test.DoStuff(options.Test); return; } if (options.ComputerFile != null) { if (!File.Exists(options.ComputerFile)) { Console.WriteLine("Specified ComputerFile does not exist!"); return; } if (options.CollectMethod.Equals(Default)) { options.CollectMethod = ComputerOnly; Console.WriteLine("ComputerFile detected with default enumeration. Switching to ComputerOnly collection method"); } if (!(options.CollectMethod.Equals(Session) || options.CollectMethod.Equals(SessionLoop) || options.CollectMethod.Equals(LoggedOn) || options.CollectMethod.Equals(LocalGroup) || options.CollectMethod.Equals(ComputerOnly))) { Console.WriteLine("ComputerFile can only be used with the following collection methods: ComputerOnly, Session, SessionLoop, LocalGroup, LoggedOn"); return; } } //Lets test our connection to LDAP before we do anything else try { using (var conn = Utils.Instance.GetLdapConnection(options.Domain)) { if (conn == null) { Console.WriteLine("LDAP connection test failed, probably can't contact domain"); return; } conn.Bind(); } } catch (LdapException) { Console.WriteLine("Ldap Connection Failure."); Console.WriteLine("Try again with the IgnoreLdapCert option if using SecureLDAP or check your DomainController option"); return; } if (options.Uri != null) { using (var client = new WebClient()) { client.Headers.Add("content-type", "application/json"); client.Headers.Add("Accept", "application/json; charset=UTF-8"); if (options.UserPass != null) { client.Headers.Add("Authorization", options.GetEncodedUserPass()); } try { client.DownloadData(options.GetCheckURI()); Console.WriteLine("Successfully connected to the Neo4j REST endpoint."); } catch { Console.WriteLine("Unable to connect to the Neo4j REST endpoint. Check your URI and username/password"); return; } } } if (options.Stealth) { Console.WriteLine("Note: All stealth options are single threaded"); } if (options.CollectMethod.Equals(LocalGroup) && options.Stealth) { Console.WriteLine("Note: You specified Stealth and LocalGroup which is equivalent to GPOLocalGroup"); options.CollectMethod = GPOLocalGroup; } var runner = new EnumerationRunner(options); if (options.CollectMethod.Equals(SessionLoop)) { if (options.MaxLoopTime == null) { Console.WriteLine("Session Loop mode specified without MaxLoopTime, will loop indefinitely"); } else { Console.WriteLine($"Session Loop mode specified. Looping will end on {options.LoopEnd.ToShortDateString()} at {options.LoopEnd.ToShortTimeString()}"); } } if (options.Stealth) { runner.StartStealthEnumeration(); } else { runner.StartEnumeration(); } Cache.Instance.SaveCache(); Utils.DeduplicateFiles(); if (options.CompressData) { Utils.CompressFiles(); } }
public static void Main(string[] args) { if (args == null) { throw new ArgumentNullException(nameof(args)); } var options = new Options(); if (!Parser.Default.ParseArguments(args, options)) { return; } if (options.CacheFile == null) { var sid = Utils.GetLocalMachineSid(); var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(sid); options.CacheFile = $"{Convert.ToBase64String(plainTextBytes)}.bin"; } try { // ReSharper disable once ReturnValueOfPureMethodIsNotUsed Path.Combine(options.JsonFolder, options.CacheFile); } catch (ArgumentException) { Console.WriteLine("Invalid characters in output path. Check for trailing backslashes!"); return; } if (options.CollectionMethod.Length == 1) { options.CollectionMethod = options.CollectionMethod[0].Split(','); } if (options.Jitter > 100 || options.Jitter < 0) { Console.WriteLine("Jitter must be a value between 0 and 100!"); return; } if (options.Throttle < 0) { Console.WriteLine("Throttle must be 0 or greater!"); return; } var resolved = ResolvedCollectionMethod.None; foreach (var unparsed in options.CollectionMethod) { try { var e = (CollectionMethod)Enum.Parse(typeof(CollectionMethod), unparsed, true); switch (e) { case All: resolved = resolved | ResolvedCollectionMethod.ACL | ResolvedCollectionMethod.Container | ResolvedCollectionMethod.Group | ResolvedCollectionMethod.LocalAdmin | ResolvedCollectionMethod.ObjectProps | ResolvedCollectionMethod.RDP | ResolvedCollectionMethod.Session | ResolvedCollectionMethod.Trusts | ResolvedCollectionMethod.DCOM; break; case DcOnly: resolved = resolved | ResolvedCollectionMethod.ACL | ResolvedCollectionMethod.Container | ResolvedCollectionMethod.Trusts | ResolvedCollectionMethod.ObjectProps | ResolvedCollectionMethod.GPOLocalGroup | ResolvedCollectionMethod.Group; break; case CollectionMethod.Group: resolved = resolved | ResolvedCollectionMethod.Group; break; case ComputerOnly: resolved = resolved | ResolvedCollectionMethod.LocalAdmin | ResolvedCollectionMethod.Session | ResolvedCollectionMethod.RDP | ResolvedCollectionMethod.DCOM; break; case LocalGroup: resolved = resolved | ResolvedCollectionMethod.LocalAdmin | ResolvedCollectionMethod.RDP | ResolvedCollectionMethod.DCOM; break; case GPOLocalGroup: resolved = resolved | ResolvedCollectionMethod.GPOLocalGroup; break; case Session: resolved = resolved | ResolvedCollectionMethod.Session; break; case LoggedOn: resolved = resolved | ResolvedCollectionMethod.LoggedOn; break; case Trusts: resolved = resolved | ResolvedCollectionMethod.Trusts; break; case ACL: resolved = resolved | ResolvedCollectionMethod.ACL; break; case SessionLoop: resolved = resolved | ResolvedCollectionMethod.SessionLoop; break; case Default: resolved = resolved | ResolvedCollectionMethod.RDP | ResolvedCollectionMethod.DCOM | ResolvedCollectionMethod.LocalAdmin | ResolvedCollectionMethod.Group | ResolvedCollectionMethod.Session | ResolvedCollectionMethod.Trusts; break; case ObjectProps: resolved = resolved | ResolvedCollectionMethod.ObjectProps; break; case Container: resolved = resolved | ResolvedCollectionMethod.Container; break; case LocalAdmin: resolved = resolved | ResolvedCollectionMethod.LocalAdmin; break; case RDP: resolved = resolved | ResolvedCollectionMethod.RDP; break; case DCOM: resolved = resolved | ResolvedCollectionMethod.DCOM; break; default: throw new ArgumentOutOfRangeException(); } } catch { Console.WriteLine($"Failed to parse value {unparsed}. Check your values for CollectionMethods!"); return; } } if (options.Debug) { Console.WriteLine("Debug Mode activated!"); options.Threads = 1; } if ((resolved & ResolvedCollectionMethod.SessionLoop) != 0) { if (options.MaxLoopTime != null) { var regex = new Regex("[0-9]+[smdh]"); var matches = regex.Matches(options.MaxLoopTime); var numregex = new Regex("[0-9]+"); var timeregex = new Regex("[smdh]"); if (matches.Count == 0) { Console.WriteLine("LoopEndTime does not match required format"); return; } var now = DateTime.Now; var drift = 0; foreach (var match in matches) { var num = int.Parse(numregex.Match(match.ToString()).Value); var spec = timeregex.Match(match.ToString()); switch (spec.Value) { case "s": now = now.AddSeconds(num); drift += num; break; case "m": now = now.AddMinutes(num); drift += num * 60; break; case "h": now = now.AddHours(num); drift += num * 60 * 60; break; case "d": now = now.AddDays(num); drift += num * 60 * 60 * 24; break; } } options.LoopEnd = now; if (drift == 0) { Console.WriteLine("LoopEndTime is zero! Specify a real value"); return; } } else { options.LoopEnd = DateTime.Now + TimeSpan.FromHours(2); } } options.CurrentUser = WindowsIdentity.GetCurrent().Name.Split('\\')[1]; Cache.CreateInstance(options); Utils.CreateInstance(options); if (!Utils.CheckWritePrivs()) { Console.WriteLine("Unable to write in chosen directory. Please check privs"); return; } var nowtime = DateTime.Now; Console.WriteLine($"Initializing BloodHound at {nowtime.ToShortTimeString()} on {nowtime.ToShortDateString()}"); if (options.ComputerFile != null) { if (options.PingTimeout < 1000) { Console.WriteLine("Increasing ping timeout to 1 second for ComputerFile mode"); options.PingTimeout = 1000; } } if (Utils.Instance.GetDomainList().Count == 0) { Console.WriteLine("Unable to contact domain. Try from a domain context!"); return; } if (options.DomainController != null) { Console.WriteLine("Manually specifying a domain controller will likely result in data loss. Only use this for performance/opsec reasons"); if (options.SearchForest) { Console.WriteLine("SearchForest is not usable with the --DomainController flag"); options.SearchForest = false; } } else { //Build our DC cache Utils.Instance.GetUsableDomainControllers(); } SessionHelpers.Init(options); LocalGroupHelpers.Init(options); GroupHelpers.Init(); AclHelpers.Init(); TrustHelpers.Init(); ContainerHelpers.Init(); if (options.Test != null) { Test.DoStuff(options.Test); return; } //Lets test our connection to LDAP before we do anything else try { var conn = Utils.Instance.GetLdapConnection(options.Domain); if (conn == null) { Console.WriteLine("LDAP connection test failed, probably can't contact domain"); return; } conn.Bind(); } catch (LdapException) { Console.WriteLine("Ldap Connection Failure."); if (options.LdapPass != null) { Console.WriteLine("Check credentials supplied to SharpHound"); } Console.WriteLine("Try again with the IgnoreLdapCert option if using SecureLDAP or check your DomainController/LdapPort option"); return; } //if (options.Uri != null) //{ // if (!options.Uri.StartsWith("http",StringComparison.OrdinalIgnoreCase)) // { // Console.WriteLine("URI must start with http:// or https://"); // return; // } // using (var client = new WebClient()) // { // client.Headers.Add("content-type", "application/json"); // client.Headers.Add("Accept", "application/json; charset=UTF-8"); // if (options.UserPass != null) // client.Headers.Add("Authorization", options.GetEncodedUserPass()); // try // { // client.DownloadData(options.GetCheckURI()); // Console.WriteLine("Successfully connected to the Neo4j REST endpoint."); // Console.WriteLine("WARNING: As of BloodHound 1.5, using the REST API is unsupported and will be removed in a future release."); // Console.WriteLine("WARNING: Container collection will not work with the REST API, and bugs may exist."); // } // catch // { // Console.WriteLine("Unable to connect to the Neo4j REST endpoint. Check your URI and username/password"); // Console.WriteLine("WARNING: As of BloodHound 1.5, using the REST API is unsupported and will be removed in a future release."); // Console.WriteLine("WARNING: Container collection will not work with the REST API, and bugs may exist."); // return; // } // } //} if (options.Stealth) { Console.WriteLine("Note: All stealth options are single threaded"); } if (options.Throttle > 0) { Console.WriteLine( $"Adding a delay of {options.Throttle} milliseconds to computer requests with a jitter of {options.Jitter}%"); } //Do some sanity checks if (options.ComputerFile != null) { if (!File.Exists(options.ComputerFile)) { Console.WriteLine("Specified ComputerFile does not exist!"); return; } if (options.Stealth) { Console.WriteLine("Switching to one thread for ComputerFile, removing stealth"); options.Stealth = false; options.Threads = 1; } Console.WriteLine("ComputerFile detected! Removing non-computer collection methods"); resolved = resolved & ~ResolvedCollectionMethod.ACL & ~ResolvedCollectionMethod.Group & ~ResolvedCollectionMethod.GPOLocalGroup & ~ResolvedCollectionMethod.Trusts & ~ResolvedCollectionMethod.Container & ~ResolvedCollectionMethod.ObjectProps; } if (options.Stealth) { if ((resolved & ResolvedCollectionMethod.LocalAdmin) != 0) { Console.WriteLine("Note: You specified Stealth and LocalGroup which is equivalent to GPOLocalGroup"); resolved = resolved & ~ResolvedCollectionMethod.LocalAdmin; resolved = resolved | ResolvedCollectionMethod.GPOLocalGroup; } if ((resolved & ResolvedCollectionMethod.LoggedOn) != 0) { Console.WriteLine("LoggedOn enumeration is not supported with Stealth"); resolved = resolved & ~ResolvedCollectionMethod.LoggedOn; } } if ((resolved & ResolvedCollectionMethod.Session) != 0 && (resolved & ResolvedCollectionMethod.SessionLoop) != 0) { resolved = resolved ^ ResolvedCollectionMethod.Session; } if ((resolved & ResolvedCollectionMethod.LoggedOn) != 0 && (resolved & ResolvedCollectionMethod.SessionLoop) != 0) { resolved = resolved ^ ResolvedCollectionMethod.LoggedOn; resolved = resolved | ResolvedCollectionMethod.LoggedOnLoop; } if ((resolved & ResolvedCollectionMethod.SessionLoop) != 0) { Console.WriteLine(options.MaxLoopTime == null ? $"Session Loop mode specified without MaxLoopTime, will loop for 2 hours ({options.LoopEnd.ToShortDateString()} at {options.LoopEnd.ToShortTimeString()})" : $"Session Loop mode specified. Looping will end on {options.LoopEnd.ToShortDateString()} at {options.LoopEnd.ToShortTimeString()}"); Console.WriteLine("Looping will start after any other collection methods"); } if (resolved.Equals(ResolvedCollectionMethod.None)) { Console.WriteLine("No collection methods specified. Exiting"); return; } Console.WriteLine($"Resolved Collection Methods to {resolved}"); if ((resolved & ResolvedCollectionMethod.ACL) != 0) { Utils.Verbose("Building GUID Cache"); AclHelpers.BuildGuidCache(); } options.ResolvedCollMethods = resolved; var runner = new EnumerationRunner(options); if (options.Stealth) { runner.StartStealthEnumeration(); } else { if (options.ComputerFile == null) { runner.StartEnumeration(); } else { runner.StartCompFileEnumeration(); } } Console.WriteLine(); Cache.Instance.SaveCache(); Utils.Instance.KillConnections(); if (!options.NoZip) { Utils.CompressFiles(); } }
public static void Main(string[] args) { if (args == null) { throw new ArgumentNullException(nameof(args)); } var options = new Options(); if (!Parser.Default.ParseArguments(args, options)) { return; } try { // ReSharper disable once ReturnValueOfPureMethodIsNotUsed Path.Combine(options.CSVFolder, options.CacheFile); } catch (ArgumentException) { Console.WriteLine("Invalid characters in output path. Check for trailing backslashes!"); return; } var collectionMethods = new List <CollectionMethod>(); if (options.CollectionMethod.Length == 1) { options.CollectionMethod = options.CollectionMethod[0].Split(','); } if (options.Jitter > 100 || options.Jitter < 0) { Console.WriteLine("Jitter must be a value between 0 and 100!"); return; } if (options.Throttle < 0) { Console.WriteLine("Throttle must be 0 or greater!"); return; } foreach (var unparsed in options.CollectionMethod) { try { var e = (CollectionMethod)Enum.Parse(typeof(CollectionMethod), unparsed, true); collectionMethods.Add(e); } catch { Console.WriteLine($"Failed to parse value {unparsed}. Check your values for CollectionMethods!"); return; } } if (options.Debug) { Console.WriteLine("Debug Mode activated!"); options.Threads = 1; } if (options.MaxLoopTime != null && collectionMethods.Contains(SessionLoop)) { var regex = new Regex("[0-9]+[smdh]"); var matches = regex.Matches(options.MaxLoopTime); var numregex = new Regex("[0-9]+"); var timeregex = new Regex("[smdh]"); if (matches.Count == 0) { Console.WriteLine("LoopEndTime does not match required format"); return; } var now = DateTime.Now; var drift = 0; foreach (var match in matches) { var num = int.Parse(numregex.Match(match.ToString()).Value); var spec = timeregex.Match(match.ToString()); switch (spec.Value) { case "s": now = now.AddSeconds(num); drift += num; break; case "m": now = now.AddMinutes(num); drift += num * 60; break; case "h": now = now.AddHours(num); drift += num * 60 * 60; break; case "d": now = now.AddDays(num); drift += num * 60 * 60 * 24; break; } } options.LoopEnd = now; if (drift == 0) { Console.WriteLine("LoopEndTime is zero! Specify a real value"); return; } } options.CurrentUser = WindowsIdentity.GetCurrent().Name.Split('\\')[1]; var nowtime = DateTime.Now; Console.WriteLine($"Initializing BloodHound at {nowtime.ToShortTimeString()} on {nowtime.ToShortDateString()}"); if (options.ComputerFile != null) { if (options.PingTimeout < 1000) { Console.WriteLine("Increasing ping timeout to 1 second for ComputerFile mode"); options.PingTimeout = 1000; } } Cache.CreateInstance(options); Utils.CreateInstance(options); if (!Utils.CheckWritePrivs()) { Console.WriteLine("Unable to write in chosen directory. Please check privs"); return; } if (Utils.Instance.GetDomainList().Count == 0) { Console.WriteLine("Unable to contact domain. Try from a domain context!"); return; } SessionHelpers.Init(options); LocalAdminHelpers.Init(); GroupHelpers.Init(); AclHelpers.Init(); DomainTrustEnumeration.Init(); ContainerHelpers.Init(); if (options.Test != null) { Test.DoStuff(options.Test); return; } //Lets test our connection to LDAP before we do anything else try { using (var conn = Utils.Instance.GetLdapConnection(options.Domain)) { if (conn == null) { Console.WriteLine("LDAP connection test failed, probably can't contact domain"); return; } conn.Bind(); } } catch (LdapException) { Console.WriteLine("Ldap Connection Failure."); Console.WriteLine("Try again with the IgnoreLdapCert option if using SecureLDAP or check your DomainController/LdapPort option"); return; } if (options.Uri != null) { if (!options.Uri.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("URI must start with http:// or https://"); return; } using (var client = new WebClient()) { client.Headers.Add("content-type", "application/json"); client.Headers.Add("Accept", "application/json; charset=UTF-8"); if (options.UserPass != null) { client.Headers.Add("Authorization", options.GetEncodedUserPass()); } try { client.DownloadData(options.GetCheckURI()); Console.WriteLine("Successfully connected to the Neo4j REST endpoint."); } catch { Console.WriteLine("Unable to connect to the Neo4j REST endpoint. Check your URI and username/password"); return; } } } if (options.RemoveCSV && !options.CompressData) { Console.WriteLine("Ignoring RemoveCSV as CompressData is not set"); options.RemoveCSV = false; } if (options.Stealth) { Console.WriteLine("Note: All stealth options are single threaded"); } if (options.Throttle > 0) { Console.WriteLine( $"Adding a delay of {options.Throttle} milliseconds to computer requests with a jitter of {options.Jitter}%"); } foreach (var cmethod in collectionMethods) { options.CurrentCollectionMethod = cmethod; if (options.ComputerFile != null) { if (!File.Exists(options.ComputerFile)) { Console.WriteLine("Specified ComputerFile does not exist!"); return; } if (options.CurrentCollectionMethod.Equals(Default)) { options.CurrentCollectionMethod = ComputerOnly; Console.WriteLine("ComputerFile detected with default enumeration. Switching to ComputerOnly collection method"); } if (!(options.CurrentCollectionMethod.Equals(Session) || options.CurrentCollectionMethod.Equals(SessionLoop) || options.CurrentCollectionMethod.Equals(LoggedOn) || options.CurrentCollectionMethod.Equals(LocalGroup) || options.CurrentCollectionMethod.Equals(ComputerOnly))) { Console.WriteLine("ComputerFile can only be used with the following collection methods: ComputerOnly, Session, SessionLoop, LocalGroup, LoggedOn"); continue; } } if (options.CurrentCollectionMethod.Equals(LocalGroup) && options.Stealth) { Console.WriteLine("Note: You specified Stealth and LocalGroup which is equivalent to GPOLocalGroup"); options.CurrentCollectionMethod = GPOLocalGroup; } var runner = new EnumerationRunner(options); if (options.CurrentCollectionMethod.Equals(SessionLoop)) { Console.WriteLine(options.MaxLoopTime == null ? "Session Loop mode specified without MaxLoopTime, will loop indefinitely" : $"Session Loop mode specified. Looping will end on {options.LoopEnd.ToShortDateString()} at {options.LoopEnd.ToShortTimeString()}"); } if (options.Stealth) { runner.StartStealthEnumeration(); } else { runner.StartEnumeration(); } Console.WriteLine(); } Cache.Instance.SaveCache(); Utils.DeduplicateFiles(); if (options.CompressData) { Utils.CompressFiles(); } }