public void TestServiceCollectorWindows() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Setup(); var FirstRunId = "TestServiceCollector-1"; var SecondRunId = "TestServiceCollector-2"; var fwc = new ServiceCollector(FirstRunId); fwc.Execute(); // Create a service - This won't throw an exception, but it won't work if you are not an Admin. var serviceName = "AsaDemoService"; var exeName = "AsaDemoService.exe"; var cmd = string.Format("create {0} binPath=\"{1}\"", serviceName, exeName); ExternalCommandRunner.RunExternalCommand("sc.exe", cmd); fwc = new ServiceCollector(SecondRunId); fwc.Execute(); // Clean up cmd = string.Format("delete {0}", serviceName); ExternalCommandRunner.RunExternalCommand("sc.exe", cmd); BaseCompare bc = new BaseCompare(); if (!bc.TryCompare(FirstRunId, SecondRunId)) { Assert.Fail(); } var results = bc.Results; Assert.IsTrue(results.ContainsKey("SERVICE_CREATED")); Assert.IsTrue(results["SERVICE_CREATED"].Where(x => x.Identity.Contains("AsaDemoService")).Count() > 0); TearDown(); } }
/// <summary> /// Executes the OpenPortCollector on Linux. Calls out to the `ss` /// command and parses the output, sending the output to the database. /// </summary> private void ExecuteLinux() { Log.Debug("ExecuteLinux()"); var result = ExternalCommandRunner.RunExternalCommand("ss", "-ln"); foreach (var _line in result.Split('\n')) { var line = _line; line = line.ToLower(); if (!line.Contains("listen")) { continue; } var parts = Regex.Split(line, @"\s+"); if (parts.Length < 5) { continue; // Not long enough, must be an error } string address = null; string port = null; var addressMatches = Regex.Match(parts[4], @"^(.*):(\d+)$"); if (addressMatches.Success) { address = addressMatches.Groups[1].ToString(); port = addressMatches.Groups[2].ToString(); var obj = new OpenPortObject() { family = parts[0],//@TODO: Determine IPV4 vs IPv6 via looking at the address address = address, port = port, type = parts[0] }; DatabaseManager.Write(obj, this.runId); } } }
public void TestUserCollectorWindows() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Assert.IsTrue(AsaHelpers.IsAdmin()); Setup(); var FirstRunId = "TestUserCollector-1"; var SecondRunId = "TestUserCollector-2"; var fwc = new UserAccountCollector(FirstRunId); fwc.Execute(); var user = System.Guid.NewGuid().ToString().Substring(0, 10); var password = System.Guid.NewGuid().ToString().Substring(0, 10); var cmd = string.Format("user /add {0} {1}", user, password); ExternalCommandRunner.RunExternalCommand("net", cmd); var serviceName = System.Guid.NewGuid(); fwc = new UserAccountCollector(SecondRunId); fwc.Execute(); cmd = string.Format("user /delete {0}", user); ExternalCommandRunner.RunExternalCommand("net", cmd); BaseCompare bc = new BaseCompare(); if (!bc.TryCompare(FirstRunId, SecondRunId)) { Assert.Fail(); } var results = bc.Results; Assert.IsTrue(results.ContainsKey("USER_CREATED")); Assert.IsTrue(results["USER_CREATED"].Where(x => x.Identity.Contains(user)).Count() > 0); TearDown(); } }
/// <summary> /// Uses launchctl /// </summary> public void ExecuteMacOs() { // Get the user processes // run "launchtl dumpstate" for the super detailed view // However, dumpstate is difficult to parse Dictionary <string, ServiceObject> outDict = new Dictionary <string, ServiceObject>(); try { var result = ExternalCommandRunner.RunExternalCommand("launchctl", "list"); foreach (var _line in result.Split('\n')) { // Lines are formatted like this: // PID Exit Name //1015 0 com.apple.appstoreagent var _fields = _line.Split('\t'); if (_fields.Length < 3 || _fields[0].Contains("PID")) { continue; } var obj = new ServiceObject() { DisplayName = _fields[2], Name = _fields[2], // If we have a current PID then it is running. State = (_fields[0].Equals("-")) ? "Stopped" : "Running" }; if (!outDict.ContainsKey(obj.Identity)) { DatabaseManager.Write(obj, this.RunId); outDict.Add(obj.Identity, obj); } } } catch (ExternalException) { Log.Error("Error executing {0}", "launchctl list"); } }
/// <summary> /// On macos we use the keychain and export the certificates as .pem. /// However, on macos Certificate2 doesn't support loading from a pem. /// So first we need pkcs12s instead, we convert using openssl, which requires we set a password /// we import the pkcs12 with all our certs, delete the temp files and then iterate over it the certs /// </summary> public void ExecuteMacOs() { try { if (ExternalCommandRunner.RunExternalCommand("security", "find-certificate -ap /System/Library/Keychains/SystemRootCertificates.keychain", out string result, out string _) == 0) { string tmpPath = Path.Combine(Directory.GetCurrentDirectory(), "tmpcert.pem"); string pkPath = Path.Combine(Directory.GetCurrentDirectory(), "tmpcert.pk12"); File.WriteAllText(tmpPath, result); if (ExternalCommandRunner.RunExternalCommand("openssl", $"pkcs12 -export -nokeys -out {pkPath} -passout pass:pass -in {tmpPath}", out string _, out string _) == 0) { X509Certificate2Collection xcert = new X509Certificate2Collection(); xcert.Import(pkPath, "pass", X509KeyStorageFlags.DefaultKeySet); //lgtm [cs/hardcoded-credentials] File.Delete(tmpPath); File.Delete(pkPath); var X509Certificate2Enumerator = xcert.GetEnumerator(); while (X509Certificate2Enumerator.MoveNext()) { var certificate = X509Certificate2Enumerator.Current; var obj = new CertificateObject( StoreLocation: StoreLocation.LocalMachine.ToString(), StoreName: StoreName.Root.ToString(), Certificate: new SerializableCertificate(certificate)) { Pkcs7 = Convert.ToBase64String(certificate.Export(X509ContentType.Cert)) }; Results.Enqueue(obj); } } else { Log.Debug("Failed to export certificate with OpenSSL."); //DevSkim: ignore DS440000 } }
/// <summary> /// On macos we use the keychain and export the certificates as .pem. /// However, on macos Certificate2 doesn't support loading from a pem. /// So first we need pkcs12s instead, we convert using openssl, which requires we set a password /// we import the pkcs12 with all our certs, delete the temp files and then iterate over it the certs /// </summary> public void ExecuteMacOs() { try { var result = ExternalCommandRunner.RunExternalCommand("security", new string[] { "find-certificate", "-ap", "/System/Library/Keychains/SystemRootCertificates.keychain" }); string tmpPath = Path.Combine(Directory.GetCurrentDirectory(), "tmpcert.pem"); string pkPath = Path.Combine(Directory.GetCurrentDirectory(), "tmpcert.pk12"); File.WriteAllText(tmpPath, result); _ = ExternalCommandRunner.RunExternalCommand("openssl", new string[] { "pkcs12", "-export", "-nokeys", "-out", pkPath, "-passout pass:pass", "-in", tmpPath }); X509Certificate2Collection xcert = new X509Certificate2Collection(); xcert.Import(pkPath, "pass", X509KeyStorageFlags.DefaultKeySet); File.Delete(tmpPath); File.Delete(pkPath); var X509Certificate2Enumerator = xcert.GetEnumerator(); while (X509Certificate2Enumerator.MoveNext()) { var obj = new CertificateObject() { StoreLocation = StoreLocation.LocalMachine.ToString(), StoreName = StoreName.Root.ToString(), CertificateHashString = X509Certificate2Enumerator.Current.GetCertHashString(), Subject = X509Certificate2Enumerator.Current.Subject, Pkcs12 = X509Certificate2Enumerator.Current.GetRawCertDataString() }; DatabaseManager.Write(obj, this.runId); } } catch (Exception e) { Log.Error("Failed to dump certificates from 'security' or 'openssl'."); Logger.DebugException(e); } }
private static long SizeOnDisk(string path) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { FileInfo info = new FileInfo(path); uint clusterSize = ClusterSizes[info.Directory.Root.FullName]; uint lowSize = GetCompressedFileSizeW(path, out uint highSize); long size = (long)highSize << 32 | lowSize; return(((size + clusterSize - 1) / clusterSize) * clusterSize); } else { var exitCode = ExternalCommandRunner.RunExternalCommand("du", path, out string StdOut, out string StdErr); if (exitCode == 0 && long.TryParse(StdOut.Split('\t')[0], out long result)) { return(result); } else { return(0); } } }
/// <summary> /// On linux we check the central trusted root store (a folder), which has symlinks to actual cert locations scattered across the db /// We list all the certificates and then create a new X509Certificate2 object for each by filename. /// </summary> public void ExecuteLinux() { try { if (ExternalCommandRunner.RunExternalCommand("ls", "/etc/ssl/certs -A", out string result, out string _) == 0) { foreach (var _line in result.Split('\n')) { Log.Debug("{0}", _line); try { using X509Certificate2 certificate = new X509Certificate2("/etc/ssl/certs/" + _line); var obj = new CertificateObject( StoreLocation: StoreLocation.LocalMachine.ToString(), StoreName: StoreName.Root.ToString(), Certificate: new SerializableCertificate(certificate)) { Pkcs7 = Convert.ToBase64String(certificate.Export(X509ContentType.Cert)) }; DatabaseManager.Write(obj, RunId); } catch (Exception e) { Log.Debug("{0} {1} Issue creating certificate based on /etc/ssl/certs/{2}", e.GetType().ToString(), e.Message, _line); Log.Debug("{0}", e.StackTrace); } } } } catch (Exception e) { Log.Error(e, "Failed to dump certificates from 'ls /etc/ssl/certs -A'."); } }
/// <summary> /// On linux we check the central trusted root store (a folder), which has symlinks to /// actual cert locations scattered across the db We list all the certificates and then /// create a new X509Certificate2 object for each by filename. /// </summary> internal void ExecuteLinux(CancellationToken cancellationToken) { try { if (ExternalCommandRunner.RunExternalCommand("ls", "/etc/ssl/certs -A", out string result, out string _) == 0) { foreach (var _line in result.Split('\n')) { if (cancellationToken.IsCancellationRequested) { return; } Log.Debug("{0}", _line); try { using X509Certificate2 certificate = new X509Certificate2("/etc/ssl/certs/" + _line); var obj = new CertificateObject( StoreLocation: StoreLocation.LocalMachine.ToString(), StoreName: StoreName.Root.ToString(), Certificate: new SerializableCertificate(certificate)); HandleChange(obj); } catch (Exception e) { Log.Debug("{0} {1} Issue creating certificate based on /etc/ssl/certs/{2}", e.GetType().ToString(), e.Message, _line); Log.Debug("{0}", e.StackTrace); } } } } catch (Exception e) { Log.Error(e, "Failed to dump certificates from 'ls /etc/ssl/certs -A'."); } }
/// <summary> /// On linux we check the central trusted root store (a folder), which has symlinks to actual cert locations scattered across the db /// We list all the certificates and then create a new X509Certificate2 object for each by filename. /// </summary> public void ExecuteLinux() { try { var result = ExternalCommandRunner.RunExternalCommand("ls", new string[] { "/etc/ssl/certs", "-A" }); foreach (var _line in result.Split('\n')) { Log.Debug("{0}", _line); try { X509Certificate2 certificate = new X509Certificate2("/etc/ssl/certs/" + _line); var obj = new CertificateObject() { StoreLocation = StoreLocation.LocalMachine.ToString(), StoreName = StoreName.Root.ToString(), CertificateHashString = certificate.GetCertHashString(), Subject = certificate.Subject, Pkcs12 = certificate.HasPrivateKey ? "redacted" : certificate.Export(X509ContentType.Pkcs12).ToString() }; DatabaseManager.Write(obj, this.runId); } catch (Exception e) { Log.Debug("{0} {1} Issue creating certificate based on /etc/ssl/certs/{2}", e.GetType().ToString(), e.Message, _line); Log.Debug("{0}", e.StackTrace); } } } catch (Exception e) { Log.Error("Failed to dump certificates from 'ls /etc/ssl/certs -A'."); Logger.DebugException(e); } }
public static void Main(string[] args) { Logger.Setup(true, true); try { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { if (!Elevation.IsAdministrator()) { Log.Fatal("Must run as administrator."); Log.CloseAndFlush(); Environment.Exit(-1); } var user = System.Guid.NewGuid().ToString().Substring(0, 10); var password = System.Guid.NewGuid().ToString().Substring(0, 10); var cmd = string.Format("user /add {0} {1}", user, password); ExternalCommandRunner.RunExternalCommand("net", cmd); Log.Information("Created user {0} with password {1}", user, password); var serviceName = System.Guid.NewGuid(); var exeName = "AsaDemoService.exe"; cmd = string.Format("create {0} binPath=\"{1}\"", serviceName, exeName); ExternalCommandRunner.RunExternalCommand("sc.exe", cmd); Log.Information("Created service {0} for not-present exe {1}", serviceName, exeName); } } catch (Exception ex) { Console.WriteLine(ex.Message); Console.ReadLine(); } }
/// <summary> /// Dumps from iptables. /// </summary> internal void ExecuteLinux(CancellationToken cancellationToken) { if (ExternalCommandRunner.RunExternalCommand("iptables", "-S", out string result, out string _) == 0) { var lines = new List <string>(result.Split('\n')); Dictionary <string, FirewallAction> defaultPolicies = new Dictionary <string, FirewallAction>(); foreach (var line in lines) { if (cancellationToken.IsCancellationRequested) { return; } if (line.StartsWith("-P")) { var chainName = line.Split(' ')[1]; defaultPolicies.Add(chainName, line.Contains("ACCEPT") ? FirewallAction.Allow : FirewallAction.Block); var obj = new FirewallObject($"Default {chainName} policy") { Action = defaultPolicies[chainName], FriendlyName = $"Default {chainName} policy", Scope = FirewallScope.All }; if (!chainName.Equals("FORWARD")) { obj.Direction = chainName.Equals("INPUT") ? FirewallDirection.Inbound : FirewallDirection.Outbound; } HandleChange(obj); } else if (line.StartsWith("-A")) { var splits = line.Split(' '); var chainName = splits[1]; var obj = new FirewallObject(line) { Action = (splits[Array.IndexOf(splits, "-j") + 1] == "ACCEPT") ? FirewallAction.Allow : FirewallAction.Block, FriendlyName = line, Scope = FirewallScope.All, Protocol = splits[Array.IndexOf(splits, "-p") + 1] }; if (Array.IndexOf(splits, "--dport") > 0) { obj.RemotePorts = splits[Array.IndexOf(splits, "--dport") + 1].OfType <string>().ToList(); } if (Array.IndexOf(splits, "-d") > 0) { obj.RemoteAddresses = splits[Array.IndexOf(splits, "-d") + 1].OfType <string>().ToList(); } if (Array.IndexOf(splits, "-s") > 0) { obj.LocalAddresses = splits[Array.IndexOf(splits, "-s") + 1].OfType <string>().ToList(); } if (Array.IndexOf(splits, "--sport") > 0) { obj.LocalPorts = splits[Array.IndexOf(splits, "--sport") + 1].OfType <string>().ToList(); } if (!chainName.Equals("FORWARD")) { obj.Direction = chainName.Equals("INPUT") ? FirewallDirection.Inbound : FirewallDirection.Outbound; } HandleChange(obj); } } } }
/// <summary> /// Talks to socketfilterfw /// </summary> internal void ExecuteMacOs(CancellationToken cancellationToken) { // Example output: "Firewall is enabled. (State = 1)" var result = ExternalCommandRunner.RunExternalCommand("/usr/libexec/ApplicationFirewall/socketfilterfw", "--getglobalstate"); var enabled = result.Contains("1"); var obj = new FirewallObject("Firewall Enabled") { Action = FirewallAction.Block, Direction = FirewallDirection.Inbound, IsEnable = enabled, FriendlyName = "Firewall Enabled", Scope = FirewallScope.All }; HandleChange(obj); // Example output: "Stealth mode disabled" result = ExternalCommandRunner.RunExternalCommand("/usr/libexec/ApplicationFirewall/socketfilterfw", "--getglobalstate"); obj = new FirewallObject("Stealth Mode") { Action = FirewallAction.Block, Direction = FirewallDirection.Inbound, IsEnable = result.Contains("enabled"), FriendlyName = "Stealth Mode", Scope = FirewallScope.All }; HandleChange(obj); /* Example Output: * Automatically allow signed built-in software ENABLED * Automatically allow downloaded signed software ENABLED */ result = ExternalCommandRunner.RunExternalCommand("/usr/libexec/ApplicationFirewall/socketfilterfw", "--getallowsigned"); obj = new FirewallObject("Allow signed built-in software") { Action = FirewallAction.Allow, Direction = FirewallDirection.Inbound, IsEnable = result.Split('\n')[0].Contains("ENABLED"), FriendlyName = "Allow signed built-in software", Scope = FirewallScope.All }; HandleChange(obj); obj = new FirewallObject("Allow downloaded signed software") { Action = FirewallAction.Allow, Direction = FirewallDirection.Inbound, IsEnable = result.Split('\n')[1].Contains("ENABLED"), FriendlyName = "Allow downloaded signed software", Scope = FirewallScope.All }; HandleChange(obj); /* Example Output: * ALF: total number of apps = 2 * * 1 : /Applications/AppName.app * ( Allow incoming connections ) * * 2 : /Applications/AppName2.app * ( Block incoming connections ) */ result = ExternalCommandRunner.RunExternalCommand("/usr/libexec/ApplicationFirewall/socketfilterfw", "--listapps"); string appName = ""; Regex startsWithNumber = new Regex("^[1-9]"); var lines = new List <string>(result.Split('\n')); if (lines.Any()) { lines = lines.Skip(2).ToList(); foreach (var line in lines) { if (cancellationToken.IsCancellationRequested) { return; } if (startsWithNumber.IsMatch(line)) { appName = line.Substring(line.IndexOf('/')); } else if (line.Contains("incoming connections")) { obj = new FirewallObject(appName) { Action = (line.Contains("Allow")) ? FirewallAction.Allow : FirewallAction.Block, Direction = FirewallDirection.Inbound, FriendlyName = appName, Scope = FirewallScope.All }; HandleChange(obj); } } } }
private void ExecuteOsX() { Log.Debug("ExecuteOsX()"); var runner = new ExternalCommandRunner(); // Admin user details var result = runner.RunExternalCommand("dscacheutil", "-q group -a name admin"); var lines = result.Split('\n'); // The fourth line is a list of usernames // Formatted like: 'users: root gabe' var admins = (lines[3].Split(':')[1]).Split(' '); // details for all users result = runner.RunExternalCommand("dscacheutil", "-q user"); var accountDetails = new Dictionary <string, UserAccountObject>(); // We initialize a new object. We know by the formatting of // dscacheutil that we will never have a user without the name coming // first var newUser = new UserAccountObject(); foreach (var _line in result.Split('\n')) { var parts = _line.Split(':'); if (parts.Length < 2) { // There is a blank line separating each grouping of user data continue; } // There is one space of padding, which we strip off here var value = parts[1].Substring(1); // dscacheutil prints the user information on multiple lines switch (parts[0]) { case "name": accountDetails[value] = new UserAccountObject() { Name = value, AccountType = (admins.Contains(value)) ? "administrator" : "standard" }; newUser = accountDetails[value]; break; case "password": break; case "uid": newUser.UID = value; break; case "gid": newUser.GID = value; break; case "dir": newUser.HomeDirectory = value; break; case "shell": newUser.Shell = value; break; case "gecos": newUser.FullName = value; break; default: break; } } foreach (var username in accountDetails.Keys) { Write(accountDetails[username]); } }
public override void ExecuteInternal() { if (!roots.Any()) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { foreach (var driveInfo in DriveInfo.GetDrives()) { if (driveInfo.IsReady && driveInfo.DriveType == DriveType.Fixed) { roots.Add(driveInfo.Name); } } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { roots.Add("/"); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { roots.Add("/"); } } Action <string>?IterateOnDirectory = null; IterateOnDirectory = Path => { Log.Verbose("Started parsing {0}", Path); // To optimize calls to du on non-windows platforms we run du on the whole directory ahead of time if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var exitCode = ExternalCommandRunner.RunExternalCommand("du", Path, out string StdOut, out string StdErr); if (exitCode == 0) { foreach (var line in StdOut.Split(Environment.NewLine)) { var fields = line.Split('\t'); if (long.TryParse(fields[0], out long result)) { sizesOnDisk[fields[1]] = result; } } } } var files = Directory.EnumerateFiles(Path, "*", new System.IO.EnumerationOptions() { IgnoreInaccessible = true }); foreach (var file in files) { StallIfHighMemoryUsageAndLowMemoryModeEnabled(); Log.Verbose("Started parsing {0}", file); FileSystemObject obj = FilePathToFileSystemObject(file); if (obj != null) { Results.Push(obj); // TODO: Also try parse .DER as a key if (Path.EndsWith(".cer", StringComparison.CurrentCulture) || Path.EndsWith(".der", StringComparison.CurrentCulture) || Path.EndsWith(".p7b", StringComparison.CurrentCulture) || Path.EndsWith(".pfx", StringComparison.CurrentCulture)) { try { using var certificate = new X509Certificate2(Path); var certObj = new CertificateObject( StoreLocation: StoreLocation.LocalMachine.ToString(), StoreName: StoreName.Root.ToString(), Certificate: new SerializableCertificate(certificate)); Results.Push(certObj); } catch (Exception e) { Log.Verbose($"Could not parse certificate from file: {file}, {e.GetType().ToString()}"); } } } Log.Verbose("Finished parsing {0}", file); } Log.Verbose("Finished parsing {0}", Path); }; foreach (var root in roots) { Log.Information("{0} root {1}", Strings.Get("Scanning"), root); var directories = Directory.EnumerateDirectories(root, "*", new System.IO.EnumerationOptions() { ReturnSpecialDirectories = false, IgnoreInaccessible = true, RecurseSubdirectories = true }); //First do root IterateOnDirectory?.Invoke(root); if (!opts.SingleThread == true) { Parallel.ForEach(directories, filePath => { IterateOnDirectory?.Invoke(filePath); }); } else { foreach (var filePath in directories) { IterateOnDirectory?.Invoke(filePath); } } } }
public override void Execute() { if (!CanRunOnPlatform()) { return; } Start(); Truncate(runId); // On Windows we can use the .NET API to iterate through all the stores. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { foreach (StoreLocation storeLocation in (StoreLocation[])Enum.GetValues(typeof(StoreLocation))) { foreach (StoreName storeName in (StoreName[])Enum.GetValues(typeof(StoreName))) { try { X509Store store = new X509Store(storeName, storeLocation); store.Open(OpenFlags.ReadOnly); foreach (X509Certificate2 certificate in store.Certificates) { Write(storeLocation, storeName, certificate); } store.Close(); } catch (Exception e) { Log.Debug(e.StackTrace); Log.Debug(e.GetType().ToString()); Log.Debug(e.Message); Telemetry.TrackTrace(Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Error, e); } } } } // On linux we check the central trusted root store (a folder), which has symlinks to actual cert locations scattered across the db // We list all the certificates and then create a new X509Certificate2 object for each by filename. else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { var runner = new ExternalCommandRunner(); var result = runner.RunExternalCommand("ls", new string[] { "/etc/ssl/certs", "-A" }); Log.Debug("{0}", result); foreach (var _line in result.Split('\n')) { Log.Debug("{0}", _line); try { X509Certificate2 cert = new X509Certificate2("/etc/ssl/certs/" + _line); Write(StoreLocation.LocalMachine, StoreName.Root, cert); } catch (Exception e) { Log.Debug("{0} {1} Issue creating certificate based on /etc/ssl/certs/{2}", e.GetType().ToString(), e.Message, _line); Log.Debug("{0}", e.StackTrace); } } } // On macos we use the keychain and export the certificates as .pem. // However, on macos Certificate2 doesn't support loading from a pem, // so first we need pkcs12s instead, we convert using openssl, which requires we set a password // we import the pkcs12 with all our certs, delete the temp files and then iterate over it the certs else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { var runner = new ExternalCommandRunner(); var result = runner.RunExternalCommand("security", new string[] { "find-certificate", "-ap", "/System/Library/Keychains/SystemRootCertificates.keychain" }); string tmpPath = Path.Combine(Directory.GetCurrentDirectory(), "tmpcert.pem"); string pkPath = Path.Combine(Directory.GetCurrentDirectory(), "tmpcert.pk12"); File.WriteAllText(tmpPath, result); _ = runner.RunExternalCommand("openssl", new string[] { "pkcs12", "-export", "-nokeys", "-out", pkPath, "-passout pass:pass", "-in", tmpPath }); X509Certificate2Collection xcert = new X509Certificate2Collection(); xcert.Import(pkPath, "pass", X509KeyStorageFlags.DefaultKeySet); File.Delete(tmpPath); File.Delete(pkPath); var X509Certificate2Enumerator = xcert.GetEnumerator(); while (X509Certificate2Enumerator.MoveNext()) { Write(StoreLocation.LocalMachine, StoreName.Root, X509Certificate2Enumerator.Current); } } DatabaseManager.Commit(); Stop(); }
/// <summary> /// Executes the ServiceCollector (main entrypoint). /// </summary> public override void Execute() { Start(); if (!this.CanRunOnPlatform()) { Log.Information("ServiceCollector cannot run on this platform."); return; } Truncate(runId); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // This gathers official "services" on Windows, but perhaps neglects other startup items foreach (ServiceController service in ServiceController.GetServices()) { if (this.filter != null && !this.filter(service)) { Log.Information("Service [{0}] did not pass filter, ignoring.", service.ToString()); continue; } var obj = new ServiceObject() { DisplayName = service.DisplayName, ServiceName = service.ServiceName, StartType = service.StartType.ToString(), CurrentState = service.Status.ToString() }; this.Write(obj); } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { var runner = new ExternalCommandRunner(); // Get the user processes // run "launchtl dumpstate" for the super detailed view // However, dumpstate is difficult to parse var result = runner.RunExternalCommand("launchctl", "list"); Dictionary <string, ServiceObject> outDict = new Dictionary <string, ServiceObject>(); foreach (var _line in result.Split('\n')) { // Lines are formatted like this: // PID Exit Name //1015 0 com.apple.appstoreagent var _fields = _line.Split('\t'); if (_fields.Length < 3 || _fields[0].Contains("PID")) { continue; } var obj = new ServiceObject() { DisplayName = _fields[2], ServiceName = _fields[2], StartType = "Unknown", // If we have a current PID then it is running. CurrentState = (_fields[0].Equals("-"))?"Stopped":"Running" }; if (!outDict.ContainsKey(obj.GetUniqueHash())) { this.Write(obj); outDict.Add(obj.GetUniqueHash(), obj); } } // Then get the system processes result = runner.RunExternalCommand("sudo", "launchctl list"); foreach (var _line in result.Split('\n')) { // Lines are formatted like this, with single tab separation: // PID Exit Name // 1015 0 com.apple.appstoreagent var _fields = _line.Split('\t'); if (_fields.Length < 3 || _fields[0].Contains("PID")) { continue; } var obj = new ServiceObject() { DisplayName = _fields[2], ServiceName = _fields[2], StartType = "Unknown", // If we have a current PID then it is running. CurrentState = (_fields[0].Equals("-")) ? "Stopped" : "Running" }; if (!outDict.ContainsKey(obj.GetUniqueHash())) { this.Write(obj); outDict.Add(obj.GetUniqueHash(), obj); } } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { var runner = new ExternalCommandRunner(); var result = runner.RunExternalCommand("systemctl", "list-units --type service"); //Split lines and remove header var lines = result.Split('\n'); lines.ToList().RemoveAt(0); foreach (var _line in lines) { var _fields = _line.Split('\t'); if (_fields.Count() == 5) { var obj = new ServiceObject() { DisplayName = _fields[4], ServiceName = _fields[0], StartType = "Unknown", CurrentState = _fields[3], }; Write(obj); } } result = runner.RunExternalCommand("ls", "/etc/init.d/ -l"); lines = result.Split('\n'); String pattern = @".*\s(.*)"; foreach (var _line in lines) { Match match = Regex.Match(_line, pattern); GroupCollection groups = match.Groups; var serviceName = groups[1].ToString(); var obj = new ServiceObject() { DisplayName = serviceName, ServiceName = serviceName, StartType = "Unknown", CurrentState = "Unknown" }; Write(obj); } // without systemd (maybe just CentOS) // chkconfig --list // BSD // service -l // this provides very minor amount of info } Stop(); }
/// <summary> /// Executes the ServiceCollector (main entrypoint). /// </summary> public override void Execute() { Start(); if (!this.CanRunOnPlatform()) { Log.Information(Strings.Get("Err_ServiceCollectorIncompat")); return; } if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // This gathers official "services" on Windows, but perhaps neglects other startup items foreach (ServiceController service in ServiceController.GetServices()) { var obj = new ServiceObject() { DisplayName = service.DisplayName, ServiceName = service.ServiceName, StartType = service.StartType.ToString(), CurrentState = service.Status.ToString() }; DatabaseManager.Write(obj, this.runId); } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // Get the user processes // run "launchtl dumpstate" for the super detailed view // However, dumpstate is difficult to parse try { var result = ExternalCommandRunner.RunExternalCommand("launchctl", "list"); Dictionary <string, ServiceObject> outDict = new Dictionary <string, ServiceObject>(); foreach (var _line in result.Split('\n')) { // Lines are formatted like this: // PID Exit Name //1015 0 com.apple.appstoreagent var _fields = _line.Split('\t'); if (_fields.Length < 3 || _fields[0].Contains("PID")) { continue; } var obj = new ServiceObject() { DisplayName = _fields[2], ServiceName = _fields[2], StartType = "Unknown", // If we have a current PID then it is running. CurrentState = (_fields[0].Equals("-")) ? "Stopped" : "Running" }; if (!outDict.ContainsKey(obj.Identity)) { DatabaseManager.Write(obj, this.runId); outDict.Add(obj.Identity, obj); } } // Then get the system processes result = ExternalCommandRunner.RunExternalCommand("sudo", "launchctl list"); foreach (var _line in result.Split('\n')) { // Lines are formatted like this, with single tab separation: // PID Exit Name // 1015 0 com.apple.appstoreagent var _fields = _line.Split('\t'); if (_fields.Length < 3 || _fields[0].Contains("PID")) { continue; } var obj = new ServiceObject() { DisplayName = _fields[2], ServiceName = _fields[2], StartType = "Unknown", // If we have a current PID then it is running. CurrentState = (_fields[0].Equals("-")) ? "Stopped" : "Running" }; if (!outDict.ContainsKey(obj.Identity)) { DatabaseManager.Write(obj, this.runId); outDict.Add(obj.Identity, obj); } } } catch (Exception e) { Logger.DebugException(e); } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { try { var result = ExternalCommandRunner.RunExternalCommand("systemctl", "list-units --type service"); //Split lines and remove header var lines = result.Split('\n').Skip(1); foreach (var _line in lines) { var _fields = _line.Split('\t'); if (_fields.Count() == 5) { var obj = new ServiceObject() { DisplayName = _fields[4], ServiceName = _fields[0], StartType = "Unknown", CurrentState = _fields[3], }; DatabaseManager.Write(obj, this.runId); } } } catch (Exception e) { Logger.DebugException(e); } try { var result = ExternalCommandRunner.RunExternalCommand("ls", "/etc/init.d/ -l"); var lines = result.Split('\n').Skip(1); String pattern = @".*\s(.*)"; foreach (var _line in lines) { Match match = Regex.Match(_line, pattern); GroupCollection groups = match.Groups; var serviceName = groups[1].ToString(); var obj = new ServiceObject() { DisplayName = serviceName, ServiceName = serviceName, StartType = "Unknown", CurrentState = "Unknown" }; DatabaseManager.Write(obj, this.runId); } } catch (Exception e) { Logger.DebugException(e); } // CentOS // chkconfig --list // BSD // service -l // this provides very minor amount of info } Stop(); }
/// <summary> /// Collect event logs on macOS using the 'log' utility /// </summary> public void ExecuteMacOs() { _ = DatabaseManager.Transaction; var outputPath = Path.Combine(Directory.GetCurrentDirectory(), "events"); var file = (GatherVerboseLogs)? ExternalCommandRunner.RunExternalCommand("log", "show") : ExternalCommandRunner.RunExternalCommand("log", "show --predicate \"messageType == 16 || messageType == 17\""); // New log entries start with a timestamp like so: // 2019-09-25 20:38:53.784594-0700 0xdbf47 Error 0x0 0 0 kernel: (Sandbox) Sandbox: mdworker(15726) deny(1) mach-lookup com.apple.security.syspolicy Regex LogHeader = new Regex("^([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]).*?0x[0-9a-f]*[\\s]*([A-Za-z]*)[\\s]*0x[0-9a-f][\\s]*[0-9]*[\\s]*([0-9]*)[\\s]*(.*?):(.*)"); List <string> data = null; string previousLine = null; foreach (var line in file.Split('\n')) { if (LogHeader.IsMatch(line)) { if (previousLine != null) { var obj = new EventLogObject() { Data = (data.Count > 0) ? data : null, Event = previousLine, Level = LogHeader.Matches(previousLine).Single().Groups[2].Value, Summary = string.Format("{0}:{1}", LogHeader.Matches(previousLine).Single().Groups[4].Captures[0].Value, LogHeader.Matches(previousLine).Single().Groups[5].Captures[0].Value), Timestamp = LogHeader.Matches(previousLine).Single().Groups[1].Captures[0].Value, Source = LogHeader.Matches(previousLine).Single().Groups[4].Captures[0].Value }; DatabaseManager.Write(obj, runId); } previousLine = line; data = new List <string>(); } else { if (previousLine != null) { data.Add(line); } } } if (previousLine != null) { var obj = new EventLogObject() { Data = (data.Count > 0) ? data:null, Event = previousLine, Level = LogHeader.Matches(previousLine).Single().Groups[2].Value, Summary = string.Format("{0}:{1}", LogHeader.Matches(previousLine).Single().Groups[4].Captures[0].Value, LogHeader.Matches(previousLine).Single().Groups[5].Captures[0].Value), Timestamp = LogHeader.Matches(previousLine).Single().Groups[1].Captures[0].Value, Source = LogHeader.Matches(previousLine).Single().Groups[4].Captures[0].Value }; DatabaseManager.Write(obj, runId); } DatabaseManager.Commit(); }
/// <summary> /// Dumps from iptables. /// </summary> public void ExecuteLinux() { var result = ExternalCommandRunner.RunExternalCommand("iptables", "-S"); var lines = new List <string>(result.Split('\n')); Dictionary <string, FirewallAction> defaultPolicies = new Dictionary <string, FirewallAction>(); foreach (var line in lines) { if (line.StartsWith("-P")) { var chainName = line.Split(' ')[1]; defaultPolicies.Add(chainName, line.Contains("ACCEPT") ? FirewallAction.Allow : FirewallAction.Block); var obj = new FirewallObject() { Action = defaultPolicies[chainName], FriendlyName = $"Default {chainName} policy", Name = $"Default {chainName} policy", Scope = FirewallScope.All }; if (!chainName.Equals("FORWARD")) { obj.Direction = chainName.Equals("INPUT") ? FirewallDirection.Inbound : FirewallDirection.Outbound; } DatabaseManager.Write(obj, RunId); } else if (line.StartsWith("-A")) { var splits = line.Split(' '); var chainName = splits[1]; var obj = new FirewallObject() { Action = (splits[Array.IndexOf(splits, "-j") + 1] == "ACCEPT") ? FirewallAction.Allow : FirewallAction.Block, FriendlyName = line, Name = line, Scope = FirewallScope.All, Protocol = splits[Array.IndexOf(splits, "-p") + 1] }; if (Array.IndexOf(splits, "--dport") > 0) { obj.RemotePorts.Add(splits[Array.IndexOf(splits, "--dport") + 1]); } if (Array.IndexOf(splits, "-d") > 0) { obj.RemoteAddresses.Add(splits[Array.IndexOf(splits, "-d") + 1]); } if (Array.IndexOf(splits, "-s") > 0) { obj.LocalAddresses.Add(splits[Array.IndexOf(splits, "-s") + 1]); } if (Array.IndexOf(splits, "--sport") > 0) { obj.LocalPorts.Add(splits[Array.IndexOf(splits, "--sport") + 1]); } if (!chainName.Equals("FORWARD")) { obj.Direction = chainName.Equals("INPUT") ? FirewallDirection.Inbound : FirewallDirection.Outbound; } DatabaseManager.Write(obj, RunId); } } }
/// <summary> /// Executes the UserAccountCollector on Windows. Uses WMI to gather local users. /// </summary> public void ExecuteWindows() { try { List <string> lines = new List <string>(ExternalCommandRunner.RunExternalCommand("net", "localgroup").Split('\n')); lines.RemoveRange(0, 4); foreach (string line in lines) { if (line.Contains('*')) { var groupName = line.Substring(1).Trim(); GroupAccountObject group; //Get the group details if (!groups.ContainsKey(String.Format("{0}\\{1}", Environment.MachineName, groupName))) { SelectQuery query = new SelectQuery("SELECT * FROM Win32_Group where Name='" + groupName + "' AND Domain='" + Environment.MachineName + "'"); ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); ManagementObject groupManagementObject = default(ManagementObject); // TODO: Improve this foreach (ManagementObject gmo in searcher.Get()) { groupManagementObject = gmo; break; } group = new GroupAccountObject() { Name = groupName, Caption = Convert.ToString(groupManagementObject["Caption"]), Description = Convert.ToString(groupManagementObject["Description"]), InstallDate = Convert.ToString(groupManagementObject["InstallDate"]), Status = Convert.ToString(groupManagementObject["Status"]), LocalAccount = Convert.ToBoolean(groupManagementObject["LocalAccount"]), SID = Convert.ToString(groupManagementObject["SID"]), SIDType = Convert.ToInt32(groupManagementObject["SIDType"]), Domain = Convert.ToString(groupManagementObject["Domain"]), Users = new List <string>() }; } else { group = groups[String.Format("{0}\\{1}", Environment.MachineName, groupName)]; } //Get the members of the group var args = string.Format("/Node:\"{0}\" path win32_groupuser where (groupcomponent=\"win32_group.name=\\\"{1}\\\",domain=\\\"{2}\\\"\")", Environment.MachineName, groupName, Environment.MachineName); List <string> lines_int = new List <string>(ExternalCommandRunner.RunExternalCommand("wmic", args).Split('\n')); lines_int.RemoveRange(0, 1); foreach (string line_int in lines_int) { var userName = line_int.Trim(); if (userName.Equals("") || !userName.Contains("Domain")) { continue; } else { Regex r = new Regex(@".*Win32_UserAccount.Domain=""(.*?)"",Name=""(.*?)"""); var domain = r.Match(userName).Groups[1].Value.ToString(); userName = r.Match(userName).Groups[2].Value.ToString(); if (userName.Equals("")) { continue; } Log.Verbose("Found {0}\\{1} as member of {2}", domain, userName, groupName); if (!group.Users.Contains(String.Format("{0}\\{1}", domain, userName))) { group.Users.Add(String.Format("{0}\\{1}", domain, userName)); } var query = new SelectQuery("SELECT * FROM Win32_UserAccount where Domain='" + domain + "' and Name='" + userName + "'"); var searcher = new ManagementObjectSearcher(query); foreach (ManagementObject user in searcher.Get()) { if (users.ContainsKey(userName)) { if (!users[userName].Groups.Contains(String.Format("{0}\\{1}", domain, groupName))) { users[userName].Groups.Add(groupName); } if (groupName.Equals("Administrators")) { users[userName].Privileged = true; } } else { var obj = new UserAccountObject() { AccountType = Convert.ToString(user["AccountType"]), Caption = Convert.ToString(user["Caption"]), Description = Convert.ToString(user["Description"]), Disabled = Convert.ToString(user["Disabled"]), Domain = Convert.ToString(user["Domain"]), InstallDate = Convert.ToString(user["InstallDate"]), LocalAccount = Convert.ToString(user["LocalAccount"]), Lockout = Convert.ToString(user["Lockout"]), Name = Convert.ToString(user["Name"]), FullName = Convert.ToString(user["FullName"]), PasswordChangeable = Convert.ToString(user["PasswordChangeable"]), PasswordExpires = Convert.ToString(user["PasswordExpires"]), PasswordRequired = Convert.ToString(user["PasswordRequired"]), SID = Convert.ToString(user["SID"]), Privileged = (bool)groupName.Equals("Administrators"), Groups = new List <string>() { groupName } }; users.Add(userName, obj); } } } groups[String.Format("{0}\\{1}", Environment.MachineName, groupName)] = group; } } } } catch (Exception e) { Logger.DebugException(e); } foreach (var user in users) { DatabaseManager.Write(user.Value, runId); } foreach (var group in groups) { DatabaseManager.Write(group.Value, runId); } }
/// <summary> /// Executes the User account collector on Linux. Reads /etc/passwd and /etc/shadow. /// </summary> private void ExecuteLinux() { var etc_passwd_lines = File.ReadAllLines("/etc/passwd"); var etc_shadow_lines = File.ReadAllLines("/etc/shadow"); Dictionary <string, GroupAccountObject> Groups = new Dictionary <string, GroupAccountObject>(); var accountDetails = new Dictionary <string, UserAccountObject>(); foreach (var _line in etc_passwd_lines) { var parts = _line.Split(':'); if (!accountDetails.ContainsKey(parts[0])) { accountDetails[parts[0]] = new UserAccountObject() { Name = parts[0], UID = parts[2], GID = parts[3], FullName = parts[4], HomeDirectory = parts[5], Shell = parts[6] }; } } foreach (var _line in etc_shadow_lines) { var parts = _line.Split(':'); var username = parts[0]; if (!accountDetails.ContainsKey(username)) { accountDetails[username] = new UserAccountObject() { Name = username }; } var tempDict = accountDetails[username]; if (parts[1].Contains("$")) { tempDict.PasswordStorageAlgorithm = parts[1].Split('$')[1]; } tempDict.PasswordExpires = parts[4]; tempDict.Inactive = parts[6]; tempDict.Disabled = parts[7]; accountDetails[username] = tempDict; } var result = ExternalCommandRunner.RunExternalCommand("grep", "'^sudo:.*$' /etc/group | cut - d: -f4"); foreach (var _line in result.Split('\n')) { accountDetails[_line].Privileged = true; } foreach (var username in accountDetails.Keys) { // Admin user details var groupsRaw = ExternalCommandRunner.RunExternalCommand("groups", "username"); var groups = result.Split(' '); foreach (var group in groups) { accountDetails[username].Groups.Add(group); if (Groups.ContainsKey(group)) { Groups[group].Users.Add(username); } else { Groups[group] = new GroupAccountObject() { Name = group, Users = new List <string>() { username } }; } } DatabaseManager.Write(accountDetails[username], this.runId); } foreach (var group in Groups) { DatabaseManager.Write(group.Value, this.runId); } }
/// <summary> /// Uses systemctl (relies on systemd) and also checks /etc/init.d /// </summary> public void ExecuteLinux() { try { var result = ExternalCommandRunner.RunExternalCommand("systemctl", "list-units --type service"); //Split lines and remove header var lines = result.Split('\n').Skip(1); foreach (var _line in lines) { var _fields = _line.Split('\t'); if (_fields.Length == 5) { var obj = new ServiceObject() { DisplayName = _fields[4], Name = _fields[0], State = _fields[3], }; DatabaseManager.Write(obj, this.RunId); } } } catch (ExternalException) { Log.Error("Error executing {0}", "systemctl list-units --type service"); } try { var result = ExternalCommandRunner.RunExternalCommand("ls", "/etc/init.d/ -l"); var lines = result.Split('\n').Skip(1); String pattern = @".*\s(.*)"; foreach (var _line in lines) { Match match = Regex.Match(_line, pattern); GroupCollection groups = match.Groups; var serviceName = groups[1].ToString(); var obj = new ServiceObject() { DisplayName = serviceName, Name = serviceName, }; DatabaseManager.Write(obj, this.RunId); } } catch (ExternalException) { Log.Error("Error executing {0}", "ls /etc/init.d/ -l"); } // CentOS // chkconfig --list // BSD // service -l // this provides very minor amount of info }
/// <summary> /// Executes the User account collector on Linux. Reads /etc/passwd and /etc/shadow. /// </summary> private void ExecuteLinux() { var etc_passwd_lines = File.ReadAllLines("/etc/passwd"); var etc_shadow_lines = File.ReadAllLines("/etc/shadow"); Dictionary <string, GroupAccountObject> Groups = new Dictionary <string, GroupAccountObject>(); var accountDetails = new Dictionary <string, UserAccountObject>(); foreach (var _line in etc_passwd_lines) { var parts = _line.Split(':'); if (!accountDetails.ContainsKey(parts[0])) { accountDetails[parts[0]] = new UserAccountObject(parts[0]) { UID = parts[2], GID = parts[3], FullName = parts[4], HomeDirectory = parts[5], Shell = parts[6] }; } } foreach (var _line in etc_shadow_lines) { var parts = _line.Split(':'); var username = parts[0]; if (!accountDetails.ContainsKey(username)) { accountDetails[username] = new UserAccountObject(username) { }; } var tempDict = accountDetails[username]; if (parts[1].Contains("$")) { tempDict.PasswordStorageAlgorithm = parts[1].Split('$')[1]; } tempDict.PasswordExpires = parts[4]; tempDict.Inactive = parts[6]; tempDict.Disabled = parts[7]; accountDetails[username] = tempDict; } foreach (var username in accountDetails.Keys) { // Admin user details var groupsRaw = ExternalCommandRunner.RunExternalCommand("groups", username); var groups = groupsRaw.Split(' '); foreach (var group in groups) { if (group.Equals("sudo")) { accountDetails[username].Privileged = true; } accountDetails[username].Groups.Add(group); if (Groups.ContainsKey(group)) { Groups[group].Users.Add(username); } else { Groups[group] = new GroupAccountObject(group); Groups[group].Users.Add(username); } } Results.Add(accountDetails[username]); } foreach (var group in Groups) { Results.Add(group.Value); } }
/// <summary> /// Executes the OpenPortCollector on OS X. Calls out to the `lsof` /// command and parses the output, sending the output to the database. /// </summary> internal void ExecuteOsX(CancellationToken cancellationToken) { try { string result = ExternalCommandRunner.RunExternalCommand("lsof", "-Pn -i4 -i6"); foreach (var _line in result.Split('\n')) { if (cancellationToken.IsCancellationRequested) { return; } var line = _line.ToUpperInvariant(); if (!line.Contains("LISTEN")) { continue; // Skip any lines which aren't open listeners } var parts = Regex.Split(line, @"\s+"); if (parts.Length <= 9) { continue; // Not long enough } var addressMatches = Regex.Match(parts[8], @"^(.*):(\d+)$"); if (addressMatches.Success) { var address = addressMatches.Groups[1].ToString(); var port = addressMatches.Groups[2].ToString(); if (int.TryParse(port, out int portInt)) { var transport = parts[7].ToUpperInvariant().Equals("TCP") ? TRANSPORT.TCP : parts[7].ToUpperInvariant().Equals("TCP") ? TRANSPORT.UDP : TRANSPORT.UNKNOWN; var family = ADDRESS_FAMILY.Unknown; switch (parts[4]) { case "IPv4": family = ADDRESS_FAMILY.InterNetwork; break; case "IPv6": family = ADDRESS_FAMILY.InterNetworkV6; break; default: family = ADDRESS_FAMILY.Unknown; break; } var obj = new OpenPortObject(portInt, transport, family) { Address = address, ProcessName = parts[0] }; HandleChange(obj); } } } } catch (Exception e) { Log.Error(e, Strings.Get("Err_Lsof")); } }
/// <summary> /// Gathers user account details on OS X. Uses dscacheutil. /// </summary> private void ExecuteOsX() { Dictionary <string, GroupAccountObject> Groups = new Dictionary <string, GroupAccountObject>(); // Admin user details var result = ExternalCommandRunner.RunExternalCommand("dscacheutil", "-q group -a name admin"); var lines = result.Split('\n'); // The fourth line is a list of usernames // Formatted like: 'users: root gabe' var admins = (lines[3].Split(':')[1]).Split(' '); // details for all users result = ExternalCommandRunner.RunExternalCommand("dscacheutil", "-q user"); var accountDetails = new Dictionary <string, UserAccountObject>(); // We initialize a new object. We know by the formatting of // dscacheutil that we will never have a user without the name coming // first, but we don't know the name yet. var newUser = new UserAccountObject(""); foreach (var _line in result.Split('\n')) { var parts = _line.Split(':'); if (parts.Length < 2) { // There is a blank line separating each grouping of user data continue; } // There is one space of padding, which we strip off here var value = parts[1].Substring(1); // dscacheutil prints the user information on multiple lines switch (parts[0]) { case "name": accountDetails[value] = new UserAccountObject(value) { AccountType = (admins.Contains(value)) ? "administrator" : "standard", Privileged = (admins.Contains(value)) }; newUser = accountDetails[value]; break; case "password": break; case "uid": newUser.UID = value; break; case "gid": newUser.GID = value; break; case "dir": newUser.HomeDirectory = value; break; case "shell": newUser.Shell = value; break; case "gecos": newUser.FullName = value; break; default: break; } } foreach (var username in accountDetails.Keys) { // Admin user details string groupsRaw = string.Empty; groupsRaw = ExternalCommandRunner.RunExternalCommand("groups", username); var groups = groupsRaw.Split(' '); foreach (var group in groups) { accountDetails[username].Groups.Add(group); if (Groups.ContainsKey(group)) { Groups[group].Users.Add(username); } else { Groups[group] = new GroupAccountObject(group); Groups[group].Users.Add(username); } } accountDetails[username].Groups.AddRange(groups); Results.Add(accountDetails[username]); } foreach (var group in Groups) { Results.Add(group.Value); } }
public override void Execute() { if (!CanRunOnPlatform()) { return; } Start(); _ = DatabaseManager.Transaction; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { foreach (IFirewallRule rule in FirewallManager.Instance.Rules.ToArray()) { var obj = new FirewallObject() { Action = rule.Action, ApplicationName = rule.ApplicationName, Direction = rule.Direction, FriendlyName = rule.FriendlyName, IsEnable = rule.IsEnable, LocalAddresses = rule.LocalAddresses.ToList().ConvertAll(address => address.ToString()), LocalPorts = rule.LocalPorts.ToList().ConvertAll(port => port.ToString()), LocalPortType = rule.LocalPortType, Name = rule.Name, Profiles = rule.Profiles, Protocol = rule.Protocol.ProtocolNumber.ToString(), RemoteAddresses = rule.RemoteAddresses.ToList().ConvertAll(address => address.ToString()), RemotePorts = rule.RemotePorts.ToList().ConvertAll(port => port.ToString()), Scope = rule.Scope, ServiceName = rule.ServiceName }; DatabaseManager.Write(obj, runId); } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // Example output: "Firewall is enabled. (State = 1)" var result = ExternalCommandRunner.RunExternalCommand("/usr/libexec/ApplicationFirewall/socketfilterfw", "--getglobalstate"); var enabled = result.Contains("1"); var obj = new FirewallObject() { Action = FirewallAction.Block, Direction = FirewallDirection.Inbound, IsEnable = enabled, FriendlyName = "Firewall Enabled", Name = "Firewall Enabled", Scope = FirewallScope.All }; DatabaseManager.Write(obj, runId); // Example output: "Stealth mode disabled" result = ExternalCommandRunner.RunExternalCommand("/usr/libexec/ApplicationFirewall/socketfilterfw", "--getglobalstate"); obj = new FirewallObject() { Action = FirewallAction.Block, Direction = FirewallDirection.Inbound, IsEnable = result.Contains("enabled"), FriendlyName = "Stealth Mode", Name = "Stealth Mode", Scope = FirewallScope.All }; DatabaseManager.Write(obj, runId); /* Example Output: * Automatically allow signed built-in software ENABLED * Automatically allow downloaded signed software ENABLED */ result = ExternalCommandRunner.RunExternalCommand("/usr/libexec/ApplicationFirewall/socketfilterfw", "--getallowsigned"); obj = new FirewallObject() { Action = FirewallAction.Allow, Direction = FirewallDirection.Inbound, IsEnable = result.Split('\n')[0].Contains("ENABLED"), FriendlyName = "Allow signed built-in software", Name = "Allow signed built-in software", Scope = FirewallScope.All }; DatabaseManager.Write(obj, runId); obj = new FirewallObject() { Action = FirewallAction.Allow, Direction = FirewallDirection.Inbound, IsEnable = result.Split('\n')[1].Contains("ENABLED"), FriendlyName = "Allow downloaded signed software", Name = "Allow downloaded signed software", Scope = FirewallScope.All }; DatabaseManager.Write(obj, runId); /* Example Output: * ALF: total number of apps = 2 * * 1 : /Applications/AppName.app * ( Allow incoming connections ) * * 2 : /Applications/AppName2.app * ( Block incoming connections ) */ result = ExternalCommandRunner.RunExternalCommand("/usr/libexec/ApplicationFirewall/socketfilterfw", "--listapps"); string appName = ""; Regex startsWithNumber = new Regex("^[1-9]"); var lines = new List <string>(result.Split('\n')); if (lines.Count() > 0) { lines = lines.Skip(2).ToList(); foreach (var line in lines) { if (startsWithNumber.IsMatch(line)) { appName = line.Substring(line.IndexOf('/')); } else if (line.Contains("incoming connections")) { obj = new FirewallObject() { Action = (line.Contains("Allow"))?FirewallAction.Allow:FirewallAction.Block, Direction = FirewallDirection.Inbound, FriendlyName = appName, Name = appName, Scope = FirewallScope.All }; DatabaseManager.Write(obj, runId); } } } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { var result = ExternalCommandRunner.RunExternalCommand("iptables", "-S"); var lines = new List <string>(result.Split('\n')); Dictionary <string, FirewallAction> defaultPolicies = new Dictionary <string, FirewallAction>(); foreach (var line in lines) { if (line.StartsWith("-P")) { var chainName = line.Split(' ')[1]; defaultPolicies.Add(chainName, line.Contains("ACCEPT") ? FirewallAction.Allow : FirewallAction.Block); var obj = new FirewallObject() { Action = defaultPolicies[chainName], FriendlyName = string.Format("Default {0} policy", chainName), Name = string.Format("Default {0} policy", chainName), Scope = FirewallScope.All }; if (!chainName.Equals("FORWARD")) { obj.Direction = chainName.Equals("INPUT") ? FirewallDirection.Inbound : FirewallDirection.Outbound; } DatabaseManager.Write(obj, runId); } else if (line.StartsWith("-A")) { var splits = line.Split(' '); var chainName = splits[1]; var obj = new FirewallObject() { Action = (splits[Array.IndexOf(splits, "-j") + 1] == "ACCEPT") ? FirewallAction.Allow : FirewallAction.Block, FriendlyName = line, Name = line, Scope = FirewallScope.All, Protocol = splits[Array.IndexOf(splits, "-p") + 1] }; if (Array.IndexOf(splits, "--dport") > 0) { obj.RemotePorts = new List <string>() { splits[Array.IndexOf(splits, "--dport") + 1] }; } if (Array.IndexOf(splits, "-d") > 0) { obj.RemoteAddresses = new List <string>() { splits[Array.IndexOf(splits, "-d") + 1] }; } if (Array.IndexOf(splits, "-s") > 0) { obj.LocalAddresses = new List <string>() { splits[Array.IndexOf(splits, "-s") + 1] }; } if (Array.IndexOf(splits, "--sport") > 0) { obj.LocalPorts = new List <string>() { splits[Array.IndexOf(splits, "--sport") + 1] }; } if (!chainName.Equals("FORWARD")) { obj.Direction = chainName.Equals("INPUT") ? FirewallDirection.Inbound : FirewallDirection.Outbound; } DatabaseManager.Write(obj, runId); } } } DatabaseManager.Commit(); Stop(); }
/// <summary> /// Executes the UserAccountCollector on Windows. Uses WMI to gather local users. /// </summary> public void ExecuteWindows() { Dictionary <string, UserAccountObject> users = new Dictionary <string, UserAccountObject>(); Dictionary <string, GroupAccountObject> groups = new Dictionary <string, GroupAccountObject>(); GroupAccountObject?GetGroup(string groupName) { //Get the group details if (!groups.ContainsKey($"{Environment.MachineName}\\{groupName}")) { SelectQuery query = new SelectQuery("SELECT * FROM Win32_Group where Name='" + groupName + "' AND Domain='" + Environment.MachineName + "'"); using ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); // TODO: Improve this foreach (ManagementObject gmo in searcher.Get()) { return(new GroupAccountObject(groupName) { Caption = Convert.ToString(gmo["Caption"], CultureInfo.InvariantCulture), Description = Convert.ToString(gmo["Description"], CultureInfo.InvariantCulture), InstallDate = Convert.ToString(gmo["InstallDate"], CultureInfo.InvariantCulture), Status = Convert.ToString(gmo["Status"], CultureInfo.InvariantCulture), LocalAccount = Convert.ToBoolean(gmo["LocalAccount"], CultureInfo.InvariantCulture), SID = Convert.ToString(gmo["SID"], CultureInfo.InvariantCulture), SIDType = Convert.ToInt32(gmo["SIDType"], CultureInfo.InvariantCulture), Domain = Convert.ToString(gmo["Domain"], CultureInfo.InvariantCulture), }); } } else { return(groups[$"{Environment.MachineName}\\{groupName}"]); } return(null); } try { List <string> lines = new List <string>(ExternalCommandRunner.RunExternalCommand("net", "localgroup").Split('\n')); lines.RemoveRange(0, 4); foreach (string line in lines) { if (line.Contains('*')) { var groupName = line.Substring(1).Trim(); var group = GetGroup(groupName); if (group != null) { group.Users = new List <string>(); //Get the members of the group var args = $"/Node:\"{Environment.MachineName}\" path win32_groupuser where (groupcomponent=\"win32_group.name=\\\"{groupName}\\\",domain=\\\"{Environment.MachineName}\\\"\")"; List <string> lines_int = new List <string>(ExternalCommandRunner.RunExternalCommand("wmic", args).Split('\n')); lines_int.RemoveRange(0, 1); foreach (string line_int in lines_int) { var userName = line_int.Trim(); if (string.IsNullOrEmpty(userName) || !userName.Contains("Domain")) { continue; } else { Regex r = new Regex(@".*Win32_UserAccount.Domain=""(.*?)"",Name=""(.*?)"""); var domain = r.Match(userName).Groups[1].Value; userName = r.Match(userName).Groups[2].Value; if (string.IsNullOrEmpty(userName)) { continue; } Log.Verbose("Found {0}\\{1} as member of {2}", domain, userName, groupName); if (!group.Users.Contains($"{domain}\\{userName}")) { group.Users.Add($"{domain}\\{userName}"); } var query = new SelectQuery($"SELECT * FROM Win32_UserAccount where Domain='{domain}' and Name='{userName}'"); using var searcher = new ManagementObjectSearcher(query); foreach (ManagementObject user in searcher.Get()) { if (users.ContainsKey(userName)) { if (!users[userName].Groups.Contains($"{domain}\\{groupName}")) { users[userName].Groups.Add(groupName); } if (groupName.Equals("Administrators")) { users[userName].Privileged = true; } } else { var name = Convert.ToString(user["Name"], CultureInfo.InvariantCulture); if (name is string UserName) { var obj = new UserAccountObject(UserName) { AccountType = Convert.ToString(user["AccountType"], CultureInfo.InvariantCulture), Caption = Convert.ToString(user["Caption"], CultureInfo.InvariantCulture), Description = Convert.ToString(user["Description"], CultureInfo.InvariantCulture), Disabled = Convert.ToString(user["Disabled"], CultureInfo.InvariantCulture), Domain = Convert.ToString(user["Domain"], CultureInfo.InvariantCulture), InstallDate = Convert.ToString(user["InstallDate"], CultureInfo.InvariantCulture), LocalAccount = Convert.ToString(user["LocalAccount"], CultureInfo.InvariantCulture), Lockout = Convert.ToString(user["Lockout"], CultureInfo.InvariantCulture), FullName = Convert.ToString(user["FullName"], CultureInfo.InvariantCulture), PasswordChangeable = Convert.ToString(user["PasswordChangeable"], CultureInfo.InvariantCulture), PasswordExpires = Convert.ToString(user["PasswordExpires"], CultureInfo.InvariantCulture), PasswordRequired = Convert.ToString(user["PasswordRequired"], CultureInfo.InvariantCulture), SID = Convert.ToString(user["SID"], CultureInfo.InvariantCulture), Privileged = groupName.Equals("Administrators"), Hidden = IsHiddenWindowsUser(UserName) }; obj.Groups.Add(groupName); users.Add(userName, obj); } } } } groups[$"{Environment.MachineName}\\{groupName}"] = group; } } } } } catch (Exception e) when( e is TypeInitializationException || e is PlatformNotSupportedException) { Log.Warning(Strings.Get("CollectorNotSupportedOnPlatform"), GetType().ToString()); } catch (ExternalException) { Log.Error("Failed to run {0}", "net localgroup"); } foreach (var user in users) { Results.Add(user.Value); } foreach (var group in groups) { Results.Add(group.Value); } }
/// <summary> /// Uses systemctl (relies on systemd) and also checks /etc/init.d /// </summary> internal void ExecuteLinux(CancellationToken cancellationToken) { try { var result = ExternalCommandRunner.RunExternalCommand("systemctl", "list-units --type service"); //Split lines and remove header var lines = result.Split('\n').Skip(1); foreach (var _line in lines) { if (cancellationToken.IsCancellationRequested) { return; } var _fields = _line.Split('\t'); if (_fields.Length == 5) { var obj = new ServiceObject(_fields[0]) { DisplayName = _fields[4], State = _fields[3], }; HandleChange(obj); } } } catch (ExternalException) { Log.Error("Error executing {0}", "systemctl list-units --type service"); } try { var result = ExternalCommandRunner.RunExternalCommand("ls", "/etc/init.d/ -l"); var lines = result.Split('\n').Skip(1); String pattern = @".*\s(.*)"; foreach (var _line in lines) { Match match = Regex.Match(_line, pattern); GroupCollection groups = match.Groups; var serviceName = groups[1].ToString(); var obj = new ServiceObject(serviceName) { DisplayName = serviceName, }; HandleChange(obj); } } catch (ExternalException) { Log.Error("Error executing {0}", "ls /etc/init.d/ -l"); } // CentOS chkconfig --list // BSD service -l this provides very minor amount of info }
public override void Execute() { if (!CanRunOnPlatform()) { return; } Start(); if (gatherFromFiles) { var roots = new List <string>(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { foreach (var driveInfo in DriveInfo.GetDrives()) { if (driveInfo.IsReady && driveInfo.DriveType == DriveType.Fixed) { roots.Add(driveInfo.Name); } } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { roots.Add("/"); // @TODO Improve this } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { roots.Add("/"); // @TODO Improve this } foreach (var root in roots) { var fileInfoEnumerable = DirectoryWalker.WalkDirectory(root, "Certificate"); Parallel.ForEach(fileInfoEnumerable, (fileInfo => { try { if (fileInfo is DirectoryInfo) { return; } if (fileInfo.FullName.EndsWith(".cer", StringComparison.CurrentCulture)) { var certificate = X509Certificate.CreateFromCertFile(fileInfo.FullName); var obj = new CertificateObject() { StoreLocation = fileInfo.FullName, StoreName = "Disk", CertificateHashString = certificate.GetCertHashString(), Subject = certificate.Subject, Pkcs12 = certificate.Export(X509ContentType.Pkcs12).ToString() }; DatabaseManager.Write(obj, this.runId); } } catch (Exception e) { Log.Debug("Couldn't parse certificate file {0}", fileInfo.FullName); Log.Debug("{0} {1}-{2}", e.GetType().ToString(), e.Message, e.StackTrace); } })); } } // On Windows we can use the .NET API to iterate through all the stores. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { foreach (StoreLocation storeLocation in (StoreLocation[])Enum.GetValues(typeof(StoreLocation))) { foreach (StoreName storeName in (StoreName[])Enum.GetValues(typeof(StoreName))) { try { X509Store store = new X509Store(storeName, storeLocation); store.Open(OpenFlags.ReadOnly); foreach (X509Certificate2 certificate in store.Certificates) { var obj = new CertificateObject() { StoreLocation = storeLocation.ToString(), StoreName = storeName.ToString(), CertificateHashString = certificate.GetCertHashString(), Subject = certificate.Subject, Pkcs12 = certificate.HasPrivateKey ? "redacted" : certificate.Export(X509ContentType.Pkcs12).ToString() }; DatabaseManager.Write(obj, this.runId); } store.Close(); } catch (Exception e) { Logger.DebugException(e); Telemetry.TrackTrace(Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Error, e); } } } } // On linux we check the central trusted root store (a folder), which has symlinks to actual cert locations scattered across the db // We list all the certificates and then create a new X509Certificate2 object for each by filename. else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { try { var result = ExternalCommandRunner.RunExternalCommand("ls", new string[] { "/etc/ssl/certs", "-A" }); foreach (var _line in result.Split('\n')) { Log.Debug("{0}", _line); try { X509Certificate2 certificate = new X509Certificate2("/etc/ssl/certs/" + _line); var obj = new CertificateObject() { StoreLocation = StoreLocation.LocalMachine.ToString(), StoreName = StoreName.Root.ToString(), CertificateHashString = certificate.GetCertHashString(), Subject = certificate.Subject, Pkcs12 = certificate.HasPrivateKey ? "redacted" : certificate.Export(X509ContentType.Pkcs12).ToString() }; DatabaseManager.Write(obj, this.runId); } catch (Exception e) { Log.Debug("{0} {1} Issue creating certificate based on /etc/ssl/certs/{2}", e.GetType().ToString(), e.Message, _line); Log.Debug("{0}", e.StackTrace); } } } catch (Exception e) { Log.Error("Failed to dump certificates from 'ls /etc/ssl/certs -A'."); Logger.DebugException(e); } } // On macos we use the keychain and export the certificates as .pem. // However, on macos Certificate2 doesn't support loading from a pem, // so first we need pkcs12s instead, we convert using openssl, which requires we set a password // we import the pkcs12 with all our certs, delete the temp files and then iterate over it the certs else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { try { var result = ExternalCommandRunner.RunExternalCommand("security", new string[] { "find-certificate", "-ap", "/System/Library/Keychains/SystemRootCertificates.keychain" }); string tmpPath = Path.Combine(Directory.GetCurrentDirectory(), "tmpcert.pem"); string pkPath = Path.Combine(Directory.GetCurrentDirectory(), "tmpcert.pk12"); File.WriteAllText(tmpPath, result); _ = ExternalCommandRunner.RunExternalCommand("openssl", new string[] { "pkcs12", "-export", "-nokeys", "-out", pkPath, "-passout pass:pass", "-in", tmpPath }); X509Certificate2Collection xcert = new X509Certificate2Collection(); xcert.Import(pkPath, "pass", X509KeyStorageFlags.DefaultKeySet); File.Delete(tmpPath); File.Delete(pkPath); var X509Certificate2Enumerator = xcert.GetEnumerator(); while (X509Certificate2Enumerator.MoveNext()) { var obj = new CertificateObject() { StoreLocation = StoreLocation.LocalMachine.ToString(), StoreName = StoreName.Root.ToString(), CertificateHashString = X509Certificate2Enumerator.Current.GetCertHashString(), Subject = X509Certificate2Enumerator.Current.Subject, Pkcs12 = X509Certificate2Enumerator.Current.HasPrivateKey ? "redacted" : X509Certificate2Enumerator.Current.Export(X509ContentType.Pkcs12).ToString() }; DatabaseManager.Write(obj, this.runId); } } catch (Exception e) { Log.Error("Failed to dump certificates from 'security' or 'openssl'."); Logger.DebugException(e); } } DatabaseManager.Commit(); Stop(); }