├── README.md └── SigFinder ├── App.config ├── Program.cs ├── Properties └── AssemblyInfo.cs ├── SigFinder.csproj └── SigFinder.sln /README.md: -------------------------------------------------------------------------------- 1 | # SigFinder 2 | 3 | Identify binaries with Authenticode digital signatures signed to an internal CA/domain. Useful for enumerating Windows directory paths referenced in WDAC policies or searching for internal LOB applications. 4 | 5 | ``` 6 | C:\Tools> SigFinder.exe 7 | Usage: SigFinder.exe [-ignore ,,...] [-recursive] [-domain ] 8 | ``` 9 |
10 | 11 | Optional flags: 12 | - `-ignore` - ignore all certificates containing supplied string/comma seperated strings 13 | - `-recursive` - recursively check for certificates from the provided directory path 14 | - `-domain` - only display certificates containing the the domain keyword 15 | 16 |
17 | 18 | ![sigfinder](https://github.com/mlcsec/SigFinder/assets/47215311/0cf078a1-ebeb-4757-a457-4fef4240267f) 19 | 20 |
21 | 22 | # NOTE 23 | 24 | Add quotes to directory paths containing spaces and either REMOVE the trailing backslash or ADD a backslash: 25 | ``` 26 | beacon> executeInline-Assembly --dotnetassembly C:\Tools\SigFinder.exe "C:\Program Files" -ignore microsoft 27 | beacon> executeInline-Assembly --dotnetassembly C:\Tools\SigFinder.exe "C:\Program Files\\" -ignore microsoft 28 | ``` 29 | Your beacon WILL DIE if you don't. 30 | -------------------------------------------------------------------------------- /SigFinder/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /SigFinder/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Security.Cryptography.X509Certificates; 5 | 6 | namespace SigFinder 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | if (args.Length == 0) 13 | { 14 | Console.WriteLine("Usage: SigFinder.exe [-ignore ,,...] [-recursive] [-domain ]"); 15 | return; 16 | } 17 | 18 | string directoryPath = args[0]; 19 | bool ignore = args.Contains("-ignore", StringComparer.OrdinalIgnoreCase); 20 | string[] ignoreStrings = args.SkipWhile(arg => !arg.StartsWith("-ignore")).Skip(1).FirstOrDefault()?.Split(','); 21 | 22 | bool recursive = args.Contains("-recursive", StringComparer.OrdinalIgnoreCase); 23 | string domainFilter = args.SkipWhile(arg => !arg.StartsWith("-domain")).Skip(1).FirstOrDefault(); 24 | 25 | try 26 | { 27 | CheckCertificates(directoryPath.Trim('"'), ignore, recursive, domainFilter?.Trim('"'), ignoreStrings?.Select(s => s.Trim('"')).ToArray()); 28 | } 29 | catch (UnauthorizedAccessException ex) 30 | { 31 | Console.WriteLine($"Error: {ex.Message}"); 32 | } 33 | } 34 | 35 | static void CheckCertificates(string directoryPath, bool ignore, bool recursive, string domainFilter, string[] ignoreStrings) 36 | { 37 | try 38 | { 39 | string[] excludedDirectories = { "Application Data" }; // update/remove 40 | 41 | ProcessDirectory(directoryPath, ignore, domainFilter, ignoreStrings, excludedDirectories, recursive); 42 | } 43 | catch (UnauthorizedAccessException ex) 44 | { 45 | //Console.WriteLine($"Error: {ex.Message}"); 46 | } 47 | catch (Exception ex) 48 | { 49 | Console.WriteLine($"Error: {ex.Message}"); 50 | } 51 | } 52 | 53 | static void ProcessDirectory(string rootDirectory, bool ignore, string domainFilter, string[] ignoreStrings, string[] excludedDirectories, bool recursive) 54 | { 55 | try 56 | { 57 | if (recursive) 58 | { 59 | ProcessDirectoryRecursive(rootDirectory, ignore, domainFilter, ignoreStrings, excludedDirectories); 60 | } 61 | else 62 | { 63 | ProcessDirectoryNonRecursive(rootDirectory, ignore, domainFilter, ignoreStrings, excludedDirectories, recursive); 64 | } 65 | } 66 | catch (UnauthorizedAccessException ex) 67 | { 68 | //Console.WriteLine($"Error: {ex.Message}"); 69 | } 70 | catch (Exception ex) 71 | { 72 | Console.WriteLine($"Error: {ex.Message}"); 73 | } 74 | } 75 | 76 | static void ProcessDirectoryNonRecursive(string currentDirectory, bool ignore, string domainFilter, string[] ignoreStrings, string[] excludedDirectories, bool recursive) 77 | { 78 | try 79 | { 80 | string directoryName = new DirectoryInfo(currentDirectory).Name; 81 | if (excludedDirectories.Contains(directoryName, StringComparer.OrdinalIgnoreCase)) 82 | { 83 | Console.WriteLine($"Skipped directory '{currentDirectory}' due to exclusion"); 84 | return; 85 | } 86 | 87 | // add/remove 88 | string[] executableFiles = Directory.GetFiles(currentDirectory, "*.exe", SearchOption.TopDirectoryOnly); 89 | string[] dllFiles = Directory.GetFiles(currentDirectory, "*.dll", SearchOption.TopDirectoryOnly); 90 | string[] allFiles = executableFiles.Concat(dllFiles).ToArray(); 91 | 92 | if (allFiles.Length == 0) 93 | { 94 | if (!recursive) 95 | { 96 | Console.WriteLine($"[!] No EXEs or DLLs in directory: {currentDirectory}"); 97 | } 98 | return; 99 | } 100 | 101 | ProcessFiles(allFiles, ignore, domainFilter, ignoreStrings); 102 | } 103 | catch (UnauthorizedAccessException ex) 104 | { 105 | //Console.WriteLine($"Error: {ex.Message}"); 106 | } 107 | catch (Exception ex) 108 | { 109 | Console.WriteLine($"Error: {ex.Message}"); 110 | } 111 | } 112 | 113 | static void ProcessDirectoryRecursive(string currentDirectory, bool ignore, string domainFilter, string[] ignoreStrings, string[] excludedDirectories) 114 | { 115 | ProcessDirectoryNonRecursive(currentDirectory, ignore, domainFilter, ignoreStrings, excludedDirectories, true); 116 | 117 | try 118 | { 119 | string[] subDirectories = Directory.GetDirectories(currentDirectory, "*", SearchOption.TopDirectoryOnly); 120 | foreach (string subDirectory in subDirectories) 121 | { 122 | ProcessDirectoryRecursive(subDirectory, ignore, domainFilter, ignoreStrings, excludedDirectories); 123 | } 124 | } 125 | catch (UnauthorizedAccessException ex) 126 | { 127 | //Console.WriteLine($"Error: {ex.Message}"); 128 | } 129 | catch (Exception ex) 130 | { 131 | Console.WriteLine($"Error: {ex.Message}"); 132 | } 133 | } 134 | 135 | static void ProcessFiles(string[] files, bool ignore, string domainFilter, string[] ignoreStrings) 136 | { 137 | foreach (string filePath in files) 138 | { 139 | try 140 | { 141 | Console.WriteLine($"\nChecking certificate for: {filePath}"); 142 | 143 | if (TryGetCertificate(filePath, out X509Certificate2 certificate)) 144 | { 145 | if (ignore && ContainsIgnoreString(certificate.Subject, certificate.Issuer, ignoreStrings)) 146 | { 147 | Console.WriteLine("[-] Certificate ignored. Contains ignore string"); 148 | continue; 149 | } 150 | 151 | if (!string.IsNullOrEmpty(domainFilter) && !ContainsDomain(certificate.Subject, domainFilter) && !ContainsDomain(certificate.Issuer, domainFilter)) 152 | { 153 | Console.WriteLine($"[-] Certificate ignored. Does not contain domain: {domainFilter}"); 154 | continue; 155 | } 156 | 157 | Console.WriteLine("[+] Certificate found!"); 158 | Console.WriteLine($"[*] Subject: {certificate.Subject}"); 159 | Console.WriteLine($"[*] Issuer: {certificate.Issuer}"); 160 | } 161 | else 162 | { 163 | Console.WriteLine("[-] No certificate found"); 164 | } 165 | } 166 | catch (UnauthorizedAccessException ex) 167 | { 168 | //Console.WriteLine($"Error: {ex.Message}"); 169 | } 170 | catch (Exception ex) 171 | { 172 | Console.WriteLine($"Error: {ex.Message}"); 173 | } 174 | } 175 | } 176 | 177 | static bool TryGetCertificate(string filePath, out X509Certificate2 certificate) 178 | { 179 | certificate = null; 180 | 181 | try 182 | { 183 | certificate = new X509Certificate2(filePath); 184 | return true; 185 | } 186 | catch (Exception ex) 187 | { 188 | //Console.WriteLine($"[!] Error checking certificate for {filePath}: {ex.Message}"); 189 | return false; 190 | } 191 | } 192 | 193 | static bool ContainsDomain(string text, string domain) 194 | { 195 | return text.IndexOf(domain, StringComparison.OrdinalIgnoreCase) >= 0; 196 | } 197 | 198 | static bool ContainsIgnoreString(string subject, string issuer, string[] ignoreStrings) 199 | { 200 | if (ignoreStrings == null || ignoreStrings.Length == 0) 201 | return false; 202 | 203 | return (subject != null && ignoreStrings.Any(ignore => subject.IndexOf(ignore, StringComparison.OrdinalIgnoreCase) >= 0)) 204 | || (issuer != null && ignoreStrings.Any(ignore => issuer.IndexOf(ignore, StringComparison.OrdinalIgnoreCase) >= 0)); 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /SigFinder/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SigFinder")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SigFinder")] 13 | [assembly: AssemblyCopyright("Copyright © 2024")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a6579fc6-c220-4078-941e-5671061fec68")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /SigFinder/SigFinder.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A6579FC6-C220-4078-941E-5671061FEC68} 8 | Exe 9 | SigFinder 10 | SigFinder 11 | v4.8 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /SigFinder/SigFinder.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33516.290 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SigFinder", "SigFinder.csproj", "{A6579FC6-C220-4078-941E-5671061FEC68}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A6579FC6-C220-4078-941E-5671061FEC68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A6579FC6-C220-4078-941E-5671061FEC68}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A6579FC6-C220-4078-941E-5671061FEC68}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A6579FC6-C220-4078-941E-5671061FEC68}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {777F074D-DF22-4869-A3C8-7B03493684A6} 24 | EndGlobalSection 25 | EndGlobal 26 | --------------------------------------------------------------------------------