示例#1
0
        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();
            }
        }
示例#2
0
        /// <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);
                }
            }
        }
示例#3
0
        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
                    }
                }
示例#6
0
        /// <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);
            }
        }
示例#7
0
 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'.");
            }
        }
示例#10
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
            {
                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();
            }
        }
示例#12
0
        /// <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);
                    }
                }
            }
        }
示例#13
0
        /// <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);
                    }
                }
            }
        }
示例#14
0
        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]);
            }
        }
示例#15
0
        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
        }
示例#24
0
        /// <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"));
            }
        }
示例#26
0
        /// <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);
            }
        }
示例#27
0
        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();
        }
示例#28
0
        /// <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();
        }