├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── powershell-analysis.yml ├── .gitignore ├── .vscode └── settings.json ├── AaronLocker ├── Compare-Policies.ps1 ├── Create-Policies.ps1 ├── CustomizationInputs │ ├── GetExeFilesToDenyList.ps1 │ ├── GetSafePathsToAllow.ps1 │ ├── HashRuleData.ps1 │ ├── KnownAdmins.ps1 │ ├── TrustedSigners-MsvcMfc.ps1 │ ├── TrustedSigners.ps1 │ ├── UnsafePathsToBuildRulesFor.ps1 │ ├── WDACTrustedSigners-MsvcMfc.ps1 │ └── WDACTrustedSigners.ps1 ├── ExportPolicy-ToExcel.ps1 ├── GPOConfiguration │ ├── Clear-GPOAppLockerPolicy.ps1 │ └── Set-GPOAppLockerPolicy.ps1 ├── Generate-EventWorkbook.ps1 ├── Get-AppLockerEvents.ps1 ├── LocalConfiguration │ ├── ApplyPolicyToLocalGPO.ps1 │ ├── ClearApplockerLogs.ps1 │ ├── ClearLocalAppLockerPolicy.ps1 │ └── ConfigureForAppLocker.ps1 ├── MergeRules-Static │ ├── AppLockerRules-Microsoft OneDrive Hash Rules.xml │ ├── AppLockerRules-Microsoft OneDrive Publisher Rules.xml │ ├── AppLockerRules-Microsoft Teams Hash Rules.xml │ ├── AppLockerRules-Microsoft Teams Publisher Rules.xml │ ├── AppLockerRules-Teams Update Publisher Rules.xml │ ├── Deny-DeployAgent.xml │ ├── Deny-OldBginfo.xml │ ├── Deny-WindowsTemp.xml │ ├── OneDrive-InitialInstall-Win10v1607.xml │ ├── OneDrive-InitialInstall-Win10v1803.xml │ ├── OneDrive-InitialInstall-Win10v1809.xml │ └── OneDrive-SharePoint.xml ├── Save-WEFEvents.ps1 ├── Scan-Directories.ps1 ├── ScanResults │ ├── ExeDenyListData.txt │ ├── Writable_Full_PF.xml │ ├── Writable_Full_PF86.xml │ ├── Writable_Full_windir.xml │ ├── Writable_PF.txt │ ├── Writable_PF86.txt │ └── Writable_windir.txt └── Support │ ├── BuildRulesForFilesInWritableDirectories.ps1 │ ├── Config.ps1 │ ├── Create-Policies-AppLocker.ps1 │ ├── Create-Policies-WDAC.ps1 │ ├── DefaultRulesWithPlaceholders.xml │ ├── DownloadAccesschk.ps1 │ ├── Enum-WritableDirs.ps1 │ ├── ExportPolicy-ToCsv.ps1 │ ├── Get-AaronLockerTimestamp.ps1 │ ├── Set-OutputEncodingToUnicode.ps1 │ └── SupportFunctions.ps1 ├── LICENSE ├── MergeRules-Static ├── AppLockerRules-Cisco WebEx Installer Publisher Rules.xml ├── AppLockerRules-Cisco WebEx Publisher Rules.xml ├── AppLockerRules-GoToMeeting Publisher Rules.xml ├── AppLockerRules-Logitech Options Publisher Rules.xml ├── AppLockerRules-Zoom Publisher Rules.xml ├── README.md └── _AppLockerRules-Template Publisher Rules.xml ├── README.md ├── configs ├── README.md ├── Windows10 │ └── 22H2 │ │ ├── AppLockerRules-20231105-2334-Audit.xml │ │ └── AppLockerRules-20231105-2334-Enforce.xml └── Windows11 │ └── 22H2 │ ├── AppLockerRules-20231105-2333-Audit.xml │ ├── AppLockerRules-20231105-2333-Enforce.xml │ ├── AppLockerRules-20231105-AppX-Enforce.xml │ ├── AppLockerRules-20231105-Dll-Enforce.xml │ ├── AppLockerRules-20231105-Exe-Enforce.xml │ ├── AppLockerRules-20231105-Msi-Enforce.xml │ └── AppLockerRules-20231105-Script-Enforce.xml ├── lolbins.txt └── scripts ├── Get-Signatures.ps1 └── mdac.ps1.ps /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Standard to msysgit 5 | *.xlsx diff=astextplain 6 | *.XLSX diff=astextplain 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Set update schedule for GitHub Actions 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | # Check for updates to GitHub Actions every weekday 8 | interval: "daily" 9 | -------------------------------------------------------------------------------- /.github/workflows/powershell-analysis.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # 6 | # https://github.com/microsoft/action-psscriptanalyzer 7 | # For more information on PSScriptAnalyzer in general, see 8 | # https://github.com/PowerShell/PSScriptAnalyzer 9 | 10 | name: powershell-analysis 11 | 12 | on: 13 | push: 14 | branches: [ main ] 15 | paths: 16 | - '**.ps1' 17 | workflow_dispatch: 18 | 19 | jobs: 20 | build: 21 | name: PSScriptAnalyzer 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Run PSScriptAnalyzer 27 | uses: microsoft/psscriptanalyzer-action@6b2948b1944407914a58661c49941824d149734f 28 | with: 29 | # Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options. 30 | # The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules. 31 | path: ./ 32 | recurse: true 33 | # Include your own basic security rules. Removing this option will run all the rules 34 | #includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText"' 35 | output: results.sarif 36 | 37 | # Upload the SARIF file generated in the previous step 38 | - name: Upload SARIF results file 39 | uses: github/codeql-action/upload-sarif@v3 40 | with: 41 | sarif_file: results.sarif 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db 38 | ~*.* 39 | 40 | # Generated AaronLocker files 41 | # AaronLocker/Outputs/* 42 | # AaronLocker/MergeRules-Dynamic/* 43 | 44 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "accesschk", 4 | "Authenticode" 5 | ] 6 | } -------------------------------------------------------------------------------- /AaronLocker/Compare-Policies.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Compares two AppLocker policies. 4 | 5 | TODO: Add an option to get policies from AD GPO. 6 | 7 | .DESCRIPTION 8 | Reads two AppLocker policy XML files, canonicalizes and compares the rule information and reports results as tab-delimited CSV, or optionally to an Excel workbook formatted for sorting and filtering. 9 | Output columns are Compare, Rule, Reference, and Comparison. 10 | The "Compare" column is one of the following values: 11 | "==" if values are the same in both rule sets 12 | "<->" if values are present in both rule sets but different 13 | "<--" if the rule exists only in the reference rule set 14 | "-->" if the rule exists only in the comparison rule set 15 | The "Rule" column is either the name of a rule collection (Exe, Dll, Script, etc.) or information about a specific rule. 16 | The "Reference" column shows data from the ReferencePolicyXML parameter. 17 | The "Comparison" column shows data from the ComparisonPolicyXML parameter. 18 | 19 | Where the "Rule" column contains just the name of a rule collection, the Reference and Comparison columns indicate whether rules for that collection are "AuditOnly" or "Enabled" (enforced). 20 | 21 | Otherwise, the "Rule" column shows information about a specific rule, including: the file type (e.g., Dll, Exe); rule type (Publisher, Path, Hash); Allow or Deny; user/group SID; and rule-type-specific information. 22 | For Publisher rules, the rule-specific information catenates the publisher, product, and binary name. (Product or binary name might be empty.) 23 | For Path rules, the path is the rule-specific information. 24 | For Hash rules, the source file name is the rule-specific information. 25 | 26 | The Reference and Comparison columns show more detailed rule-type-specific information about the rule from the Reference and Comparison rule sets: 27 | For Publisher rules: the low and high version numbers that the rule applies to. If the Publisher rule includes exceptions, the raw XML is appended. 28 | For Path rules: exceptions to the rule, sorted. 29 | For Hash rules: the hash algorithm and value. 30 | 31 | When a rule set contains overlapping rules (e.g., two separate hashes allowed for the same file name), the detailed information is appended into the Reference or Comparison column. 32 | 33 | Note that when the -Excel switch is not used, line breaks within the CSV text fields are represented as "^|^". 34 | 35 | 36 | .PARAMETER ReferencePolicyXML 37 | Path to AppLocker policy XML file. 38 | Use "local" to inspect local policy. 39 | Use "effective" to inspect effective policy. 40 | 41 | .PARAMETER ComparisonPolicyXML 42 | Path to AppLocker policy XML file. 43 | Use "local" to inspect local policy. 44 | Use "effective" to inspect effective policy. 45 | 46 | .PARAMETER DifferencesOnly 47 | If this optional switch is specified, entries that are in both sets and are identical are not reported. 48 | 49 | .PARAMETER Excel 50 | If this optional switch is specified, outputs to a formatted Excel rather than tab-delimited CSV text to the pipeline. Note that when the -Excel switch is not used, line breaks within the CSV text fields are represented as "^|^". 51 | 52 | .PARAMETER GridView 53 | If this optional switch is specified, outputs to a PowerShell GridView (note that line breaks within the CSV text fields are represented as "^|^"). 54 | 55 | .EXAMPLE 56 | .\Compare-Policies.ps1 local effective -DifferencesOnly 57 | Compare local policy against effective policy and report only the differences. 58 | #> 59 | 60 | 61 | param( 62 | # path to XML file containing AppLocker policy 63 | [parameter(Mandatory = $true)] 64 | [String] 65 | $ReferencePolicyXML, 66 | 67 | # path to XML file containing AppLocker policy 68 | [parameter(Mandatory = $true)] 69 | [String] 70 | $ComparisonPolicyXML, 71 | 72 | # Don't report items that are the same in both sets. 73 | [switch] 74 | $DifferencesOnly, 75 | 76 | # Output to Excel 77 | [switch] 78 | $Excel, 79 | 80 | # Output to GridView 81 | [switch] 82 | $GridView 83 | ) 84 | 85 | $rootDir = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path) 86 | # Get configuration settings and global functions from .\Support\Config.ps1) 87 | # Dot-source the config file. 88 | . $rootDir\Support\Config.ps1 89 | 90 | $OutputEncodingPrevious = $OutputEncoding 91 | $OutputEncoding = [System.Text.ASCIIEncoding]::Unicode 92 | 93 | $keySep = " | " 94 | $linebreakSeq = "^|^" 95 | $tab = "`t" 96 | 97 | $refname = $compname = [string]::Empty 98 | 99 | # Get reference policy from local policy, effective policy, or a named file 100 | if ($ReferencePolicyXML.ToLower() -eq "local") { 101 | $ReferencePolicy = [xml](Get-AppLockerPolicy -Local -Xml) 102 | $refname = "Local Policy" 103 | } 104 | elseif ($ReferencePolicyXML.ToLower() -eq "effective") { 105 | $ReferencePolicy = [xml](Get-AppLockerPolicy -Effective -Xml) 106 | $refname = "Effective Policy" 107 | } 108 | else { 109 | $ReferencePolicy = [xml](Get-Content $ReferencePolicyXML) 110 | $refname = [System.IO.Path]::GetFileNameWithoutExtension($ReferencePolicyXML) 111 | } 112 | 113 | # Get comparison policy from local policy, effective policy, or a named file 114 | if ($ComparisonPolicyXML.ToLower() -eq "local") { 115 | $ComparisonPolicy = [xml](Get-AppLockerPolicy -Local -Xml) 116 | $compname = "Local Policy" 117 | } 118 | elseif ($ComparisonPolicyXML.ToLower() -eq "effective") { 119 | $ComparisonPolicy = [xml](Get-AppLockerPolicy -Effective -Xml) 120 | $compname = "Effective Policy" 121 | } 122 | else { 123 | $ComparisonPolicy = [xml](Get-Content $ComparisonPolicyXML) 124 | $compname = [System.IO.Path]::GetFileNameWithoutExtension($ComparisonPolicyXML) 125 | } 126 | 127 | 128 | # Create CSV headers 129 | [System.Collections.ArrayList]$csv = @() 130 | $csv.Add( "Compare" + $tab + "Rule" + $tab + "Reference ($refname)" + $tab + "Comparison ($compname)" + $tab + "Reference info" + $tab + "Comparison info" ) | Out-Null 131 | 132 | 133 | function GetNodeKeyAndValue( $fType, $oNode, [ref] $oKey, [ref] $oValue ) { 134 | $userOrGroup = $oNode.UserOrGroupSid 135 | $action = $oNode.Action 136 | $nameAndDescr = ($oNode.Name + $linebreakSeq + $oNode.Description).Replace("`r`n", $linebreakSeq).Replace("`n", $linebreakSeq) 137 | $oValue.Value = @{ ruleDetail = ""; ruleDoco = $nameAndDescr; } 138 | switch ( $oNode.LocalName ) { 139 | 140 | "FilePublisherRule" { 141 | $ruletype = "Publisher" 142 | $condition = $oNode.Conditions.FilePublisherCondition 143 | $ruleInfo = $condition.PublisherName + $keySep + $condition.ProductName + $keySep + $condition.BinaryName 144 | $oKey.Value = $fType + $keySep + $ruletype + $keySep + $action + $keySep + $userOrGroup + $keySep + $ruleInfo 145 | $oValue.Value.ruleDetail = "Ver " + $condition.BinaryVersionRange.LowSection + " to " + $condition.BinaryVersionRange.HighSection 146 | if ($oNode.Exceptions.InnerXml.Length -gt 0 ) { 147 | $oValue.Value.ruleDetail += ("; Exceptions: " + $oNode.Exceptions.InnerXml) 148 | } 149 | } 150 | 151 | "FilePathRule" { 152 | $ruletype = "Path" 153 | $ruleInfo = $oNode.Conditions.FilePathCondition.Path 154 | # Exceptions in canonical order. 155 | $exceptions = 156 | ( 157 | ( 158 | @($oNode.Exceptions.FilePathCondition.Path) + 159 | @($oNode.Exceptions.FilePublisherCondition.BinaryName) + 160 | @($oNode.Exceptions.FileHashCondition.FileHash.SourceFileName) 161 | ) | Sort-Object 162 | ) -join $linebreakSeq 163 | $oKey.Value = $fType + $keySep + $ruletype + $keySep + $action + $keySep + $userOrGroup + $keySep + $ruleInfo 164 | $oValue.Value.ruleDetail = $exceptions 165 | } 166 | 167 | "FileHashRule" { 168 | $ruletype = "Hash" 169 | $condition = $oNode.Conditions.FileHashCondition.FileHash 170 | $ruleInfo = $condition.SourceFileName # + "; length = " + $condition.SourceFileLength 171 | # $exceptions = "" # hash rules don't have exceptions 172 | $oKey.Value = $fType + $keySep + $ruletype + $keySep + $action + $keySep + $userOrGroup + $keySep + $ruleInfo 173 | $oValue.Value.ruleDetail = $condition.Type + " " + $condition.Data 174 | } 175 | 176 | default { Write-Warning ("Unexpected/invalid rule type: " + $_.LocalName) } 177 | } 178 | } 179 | 180 | 181 | <# 182 | $collections is a hashtable containing information about the rule collections (Exe, Dll, etc.), and whether each is "Audit" or "Enforce". 183 | Key = filetype 184 | Value = Two-element array, where the first element is the reference rule set's enforcement type and the second element is the comparison rule set's enforcement type. 185 | 186 | $rules is a hashtable containing information about AppLocker rules. 187 | Key = information about rule, combining file type (Exe, Dll, Script, etc.), rule type (Publisher, Path, or Hash), and rule-specific information (see GetNodeKeyAndValue). 188 | Value = Two-element array, where first element is rule information from the reference rule set, and the second element is from the comparison rule set. 189 | #> 190 | $collections = @{} 191 | $rules = @{} 192 | 193 | <# 194 | For both collections, key is a string, value is a two-element array, where element 0 is the reference data and element 1 is the comparison data 195 | When adding a new item, create a new two-element array to set as the value, with either 0 or 1 containing data. 196 | #> 197 | 198 | 199 | $ReferencePolicy.AppLockerPolicy.RuleCollection | ForEach-Object { 200 | $filetype = $_.Type 201 | $enforce = $_.EnforcementMode 202 | 203 | <# 204 | $collections is being newly populated here; value is two-element array in which the reference policy provides data for the first element, and 205 | the second element is initially empty 206 | #> 207 | $collVal = @{ ruleDetail = $enforce; }, $null 208 | $collections.Add($filetype, $collVal) 209 | 210 | if ($_.ChildNodes.Count -eq 0) { 211 | } 212 | else { 213 | $_.ChildNodes | ForEach-Object { 214 | 215 | $childNode = $_ 216 | $oKey = [ref]"" 217 | $oValue = [ref]"" 218 | GetNodeKeyAndValue $filetype $childNode $oKey $oValue 219 | 220 | # If the reference set already contains this key, see whether the value is a duplicate or a differing value 221 | # If duplicate, ignore it. If it's different, append it to the existing value 222 | if ($rules.ContainsKey($oKey.Value)) { 223 | $existingVal = $rules[$oKey.Value][0] 224 | if ($existingVal.ruleDetail -ne $oValue.Value.ruleDetail) { 225 | $rules[$oKey.Value][0].ruleDetail += ($linebreakSeq + $oValue.Value.ruleDetail) 226 | } 227 | if ($existingVal.ruleDoco -ne $oValue.Value.ruleDoco) { 228 | $rules[$oKey.Value][0].ruleDoco += ($linebreakSeq + $oValue.Value.ruleDoco) 229 | } 230 | } 231 | else { 232 | $ruleVal = $oValue.Value, $null 233 | $rules.Add($oKey.Value, $ruleVal) 234 | } 235 | } 236 | } 237 | } 238 | 239 | $ComparisonPolicy.AppLockerPolicy.RuleCollection | ForEach-Object { 240 | $filetype = $_.Type 241 | $enforce = $_.EnforcementMode 242 | 243 | # If $collections already has this file type, add to the existing value array; otherwise create a new entry with a new two-element array, populating the second element of that array 244 | if ($collections.ContainsKey($filetype)) { 245 | $collections[$filetype][1] = @{ ruleDetail = $enforce; } 246 | } 247 | else { 248 | $collVal = $null, @{ ruleDetail = $enforce } 249 | $collections.Add($filetype, $collVal) 250 | } 251 | 252 | # Then do child nodes... 253 | if ($_.ChildNodes.Count -eq 0) { 254 | } 255 | else { 256 | $_.ChildNodes | ForEach-Object { 257 | 258 | $childNode = $_ 259 | $oKey = [ref]"" 260 | $oValue = [ref]"" 261 | GetNodeKeyAndValue $filetype $childNode $oKey $oValue 262 | 263 | if ($rules.ContainsKey($oKey.Value)) { 264 | # If there's already data in the second element, see whether it's a duplicate. If it's a duplicate, ignore; if it's a differing value, append it to the existing value 265 | $existingVal = $rules[$oKey.Value][1] 266 | if ($Null -eq $existingVal) { 267 | $rules[$oKey.Value][1] = $oValue.Value 268 | } 269 | else { 270 | if ($existingVal.ruleDetail -ne $oValue.Value.ruleDetail) { 271 | $rules[$oKey.Value][1].ruleDetail += ($linebreakSeq + $oValue.Value.ruleDetail) 272 | } 273 | if ($existingVal.ruleDoco -ne $oValue.Value.ruleDoco) { 274 | $rules[$oKey.Value][1].ruleDoco += ($linebreakSeq + $oValue.Value.ruleDoco) 275 | } 276 | } 277 | } 278 | else { 279 | $ruleVal = $null, $oValue.Value 280 | $rules.Add($oKey.Value, $ruleVal) 281 | } 282 | } 283 | } 284 | 285 | } 286 | 287 | 288 | function ShowKeyValCompare($key, $val) { 289 | # Assume that if the key is present, then one or both of val0 and val1 is present 290 | if ($null -eq $val[0]) { 291 | "-->" + $tab + $key + $tab + "" + $tab + $val[1].ruleDetail + $tab + "" + $tab + $val[1].ruleDoco 292 | } 293 | elseif ($null -eq $val[1]) { 294 | "<--" + $tab + $key + $tab + $val[0].ruleDetail + $tab + "" + $tab + $val[0].ruleDoco + $tab 295 | } 296 | else { 297 | # Canonicalize/sort before performing comparison so that the same items in a different order doesn't report as a difference 298 | # TODO: re-sort ruleDoco so that its items still correspond to the sorted ruleDetail - not just a simple alpha sort though. 299 | $val0RuleDetail = ($val[0].ruleDetail.Replace($linebreakSeq, "`n").Split("`n") | Sort-Object) -join $linebreakSeq 300 | $val1RuleDetail = ($val[1].ruleDetail.Replace($linebreakSeq, "`n").Split("`n") | Sort-Object) -join $linebreakSeq 301 | if ($val0RuleDetail -eq $val1RuleDetail) { 302 | if (!$DifferencesOnly) { 303 | "==" + $tab + $key + $tab + $val0RuleDetail + $tab + $val1RuleDetail + $tab + $val[0].ruleDoco + $tab + $val[1].ruleDoco 304 | } 305 | } 306 | else { 307 | "<->" + $tab + $key + $tab + $val0RuleDetail + $tab + $val1RuleDetail + $tab + $val[0].ruleDoco + $tab + $val[1].ruleDoco 308 | } 309 | } 310 | } 311 | 312 | 313 | # Output everything in order 314 | 315 | $csv.AddRange( @( 316 | $collections.Keys | Sort-Object | ForEach-Object { 317 | ShowKeyValCompare $_ $collections[$_] 318 | } 319 | ) 320 | ) 321 | $csv.AddRange( @( 322 | $rules.Keys | Sort-Object | ForEach-Object { 323 | ShowKeyValCompare $_ $rules[$_] 324 | } 325 | ) 326 | ) 327 | 328 | if ($Excel) { 329 | if (CreateExcelApplication) { 330 | AddWorksheetFromCsvData -csv $csv -tabname "$refname vs $compname" -CrLfEncoded $linebreakSeq 331 | ReleaseExcelApplication 332 | } 333 | } 334 | elseif ($GridView) { 335 | $csv | ConvertFrom-Csv -Delimiter "`t" | Out-GridView -Title $MyInvocation.MyCommand.Name 336 | } 337 | else { 338 | # Just output the CSV raw 339 | $csv 340 | } 341 | 342 | $OutputEncoding = $OutputEncodingPrevious 343 | 344 | -------------------------------------------------------------------------------- /AaronLocker/Create-Policies.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Builds comprehensive and robust application control "audit" and "enforce" rules for AppLocker and/or Windows Defender Application Control (WDAC) to mitigate against users running unauthorized software, \ 4 | customizable through simple text files. Writes results to the Outputs subdirectory. 5 | 6 | TODO: Find and remove redundant rules. Report stripped rules to a separate log file. 7 | 8 | .DESCRIPTION 9 | Create-Policies.ps1 initializes the environment and creates shared input files used to generate comprehensive "audit" and "enforce" rules for AppLocker and/or WDAC. 10 | The resulting policies restrict non-admin code execution to "authorized" software, in a way to minimize the need to update the rules. 11 | Broadly speaking, "authorized" means that an administrator put it on the computer, OR created a rule specifically for that item. 12 | * AppLocker supported operating systems include Windows 7 and newer, and Windows Server 2008 R2 and newer. 13 | * WDAC supported operating systems include Windows 10, version 1903 and newer. 14 | * Rules cover EXE, DLL, Script, and MSI; on Windows 8.1 and newer, rules also cover Packaged apps. 15 | * Allows non-admin execution from the Windows and ProgramFiles directories, EXCEPT: 16 | * Identifies user-writable subdirectories and disallows execution from those directories; 17 | * Disallows execution of programs that run arbitrary code that can be used to bypass application control (e.g., mshta.exe); 18 | * Disallows execution of programs that non-admins rarely need but that malware/ransomware authors are known to use (e.g., cipher.exe); 19 | * Allows execution from identified "safe" paths (non-admins cannot write to them); 20 | * Allows execution of specifically authorized code in user-writable ("unsafe") directories. 21 | * For WDAC, optionally allows execution of code that was placed on disk by an authorized "managed installer" such as SCCM; Managed installers currently must be configured separately 22 | * For WDAC, optionally allows execution of code that is determined to have good reputation as determined by Microsoft's Intelligent Security Graph 23 | 24 | Rule implementation: 25 | Rule types cover path rules, publisher/signature rules, and hash rules. 26 | Rules allowing execution from "safe" locations are implemented using path rules. 27 | For AppLocker, user-writable subdirectories of the Windows and ProgramFiles directories are identified using Sysinternals AccessChk.exe. Exceptions for those subdirectories are implemented within path rules. 28 | WDAC user-writable checks are performed at runtime and skips the scan using Sysinternals AccessChk.exe unless KnownAdmins.ps1 finds custom admins defined 29 | Exceptions for "dangerous" programs (e.g., mshta.exe, cipher.exe) are generally implemented with publisher/signature rules. 30 | Rules allowing execution of EXE, DLL, and script files from user-writable directories are implemented with publisher/signature rules when possible, and hash rules otherwise, with options for the granularity of Publisher/signature rules. 31 | Publisher/signature rules can also be created allowing execution of anything signed by a particular publisher, or a specific product by a particular publisher. 32 | 33 | Scanning for user-writable subdirectories of the Windows and ProgramFiles directories can be time-consuming and is only required for AppLocker or when custom admins are defined. 34 | The script writes results to text files in an intermediate subdirectory. The script runs the scan if those files are not found OR if the -Rescan switch is specified. 35 | It is STRONGLY recommended that the scanning be performed with administrative rights. 36 | Once scans have been performed, scanned output can be copied to another machine and rules can be maintained without needing to rescan. 37 | 38 | Dependencies: 39 | PowerShell v5.1 or higher (Windows Management Framework 5.1 or higher) 40 | Current (or recent) version of Sysinternals AccessChk.exe, either in the Path or in the same directory as this script. 41 | Scripts and support files included in this solution (some are in specific subdirectories). 42 | 43 | See external documentation for more information. 44 | 45 | .LINK 46 | Sysinternals AccessChk is available here: 47 | https://technet.microsoft.com/sysinternals/accesschk 48 | https://download.sysinternals.com/files/AccessChk.zip 49 | https://live.sysinternals.com/accesschk.exe 50 | or run .\Support\DownloadAccesschk.ps1, which downloads AccessChk.exe to the main AaronLocker directory. 51 | 52 | .PARAMETER Rescan 53 | If this switch is set, this script scans the Windows and ProgramFiles directories for user-writable subdirectories, and captures data about EXE files to DenyList. 54 | If the results from a previous scan are found in the expected location and this switch is not specified, the script does not perform those scans. If those results are not found, the script performs the scan even if this switch is not set. 55 | It is STRONGLY recommended that the scanning be performed with administrative rights. 56 | 57 | .PARAMETER ForUser 58 | If scanning a system with an administrative account with a need to inspect another user's profile for "unsafe paths," specify that username with this optional parameter. E.g., if logged on and scanning with administrative account "abby-adm" but need to inspect $env:USERPROFILE belonging to "toby", use -ForUser toby. 59 | 60 | .PARAMETER Excel 61 | If specified, also creates Excel spreadsheets representing the generated rules. 62 | 63 | .PARAMETER AppLockerOrWDAC 64 | Specifies whether to generate policy for WDAC, AppLocker, or Both (default). 65 | 66 | .PARAMETER WDACTrustManagedInstallers 67 | Specifies whether to trust executables from managed installers(s) (default=true). 68 | 69 | .PARAMETER WDACTrustISG 70 | Specifies whether to trust executables deemed reputable by Microsoft's Intelligent Security Graph (ISG) (default=false). 71 | #> 72 | 73 | 74 | #################################################################################################### 75 | # Parameters 76 | #################################################################################################### 77 | 78 | param( 79 | # If set, forces rescans for user-writable directories under Windows and ProgramFiles 80 | [Parameter()] 81 | [System.Management.Automation.SwitchParameter] $Rescan = $false, 82 | 83 | # If set, replaces current user name with another in "unsafe paths" 84 | [Parameter(Mandatory = $false)] 85 | [System.String] $ForUser, 86 | 87 | # If specified, also creates Excel spreadsheets representing the generated rules. 88 | [Parameter()] 89 | [System.Management.Automation.SwitchParameter] $Excel, 90 | 91 | # Specifies whether to create policies for WDAC only, AppLocker only, or Both (default) 92 | [ValidateSet("Both", "AppLocker", "WDAC")] 93 | [System.String] $AppLockerOrWDAC = "AppLocker", 94 | 95 | # If set, enables managed installer(s) for WDAC 96 | [Parameter()] 97 | [System.Management.Automation.SwitchParameter] $WDACTrustManagedInstallers, 98 | 99 | # If set, enables the option to trust reputable apps based on Microsoft's ISG 100 | [Parameter()] 101 | [System.Management.Automation.SwitchParameter] $WDACTrustISG 102 | ) 103 | 104 | #################################################################################################### 105 | # Initialize 106 | #################################################################################################### 107 | 108 | # -------------------------------------------------------------------------------- 109 | # Only supported PowerShell version at this time: 5.1 110 | # PS Core v6.x doesn't include AppLocker cmdlets; string .Split() has new overloads that need to be dealt with. 111 | # (At some point, may also need to check $PSVersionTable.PSEdition) 112 | $psv = $PSVersionTable.PSVersion 113 | if ($psv.Major -ne 5 -or $psv.Minor -ne 1) { 114 | $errMsg = "This script requires PowerShell v5.1.`nCurrent version = " + $PSVersionTable.PSVersion.ToString() 115 | Write-Error $errMsg 116 | return 117 | } 118 | 119 | # Make sure this script is running in FullLanguage mode 120 | if ($ExecutionContext.SessionState.LanguageMode -ne [System.Management.Automation.PSLanguageMode]::FullLanguage) { 121 | $errMsg = "This script must run in FullLanguage mode, but is running in " + $ExecutionContext.SessionState.LanguageMode.ToString() 122 | Write-Error $errMsg 123 | return 124 | } 125 | 126 | # -------------------------------------------------------------------------------- 127 | # If WDAC or Both, make sure the OS is Windows 10 version 1903 (build 18362) or greater 128 | $OSBuild = [System.Environment]::OSVersion.Version.Build 129 | if ( ($AppLockerOrWDAC -eq "WDAC") -and ($OSBuild -lt 18362) ) { 130 | $errMsg = ("AaronLocker supports WDAC on Windows 10 version 1903 (build 18362) and greater. Current build is " + $OSBuild + ".") 131 | Write-Error $errMsg 132 | return 133 | } 134 | elseif ( ($AppLockerOrWDAC -eq "Both") -and ($OSBuild -lt 18362) ) { 135 | Write-Information -InformationAction "Continue" -MessageData ("AaronLocker supports WDAC on Windows 10 version 1903 (build 18362) and greater. Current build is " + $OSBuild + ". Processing AppLocker only.") 136 | $AppLockerOrWDAC = "AppLocker" 137 | } 138 | # -------------------------------------------------------------------------------- 139 | 140 | $rootDir = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path) 141 | 142 | # Get configuration settings and global functions from .\Support\Config.ps1) 143 | # Dot-source the config file. 144 | . $rootDir\Support\Config.ps1 145 | 146 | # Create subdirectories if they don't exist (some have to exist because files are expected to be there). 147 | if (!(Test-Path($customizationInputsDir))) { mkdir $customizationInputsDir | Out-Null } 148 | if (!(Test-Path($mergeRulesDynamicDir))) { mkdir $mergeRulesDynamicDir | Out-Null } 149 | if (!(Test-Path($mergeRulesStaticDir))) { mkdir $mergeRulesStaticDir | Out-Null } 150 | if (!(Test-Path($outputsDir))) { mkdir $outputsDir | Out-Null } 151 | if (!(Test-Path($supportDir))) { mkdir $supportDir | Out-Null } 152 | if (!(Test-Path($scanResultsDir))) { mkdir $scanResultsDir | Out-Null } 153 | 154 | # Look for results from previous scan for user-writable directories under the Windows and ProgramFiles directories. 155 | # If any of the files containing the filtered results are missing, force a rescan. 156 | if ( ! ( (Test-Path($windirTxt)) -and (Test-Path($PfTxt)) -and (Test-Path($Pf86Txt)) ) ) { 157 | $Rescan = $true 158 | } 159 | 160 | # AppLocker policy creation uses Sysinternals AccessChk.exe which must be in the Path or in the script directory. If it isn't, 161 | # this script writes an error message and quits. 162 | if ($Rescan -and ($AppLockerOrWDAC -in ("AppLocker", "Both"))) { 163 | # If accesschk.exe is in the rootdir, temporarily add the rootdir to the path. 164 | # (Previous implementation invoked Get-Command to see whether accesschk.exe was in the path, and only if that failed looked for 165 | # accesschk.exe in the rootdir. However, there was no good way to keep Get-Command from displaying a "Suggestion" message in that 166 | # scenario.) 167 | # Variable for restoring original Path, if necessary. 168 | $origPath = "" 169 | # Check for accesschk.exe in the rootdir. 170 | if (Test-Path -Path $rootDir\AccessChk.exe) { 171 | # Found it in this script's directory. Temporarily prepend it to the path. 172 | $origPath = $env:Path 173 | $env:Path = "$rootDir;" + $origPath 174 | } 175 | # Otherwise, if AccessChk.exe not available in the path, write an error message and quit. 176 | elseif ($null -eq (Get-Command AccessChk.exe -ErrorAction SilentlyContinue)) { 177 | $errMsg = "Scanning for writable subdirectories requires that Sysinternals AccessChk.exe be in the Path or in the same directory with this script.`n" + 178 | "AccessChk.exe was not found.`n" + 179 | "(See .\Support\DownloadAccesschk.ps1 for help.)`n" + 180 | "Exiting..." 181 | Write-Error $errMsg 182 | return 183 | } 184 | } 185 | 186 | # Get custom admins, if any defined 187 | [System.Collections.ArrayList]$knownAdmins = @() 188 | $knownAdmins.AddRange( @(& $ps1_KnownAdmins) ) 189 | 190 | <# 191 | # TODO (one day When WDAC adds exception support, we can allow AppLocker-style rules including exceptions) 192 | # Note that WDAC, by-default, enforces a run-time check that the current directory does not grant write permissions to non-standard admin users. 193 | # However, the runtime check by WDAC is not a security feature in Windows and won't prevent a malicious user from altering the ACLs to make a previously 194 | # user-writable path pass the admin-only check after the fact. 195 | If one or more custom admins was found, override WDAC's runtime admin-writable-only check and instead revert to AppLocker parity 196 | if ($knownAdmins.Count -gt 0) {$ProcessWDACLikeAppLocker = $true} 197 | else {$ProcessWDACLikeAppLocker = $false} 198 | #> 199 | 200 | # If just processing WDAC and no custom admins are defined, 201 | if (($Rescan) -and ($AppLockerOrWDAC -eq "WDAC") -and !($ProcessWDACLikeAppLocker)) { 202 | Write-Verbose -Message "Skipping scan for user-writable directories - not required for WDAC." 203 | $Rescan = $false 204 | } 205 | 206 | #################################################################################################### 207 | # Process common custom inputs once before calling AppLocker- and WDAC-specific scripts 208 | #################################################################################################### 209 | # Get Block List -- WDAC could potentially use recommended blocks policy instead? If so, move this back to AppLocker-specific script 210 | if ( $Rescan -or ( ($AppLockerOrWDAC -in "Both", "AppLocker") -and !(Test-Path($ExeDenyListData) ) ) -or ( ($AppLockerOrWDAC -in "Both", "WDAC") ) ) { 211 | Write-Verbose -Message "Get EXE files to DenyList for later processing..." 212 | # Get the EXE files to DenyList from the script that produces that list. 213 | $exeFilesToDenyList = (& $ps1_GetExeFilesToDenyList) 214 | } 215 | 216 | # Get additional authorized safe paths from the script that produces that list 217 | Write-Verbose -Message "Get authorized safe paths for later processing..." 218 | $PathsToAllow = (& $ps1_GetSafePathsToAllow) 219 | 220 | # Run the script that gets "unsafe" user-writable paths for later processing. Should come in as a sequence of hashtables. 221 | if ( !(Test-Path($ps1_UnsafePathsToBuildRulesFor)) ) { 222 | $errmsg = "Script file not found: $ps1_UnsafePathsToBuildRulesFor`nNo new rules generated for files in writable directories." 223 | Write-Warning $errmsg 224 | } 225 | else { 226 | Write-Verbose -Message "Get 'unsafe' user-writable paths for later processing..." 227 | $UnsafePathsToBuildRulesFor = (& $ps1_UnsafePathsToBuildRulesFor) 228 | } 229 | 230 | # Run the script that produces the hash information to build additional allow rules. Should come in as a sequence of hashtables. 231 | # Each hashtable must have the following properties: 232 | # * RuleName 233 | # * HashVal (must be SHA256 with "0x" and 64 hex digits) 234 | # * FileName 235 | # The following AppLocker-specific hashtable properties are ignored for WDAC rules 236 | # * RuleCollection (case-sensitive) 237 | # * RuleDesc 238 | $hashRuleData = (& $ps1_HashRuleData) 239 | 240 | 241 | #################################################################################################### 242 | # Shared setup complete. Call AppLocker- and WDAC-specific scripts. 243 | #################################################################################################### 244 | if ($AppLockerOrWDAC -in "Both", "AppLocker") { & $ps1_CreatePoliciesAppLocker } 245 | if ($AppLockerOrWDAC -in "Both", "WDAC") { & $ps1_CreatePoliciesWDAC } 246 | 247 | # -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /AaronLocker/CustomizationInputs/GetExeFilesToDenyList.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script used by Create-Policies.ps1 to identify EXE files that should be disallowed by AppLocker for non-admin use. Can be edited if necessary. 4 | 5 | .DESCRIPTION 6 | This script outputs a list of file paths under %windir% that need to be specifically disallowed by AllowListing rules. 7 | The list of files is consumed by Create-Policies.ps1, which builds the necessary AppLocker rules to block them. 8 | You can edit this file as needed for your environment, although it is recommended that none of the programs 9 | identified in this script be removed. 10 | 11 | Note: the solution also blocks the loading of PowerShell v2 modules - these blocks are hardcoded into the base XML file. This module 12 | as currently designed can block only EXE files, not DLLs. 13 | http://www.leeholmes.com/blog/2017/03/17/detecting-and-preventing-powershell-downgrade-attacks/ 14 | #> 15 | 16 | # -------------------------------------------------------------------------------- 17 | # Files used to bypass AllowListing: 18 | 19 | # Find the multiple instances of .NET executables that have been identified as AllowList bypasses. 20 | # Create-Policies.ps1 will remove redundant information. 21 | 22 | # https://learn.microsoft.com/en-us/windows/security/application-security/application-control/windows-defender-application-control/design/applications-that-can-bypass-wdac 23 | 24 | $Files = @("addinprocess.exe", 25 | "addinprocess32.exe", 26 | "addinutil.exe", 27 | "aspnet_compiler.exe", 28 | "bash.exe", 29 | "bginfo.exe", 30 | "cdb.exe", 31 | "cscript.exe", 32 | "cipher.exe", 33 | "certreq.exe", 34 | "certutil.exe", 35 | "Cmdl32.exe", 36 | "msdt.exe" 37 | "csi.exe", 38 | "dbghost.exe", 39 | "dbgsvc.exe", 40 | "dnx.exe", 41 | "dotnet.exe", 42 | "fsi.exe", 43 | "fsiAnyCpu.exe", 44 | "infdefaultinstall.exe", 45 | "kd.exe", 46 | "kill.exe", 47 | "lxssmanager.dll", 48 | "lxrun.exe", 49 | "Microsoft.Build.dll", 50 | "Microsoft.Build.Framework.dll", 51 | "Microsoft.Workflow.Compiler.exe", 52 | "msbuild.exe2", 53 | "msbuild.dll", 54 | "mshta.exe", 55 | "ntkd.exe", 56 | "ntsd.exe", 57 | "powershellcustomhost.exe", 58 | "PresentationHost.exe", 59 | "rcsi.exe", 60 | "runas.exe", 61 | "runscripthelper.exe", 62 | "texttransform.exe", 63 | "visualuiaverifynative.exe", 64 | "system.management.automation.dll", 65 | "wfc.exe", 66 | "windbg.exe", 67 | "wmic.exe", 68 | "wscript.exe", 69 | "wsl.exe", 70 | "wslconfig.exe", 71 | "wslhost.exe") 72 | Get-ChildItem -Path "$Env:SystemRoot" -Include $Files -Recurse -ErrorAction "SilentlyContinue" | ` 73 | Select-Object -ExpandProperty "FullName" 74 | -------------------------------------------------------------------------------- /AaronLocker/CustomizationInputs/GetSafePathsToAllow.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Customizable script used by Create-Policies.ps1 that produces a list of additional "safe" paths to allow for non-admin execution. 4 | 5 | .DESCRIPTION 6 | This script outputs a simple list of directories that can be considered "safe" for non-admins to execute programs from. 7 | The list is consumed by Create-Policies.ps1, which incorporates the paths into AppLocker rules allowing execution of 8 | EXE, DLL, and Script files. 9 | NOTE: DIRECTORY/FILE PATHS IDENTIFIED IN THIS SCRIPT MUST NOT BE WRITABLE BY NON-ADMIN USERS!!! 10 | You can edit this file as needed for your environment. 11 | 12 | Note that each directory name must be followed by \*, as in these examples: 13 | "C:\ProgramData\App-V\*" 14 | "\\MYSERVER\Apps\*" 15 | Individual files can be allowed by path, also. Do not end those with "\*" 16 | 17 | Specify paths using only fixed local drive letters or UNC paths. Do not use mapped drive letters or 18 | SUBST drive letters, as the user can change their definitions. If X: is mapped to the read-only 19 | \\MYSERVER\Apps file share, and you allow execution in \\MYSERVER\Apps\*, the user can run MyProgram.exe 20 | in that share whether it is referenced as \\MYSERVER\Apps\MyProgram.exe or as X:\MyProgram.exe. Similarly, 21 | AppLocker does the right thing with SUBSTed drive letters. 22 | 23 | TODO: At some point, reimplement with hashtable output supporting "label" and "RuleCollection" properties so that path rules have more descriptive names, and can be applied to specific rule collections. 24 | 25 | #> 26 | 27 | # Add the standard domain controller GPO file shares for the computer's AD domain, and if different, for the user account's domain. 28 | # Needed to allow execution of user logon/logoff scripts. (Computer startup/shutdown scripts run as System and don't need special rules.) 29 | # As an alternative, just output the paths explicitly; e.g., "\\corp.contoso.com\netlogon\*" 30 | # Note that if logon scripts invoke other scripts/programs using an explicit \\DC\netlogon\ syntax, these rules won't cover them. Need 31 | # explicit rules naming domain controllers. (I know that sucks.) 32 | $cs = Get-CimInstance -ClassName "CIM_ComputerSystem" 33 | if ($null -ne $cs) { 34 | if ($cs.PartOfDomain) { 35 | $computerDomain = $cs.Domain 36 | "\\$computerDomain\netlogon\*" 37 | "\\$computerDomain\sysvol\*" 38 | $userDomain = $env:USERDNSDOMAIN 39 | if ($null -ne $userDomain -and $userDomain.ToUpper() -ne $computerDomain.ToUpper()) { 40 | "\\$userDomain\netlogon\*" 41 | "\\$userDomain\sysvol\*" 42 | } 43 | } 44 | else { 45 | Write-Verbose -Message "Computer is not domain-joined; not adding path for DC shares." 46 | } 47 | } 48 | 49 | ### Windows Defender put their binaries in ProgramData for a while. Comment this back out when they move it back. 50 | "%OSDRIVE%\ProgramData\Microsoft\Windows Defender\Platform\*" 51 | "%OSDRIVE%\ProgramData\Microsoft\Windows Defender\Scans\*" 52 | 53 | ## Allow the Intune Management Extension to download and run packages 54 | "%PROGRAMFILES%\Microsoft Intune Management Extension\Content\*" 55 | 56 | # Allow paths for Azure virtual machines 57 | "%OSDRIVE%\Packages\Plugins\*" 58 | "%OSDRIVE%\WindowsAzure\*" 59 | -------------------------------------------------------------------------------- /AaronLocker/CustomizationInputs/HashRuleData.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script used to define hash rules without direct access to the files. 4 | 5 | .DESCRIPTION 6 | This script outputs zero or more hashtables containing information to define hash rules. 7 | It supports creating hash rules based on AppLocker event data rather than on direct access to the files. 8 | 9 | Each hashtable must have each of the following properties: 10 | * RuleCollection 11 | * RuleName 12 | * RuleDesc 13 | * HashVal 14 | * FileName 15 | 16 | NOTES: 17 | * RuleCollection must be one of "Exe", "Dll", "Script", or "Msi", and is CASE-SENSITIVE. 18 | * HashVal must be "0x" followed by 64 hex digits (SHA256 hash). 19 | 20 | Example: 21 | 22 | @{ 23 | RuleCollection = "Script"; 24 | RuleName = "Contoso Products: DoGoodStuff.cmd - HASH RULE"; 25 | RuleDesc = "Identified in: %LOCALAPPDATA%\TEMP\DoGoodStuff.cmd"; 26 | HashVal = "0x4CA1CD60FBFBA42C00EA6EA1B56BEFE6AD90FE0EFF58285A75D77B515D864DAE"; 27 | FileName = "DoGoodStuff.cmd" 28 | } 29 | #> 30 | 31 | -------------------------------------------------------------------------------- /AaronLocker/CustomizationInputs/KnownAdmins.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Outputs a list of known administrative users or groups that should be ignored when scanning for "user-writable" directories. 4 | 5 | .DESCRIPTION 6 | Outputs a list of zero or more administrative users or groups that Enum-WritableDirs.ps1 does not know about (e.g., custom domain or local groups or users), one to a line. 7 | 8 | The script framework scans for "user-writable" directories, looking for "write" permissions and ignoring permissions granted 9 | to "known administrative" users and groups. The framework might fail to recognize custom domain groups and (in some cases) 10 | local user accounts as administrative. This script enables adding those entities to the list of known administrative users/groups. 11 | Output one entity name or SID per line. 12 | 13 | Examples where this might be needed: 14 | * Custom domain groups that have administrative rights. 15 | * On Azure Active Directory joined systems, enumeration of BUILTIN\Administrators might not work correctly - might need to enumerate administrative accounts explicitly. 16 | 17 | Examples: 18 | 19 | "DESKTOP-7TPCJ7J\renamedAdmin" 20 | "CONTOSO\SCCM-Admins" 21 | #> 22 | 23 | -------------------------------------------------------------------------------- /AaronLocker/CustomizationInputs/TrustedSigners-MsvcMfc.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script designed to be dot-sourced into TrustedSigners.ps1 that supports the creation of publisher rules for observed MSVC*.DLL and MFC*.DLL files. 4 | 5 | .DESCRIPTION 6 | There are already MSVC* and MFC* DLLs in Windows - this script also allows redistributable DLLs that often ship with other products and are installed into user-writable directories. 7 | This output allows any version of signed MSVC* or MFC* DLLs that shipped with a known version of Visual Studio. 8 | This is not the same as allowing anything signed by Microsoft or is part of Visual Studio - just the runtime library support DLLs. 9 | 10 | This file can be updated as additional MSVC* and MFC* DLLs appear in event logs when observed executing from user-writable directories. 11 | Add more files as they are identified. 12 | 13 | See TrustedSigners.ps1 for details about how this input is used. 14 | #> 15 | 16 | ########################################################################### 17 | # Visual Studio 2005 18 | ########################################################################### 19 | 20 | @{ 21 | label = "MSVC runtime DLL"; 22 | RuleCollection = "Dll"; 23 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 24 | ProductName = "MICROSOFT® VISUAL STUDIO® 2005"; 25 | BinaryName = "MSVCP80.DLL"; 26 | } 27 | 28 | @{ 29 | label = "MSVC runtime DLL"; 30 | RuleCollection = "Dll"; 31 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 32 | ProductName = "MICROSOFT® VISUAL STUDIO® 2005"; 33 | BinaryName = "MSVCR80.DLL"; 34 | } 35 | 36 | ########################################################################### 37 | # Visual Studio 2008 38 | ########################################################################### 39 | 40 | @{ 41 | label = "MFC runtime DLL"; 42 | RuleCollection = "Dll"; 43 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 44 | ProductName = "MICROSOFT® VISUAL STUDIO® 2008"; 45 | BinaryName = "MFC90U.DLL"; 46 | } 47 | 48 | @{ 49 | label = "MSVC runtime DLL"; 50 | RuleCollection = "Dll"; 51 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 52 | ProductName = "MICROSOFT® VISUAL STUDIO® 2008"; 53 | BinaryName = "MSVCP90.DLL"; 54 | } 55 | 56 | @{ 57 | label = "MSVC runtime DLL"; 58 | RuleCollection = "Dll"; 59 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 60 | ProductName = "MICROSOFT® VISUAL STUDIO® 2008"; 61 | BinaryName = "MSVCR90.DLL"; 62 | } 63 | 64 | ########################################################################### 65 | # Visual Studio 2010 66 | ########################################################################### 67 | 68 | @{ 69 | label = "MSVC runtime DLL"; 70 | RuleCollection = "Dll"; 71 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 72 | ProductName = "MICROSOFT® VISUAL STUDIO® 2010"; 73 | BinaryName = "MSVCP100.DLL"; 74 | } 75 | 76 | @{ 77 | label = "MSVC runtime DLL"; 78 | RuleCollection = "Dll"; 79 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 80 | ProductName = "MICROSOFT® VISUAL STUDIO® 2010"; 81 | BinaryName = "MSVCR100_CLR0400.DLL"; 82 | } 83 | 84 | ########################################################################### 85 | # Visual Studio 2012 86 | ########################################################################### 87 | 88 | @{ 89 | label = "MFC runtime DLL"; 90 | RuleCollection = "Dll"; 91 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 92 | ProductName = "MICROSOFT® VISUAL STUDIO® 2012"; 93 | BinaryName = "MFC110.DLL"; 94 | } 95 | 96 | @{ 97 | label = "MSVC runtime DLL"; 98 | RuleCollection = "Dll"; 99 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 100 | ProductName = "MICROSOFT® VISUAL STUDIO® 2012"; 101 | BinaryName = "MSVCP110.DLL"; 102 | } 103 | 104 | @{ 105 | label = "MSVC runtime DLL"; 106 | RuleCollection = "Dll"; 107 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 108 | ProductName = "MICROSOFT® VISUAL STUDIO® 2012"; 109 | BinaryName = "MSVCR110.DLL"; 110 | } 111 | 112 | ########################################################################### 113 | # Visual Studio 2013 114 | ########################################################################### 115 | 116 | @{ 117 | label = "MFC runtime DLL"; 118 | RuleCollection = "Dll"; 119 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 120 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 121 | BinaryName = "MFC120.DLL"; 122 | } 123 | 124 | @{ 125 | label = "MFC runtime DLL"; 126 | RuleCollection = "Dll"; 127 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 128 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 129 | BinaryName = "MFC120U.DLL"; 130 | } 131 | 132 | @{ 133 | label = "MSVC runtime DLL"; 134 | RuleCollection = "Dll"; 135 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 136 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 137 | BinaryName = "MSVCP120.DLL"; 138 | } 139 | 140 | @{ 141 | label = "MSVC runtime DLL"; 142 | RuleCollection = "Dll"; 143 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 144 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 145 | BinaryName = "MSVCR120.DLL"; 146 | } 147 | 148 | ########################################################################### 149 | # Visual Studio 2015 150 | ########################################################################### 151 | 152 | @{ 153 | label = "MSVC runtime DLL"; 154 | RuleCollection = "Dll"; 155 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 156 | ProductName = "MICROSOFT® VISUAL STUDIO® 2015"; 157 | BinaryName = "MSVCP140.DLL"; 158 | } 159 | 160 | @{ 161 | label = "MSVC runtime DLL"; 162 | RuleCollection = "Dll"; 163 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 164 | ProductName = "MICROSOFT® VISUAL STUDIO® 2015"; 165 | BinaryName = "VCRUNTIME140.DLL"; 166 | } 167 | 168 | @{ 169 | label = "MFC runtime DLL"; 170 | RuleCollection = "Dll"; 171 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 172 | ProductName = "MICROSOFT® VISUAL STUDIO® 2015"; 173 | BinaryName = "MFC140U.DLL"; 174 | } 175 | 176 | ########################################################################### 177 | # Visual Studio 2017 178 | ########################################################################### 179 | 180 | @{ 181 | label = "MSVC runtime DLL"; 182 | RuleCollection = "Dll"; 183 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 184 | ProductName = "MICROSOFT® VISUAL STUDIO® 2017"; 185 | BinaryName = "MSVCP140.DLL"; 186 | } 187 | 188 | @{ 189 | label = "MSVC runtime DLL"; 190 | RuleCollection = "Dll"; 191 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 192 | ProductName = "MICROSOFT® VISUAL STUDIO® 2017"; 193 | BinaryName = "VCRUNTIME140.DLL"; 194 | } 195 | 196 | @{ 197 | label = "MFC runtime DLL"; 198 | RuleCollection = "Dll"; 199 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 200 | ProductName = "MICROSOFT® VISUAL STUDIO® 2017"; 201 | BinaryName = "MFC140.DLL"; 202 | } 203 | 204 | ########################################################################### 205 | # Visual Studio 10 206 | ########################################################################### 207 | 208 | @{ 209 | label = "MFC runtime DLL"; 210 | RuleCollection = "Dll"; 211 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 212 | ProductName = "MICROSOFT® VISUAL STUDIO® 10"; 213 | BinaryName = "MFC100U.DLL"; 214 | } 215 | 216 | ########################################################################### 217 | # Visual Studio (unspecified) 218 | ########################################################################### 219 | 220 | @{ 221 | label = "MSVC runtime DLL"; 222 | RuleCollection = "Dll"; 223 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 224 | ProductName = "MICROSOFT® VISUAL STUDIO®"; 225 | BinaryName = "MSVCP140.DLL"; 226 | } 227 | 228 | @{ 229 | label = "MSVC runtime DLL"; 230 | RuleCollection = "Dll"; 231 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 232 | ProductName = "MICROSOFT® VISUAL STUDIO®"; 233 | BinaryName = "VCRUNTIME140.DLL"; 234 | } -------------------------------------------------------------------------------- /AaronLocker/CustomizationInputs/TrustedSigners.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Customizable script used by Create-Policies.ps1 that identifies publishers or publisher+product/file combinations to trust. 4 | 5 | .DESCRIPTION 6 | TrustedSigners.ps1 outputs a sequence of hashtables that specify a label, and either a literal publisher name, or a path to a signed file to use as an example. 7 | 8 | Each hashtable has a "label" property that is incorporated into the rule name and description. 9 | 10 | Each hashtable also has either a "PublisherName" or an "exemplar" property: 11 | * "PublisherName" is a literal canonical name identifying a publisher to trust. 12 | When using PublisherName, you can also add optional properties: 13 | * "ProductName", to restrict trust just to that product by that publisher; with "ProductName" you can also add "BinaryName" to restrict to a specific internal file name, 14 | and optionally then "FileVersion" as well to specify a minimum allowed file version. 15 | When using BinaryName, you should also specify an explicit RuleCollection, to reduce the number of rules. (E.g., no sense in having a Script rule allowing "MSVCP80.DLL".) 16 | * "RuleCollection", to apply the trust only within a single RuleCollection. RuleCollection must be one of "Exe", "Dll", "Script", or "Msi", and it is CASE-SENSITIVE. 17 | * "exemplar" is the path to a signed file; the publisher to trust is extracted from that signature. When using exemplar, you can also add an optional "useProduct" boolean value indicating whether to restrict publisher trust only to that file's product name. If "useProduct" is not specified, all files signed by the publisher are trusted. 18 | 19 | Examples showing possible combinations: 20 | 21 | # Trust everything by a specific publisher 22 | @{ 23 | label = "Trust all Contoso"; 24 | PublisherName = "O=CONTOSO, L=SEATTLE, S=WASHINGTON, C=US"; 25 | } 26 | 27 | # Trust all DLLs by a specific publisher 28 | @{ 29 | label = "Trust all Contoso DLLs"; 30 | PublisherName = "O=CONTOSO, L=SEATTLE, S=WASHINGTON, C=US"; 31 | RuleCollection = "Dll"; 32 | } 33 | 34 | # Trust a specific product published by a specific publisher 35 | @{ 36 | label = "Trust all CUSTOMAPP files published by Contoso"; 37 | PublisherName = "O=CONTOSO, L=SEATTLE, S=WASHINGTON, C=US"; 38 | ProductName = "CUSTOMAPP"; 39 | } 40 | 41 | # Trust any version of a specific signed file by a specific publisher/product 42 | # RuleCollection must be one of Exe, Dll, Script, or Msi, and is CASE-SENSITIVE 43 | @{ 44 | label = "Trust Contoso's SAMPLE.DLL in CUSTOMAPP"; 45 | PublisherName = "O=CONTOSO, L=SEATTLE, S=WASHINGTON, C=US"; 46 | ProductName = "CUSTOMAPP"; 47 | BinaryName = "SAMPLE.DLL"; 48 | FileVersion = "10.0.15063.0"; 49 | RuleCollection = "Dll"; 50 | } 51 | 52 | # Trust everything signed by the same publisher as the exemplar file (Autoruns.exe) 53 | @{ 54 | label = "Trust the publisher of Autoruns.exe"; 55 | exemplar = "C:\Program Files\Sysinternals\Autoruns.exe"; 56 | } 57 | 58 | # Trust everything with the same publisher and product as the exemplar file (LuaBuglight.exe) 59 | @{ 60 | label = "Trust everything with the same publisher and product as LuaBuglight.exe"; 61 | exemplar = "C:\Program Files\Utils\LuaBuglight.exe"; 62 | useProduct = $true 63 | } 64 | #> 65 | 66 | 67 | @{ 68 | # Allow Microsoft-signed files with the Microsoft Teams product name. 69 | label = "Microsoft Teams"; 70 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 71 | ProductName = "MICROSOFT TEAMS"; 72 | } 73 | 74 | @{ 75 | label = "Microsoft-signed MSI files"; 76 | RuleCollection = "Msi"; 77 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 78 | } 79 | 80 | @{ 81 | # Windows' built-in troubleshooting often involves running Microsoft-signed scripts in the user's profile 82 | label = "Microsoft-signed script files"; 83 | RuleCollection = "Script"; 84 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 85 | } 86 | 87 | # During Windows upgrade, setup loads %OSDRIVE%\$WINDOWS.~BT\SOURCES\GENERALTEL.DLL, which loads two other DLLs in the same directory 88 | @{ 89 | label = "Allow selected files from %OSDRIVE%\$WINDOWS.~BT\SOURCES during Windows upgrade"; 90 | RuleCollection = "Dll"; 91 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 92 | ProductName = "MICROSOFT® WINDOWS® OPERATING SYSTEM"; 93 | BinaryName = "GENERALTEL.DLL"; 94 | } 95 | @{ 96 | label = "Allow selected files from %OSDRIVE%\$WINDOWS.~BT\SOURCES during Windows upgrade"; 97 | RuleCollection = "Dll"; 98 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 99 | ProductName = "MICROSOFT® WINDOWS® OPERATING SYSTEM"; 100 | BinaryName = "WDSCORE.DLL"; 101 | } 102 | @{ 103 | label = "Allow selected files from %OSDRIVE%\$WINDOWS.~BT\SOURCES during Windows upgrade"; 104 | RuleCollection = "Dll"; 105 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 106 | ProductName = "MICROSOFT® WINDOWS® OPERATING SYSTEM"; 107 | BinaryName = "AEINV.DLL"; 108 | } 109 | 110 | # Allow protected content run in MS Edge 111 | @{ 112 | label = "MS Edge content protection"; 113 | PublisherName = "O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US"; 114 | ProductName = "WIDEVINE CONTENT DECRYPTION MODULE"; 115 | RuleCollection = "Dll"; 116 | } 117 | 118 | # Uncomment this block if Google Chrome is installed to ProgramFiles. 119 | # Google Chrome runs some code in the user profile even when Chrome is installed to Program Files. 120 | # This creates publisher rules that allow those components to run. 121 | # Note that PublisherName used to be "O=GOOGLE INC, L=MOUNTAIN VIEW, S=CALIFORNIA, C=US" 122 | <# 123 | #region 124 | @{ 125 | label = "Google Chrome SWReporter tool"; 126 | RuleCollection = "Exe"; 127 | PublisherName = "O=GOOGLE LLC, L=MOUNTAIN VIEW, S=CA, C=US"; 128 | ProductName = "SOFTWARE REPORTER TOOL"; 129 | BinaryName = "SOFTWARE_REPORTER_TOOL.EXE"; 130 | } 131 | @{ 132 | label = "Google Chrome Cleanup"; 133 | RuleCollection = "Dll"; 134 | PublisherName = "O=ESET, SPOL. S R.O., L=BRATISLAVA, C=SK"; 135 | ProductName = "CHROME CLEANUP"; 136 | } 137 | @{ 138 | label = "Google Chrome Cleanup"; 139 | RuleCollection = "Dll"; 140 | PublisherName = "O=ESET, SPOL. S R.O., L=BRATISLAVA, S=SLOVAKIA, C=SK"; 141 | ProductName = "CHROME CLEANUP"; 142 | } 143 | @{ 144 | label = "Google Chrome Protector"; 145 | RuleCollection = "Dll"; 146 | PublisherName = "O=ESET, SPOL. S R.O., L=BRATISLAVA, S=SLOVAKIA, C=SK"; 147 | ProductName = "CHROME PROTECTOR"; 148 | } 149 | 150 | @{ 151 | label = "Google Chrome, Identified in: '%LOCALAPPDATA%\GOOGLE\CHROME\USER DATA\SWREPORTER\81.235.200\EDLS_64.DLL'"; 152 | PublisherName = "O=ESET, SPOL. S R.O., L=BRATISLAVA, C=SK"; 153 | ProductName = "CHROME CLEANUP"; 154 | RuleCollection = "Dll"; 155 | } 156 | 157 | @{ 158 | label = "Google Chrome, Identified in: '%LOCALAPPDATA%\GOOGLE\CHROME\USER DATA\SWREPORTER\81.235.200\SOFTWARE_REPORTER_TOOL.EXE'"; 159 | PublisherName = "O=GOOGLE LLC, L=MOUNTAIN VIEW, S=CA, C=US"; 160 | ProductName = "SOFTWARE REPORTER TOOL"; 161 | RuleCollection = "Exe"; 162 | } 163 | 164 | @{ 165 | label = "Google Chrome, Identified in: '%LOCALAPPDATA%\GOOGLE\UPDATE\1.3.35.452\GOOPDATE.DLL'"; 166 | PublisherName = "O=GOOGLE LLC, L=MOUNTAIN VIEW, S=CALIFORNIA, C=US"; 167 | ProductName = "GOOGLE UPDATE"; 168 | RuleCollection = "Dll"; 169 | } 170 | 171 | @{ 172 | label = "Google Chrome, Identified in: '%LOCALAPPDATA%\GOOGLE\CHROME\APPLICATION\79.0.3945.117\CHROME_CHILD.DLL'"; 173 | PublisherName = "O=GOOGLE LLC, L=MOUNTAIN VIEW, S=CA, C=US"; 174 | ProductName = "GOOGLE CHROME"; 175 | } 176 | 177 | @{ 178 | label = "Google Chrome, Identified in: '%LOCALAPPDATA%\GOOGLE\UPDATE\1.3.35.452\GOOPDATE.DLL'"; 179 | PublisherName = "O=GOOGLE LLC, L=MOUNTAIN VIEW, S=CALIFORNIA, C=US"; 180 | ProductName = "GOOGLE UPDATE"; 181 | } 182 | 183 | @{ 184 | label = "Google Chrome, Identified in: '%LOCALAPPDATA%\GOOGLE\CHROME\USER DATA\SWREPORTER\85.244.200\EM000_64.DLL'"; 185 | PublisherName = "O=ESET, SPOL. S R.O., L=BRATISLAVA, C=SK"; 186 | ProductName = "CHROME PROTECTOR"; 187 | } 188 | 189 | @{ 190 | label = "Google Chrome, Identified in: '%LOCALAPPDATA%\GOOGLE\VIDEO SUPPORT PLUGIN\19.12.1000.0\GOOGLETALKAX.DLL'"; 191 | PublisherName = "O=GOOGLE LLC, L=MOUNTAIN VIEW, S=CA, C=US"; 192 | ProductName = "GOOGLE TALK PLUGIN"; 193 | RuleCollection = "Dll"; 194 | } 195 | 196 | @{ 197 | label = "Google Chrome, Identified in: '%LOCALAPPDATA%\GOOGLE\UPDATE\1.3.35.452\GOOGLECRASHHANDLER.EXE'"; 198 | PublisherName = "O=GOOGLE LLC, L=MOUNTAIN VIEW, S=CALIFORNIA, C=US"; 199 | ProductName = "GOOGLE UPDATE"; 200 | } 201 | 202 | @{ 203 | label = "Google Chrome, Identified in: '%LOCALAPPDATA%\GOOGLE\VIDEO SUPPORT PLUGIN\19.12.1000.0\CRASHPAD_HANDLER.EXE'"; 204 | PublisherName = "O=GOOGLE LLC, L=MOUNTAIN VIEW, S=CA, C=US"; 205 | RuleCollection = "Exe"; 206 | } 207 | #endregion 208 | #> 209 | 210 | 211 | # Allow MSVC/MFC redistributable DLLs. Dot-source the MSVC/MFC DLL include file in this directory 212 | . ([System.IO.Path]::Combine( [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path), "TrustedSigners-MsvcMfc.ps1")) 213 | 214 | -------------------------------------------------------------------------------- /AaronLocker/CustomizationInputs/UnsafePathsToBuildRulesFor.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Customizable script used by Create-Policies.ps1 that identifies user-writable paths containing files that need to be allowed to execute. 4 | 5 | .DESCRIPTION 6 | This script outputs a sequence of hashtables that identify user-writable files or directory paths containing content that users must be allowed to execute. 7 | (The scripts favor publisher rules over hash rules.) 8 | Each hashtable must include "label" and "paths" properties, with additional optional properties. 9 | Hashtable properties: 10 | * label - REQUIRED; incorporated into rules' names and descriptions. 11 | * paths - REQUIRED; identifies one or more paths (comma separated if more than one). 12 | If a path is a directory, rules are generated for the existing files in that directory. 13 | If a path is to a file, a rule is generated for that file. 14 | * pubruleGranularity - OPTIONAL; specifies granularity of publisher rules. 15 | If specified, must be one of the following: 16 | pubOnly - lowest granularity: Publisher rules specify publisher only 17 | pubProduct - Publisher rules specify publisher and product 18 | pubProductBinary - (default) Publisher rules specify publisher, product, and binary name 19 | pubProdBinVer - highest granularity: Publisher rules specify publisher, product, binary name, and minimum version. 20 | Microsoft-signed Windows and Visual Studio files are always handled at a minimum granularity of "pubProductBinary"; 21 | other Microsoft-signed files are handled at a minimum granularity of "pubProduct". 22 | ****NOTE**** 23 | pubruleGranularity is handled very differently for WDAC policies. WDAC rules use the pubruleGranularity to determine the default -Level but then falls 24 | back to successively more restrictive options.The Granularity mappings are as follows: 25 | pubOnly --> -Level Publisher -Fallback FilePublisher,FileName,Hash 26 | pubProduct --> -Level FilePublisher -SpecificFileNameLevel ProductName -Fallback FilePublisher,FileName,Hash 27 | pubProductBinary --> NOT supported for WDAC rules. Reverts to pubProdBinVer. 28 | pubProdBinVer --> -Level FilePublisher -Fallback FileName,Hash (ProductName *not* included in generated rule) 29 | * JSHashRules - OPTIONAL; if specified and set to $true, generates hash rules for unsigned .js files; otherwise, doesn't generate them. 30 | NOTE: JSHashRules is *ignored* for WDAC policy generation. Hash rules are always created for .js files discovered. 31 | * noRecurse - OPTIONAL; if specified and set to $true, rules are generated only for the files in the specified directory or directories. 32 | Otherwise, rules are also generated for files in subdirectories of the specified directory or directories. 33 | NOTE: noRecurse is *ignored* for WDAC policy generation. Subdirectories are always scanned. 34 | * enforceMinVersion - DEPRECATED and OPTIONAL. pubruleGranularity takes precedence if specified. 35 | Otherwise, setting to $false equivalent to pubruleGranularity = pubProductBinary; 36 | setting to $true equivalent to pubruleGranularity = pubProdBinVer. 37 | NOTE: enforceMinversion is always *ignored* for WDAC policy generation. MinVersion is always included in rules. 38 | 39 | Examples of valid hash tables: 40 | 41 | # Search one directory and its subdirectories for files to generate rules for. 42 | # Default granularity for publisher rules: create a separate rule for each file but allow any file version. 43 | @{ 44 | label = "OneDrive"; 45 | paths = "$env:LOCALAPPDATA\Microsoft\OneDrive"; 46 | } 47 | 48 | # Search one directory and its subdirectories for files to generate rules for. 49 | # Generated publisher rules contain only publisher and product names. 50 | # (Note that some Microsoft-signed files will also include binary name.) 51 | @{ 52 | label = "OneDrive"; 53 | paths = "$env:LOCALAPPDATA\Microsoft\OneDrive"; 54 | pubruleGranularity = "pubProduct"; 55 | } 56 | 57 | # Search two separate directory structures for files to generate rules for, plus one explicitly-identified file. 58 | @{ 59 | label = "ContosoIT"; 60 | paths = "$env:LOCALAPPDATA\Programs\MyContosoIT\Helper", 61 | "C:\ProgramData\COntosoIT\ContosoIT System Health Client", 62 | "$env:LOCALAPPDATA\TEMP\CORPSEC\ITGSECLOGONGPEXEC.EXE" 63 | } 64 | 65 | # Generate rules for three distinct files; do not recurse subdirectories looking for additional matches. 66 | @{ 67 | label = "Custom backup scripts"; 68 | paths = "C:\Backups\MyBackup.vbs", 69 | "C:\Backups\MyPersonalBackup.vbs", 70 | "C:\Backups\Exports\RegExport.1.cmd"; 71 | noRecurse = $true 72 | } 73 | #> 74 | 75 | # Defender DLLs still being listed as blocked even with path rules 76 | @{ 77 | label = "Microsoft Defender"; 78 | paths = "$Env:ProgramData\Microsoft\Windows Defender\Platform", "$Env:ProgramData\Microsoft\Windows Defender\Scans"; 79 | pubruleGranularity = "pubProduct"; 80 | } 81 | 82 | @{ 83 | label = "Microsoft OneDrive"; 84 | paths = "$env:LOCALAPPDATA\Microsoft\OneDrive"; 85 | pubruleGranularity = "pubProduct"; 86 | } 87 | 88 | @{ 89 | label = "Microsoft Teams"; 90 | paths = "$env:LOCALAPPDATA\Microsoft\Teams\current", 91 | "$env:LOCALAPPDATA\Microsoft\TeamsMeetingAddin", 92 | "$env:LOCALAPPDATA\Microsoft\TeamsPresenceAddin"; 93 | pubruleGranularity = "pubProduct"; 94 | } 95 | 96 | @{ 97 | label = "Squirrel Update"; 98 | paths = "$env:LOCALAPPDATA\SquirrelTemp"; 99 | pubruleGranularity = "pubProduct"; 100 | } 101 | 102 | # @{ 103 | # label = "Zoom"; 104 | # paths = "$env:LOCALAPPDATA\Zoom", 105 | # "$env:AppData\Zoom\bin"; 106 | # pubruleGranularity = "pubProduct"; 107 | # } 108 | 109 | # @{ 110 | # label = "GoToMeeting"; 111 | # paths = "$env:LOCALAPPDATA\GoToMeeting"; 112 | # pubruleGranularity = "pubProduct"; 113 | # } 114 | 115 | # @{ 116 | # label = "Cisco WebEx"; 117 | # paths = "$env:LOCALAPPDATA\Programs\Cisco Spark"; 118 | # pubruleGranularity = "pubProduct"; 119 | # } 120 | -------------------------------------------------------------------------------- /AaronLocker/CustomizationInputs/WDACTrustedSigners-MsvcMfc.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script designed to be dot-sourced into TrustedSigners.ps1 that supports the creation of publisher rules for observed MSVC*.DLL and MFC*.DLL files. 4 | 5 | .DESCRIPTION 6 | There are already MSVC* and MFC* DLLs in Windows - this script also allows redistributable DLLs that often ship with other products and are installed into user-writable directories. 7 | This output allows any version of signed MSVC* or MFC* DLLs that shipped with a known version of Visual Studio. 8 | This is not the same as allowing anything signed by Microsoft or is part of Visual Studio - just the runtime library support DLLs. 9 | 10 | This file can be updated as additional MSVC* and MFC* DLLs appear in event logs when observed executing from user-writable directories. 11 | Add more files as they are identified. 12 | 13 | See TrustedSigners.ps1 for details about how this input is used. 14 | #> 15 | 16 | ########################################################################### 17 | # Visual Studio 2008 18 | ########################################################################### 19 | 20 | @{ 21 | label = "MFC runtime DLL"; 22 | IssuerName = "Microsoft Code Signing PCA"; 23 | IssuerTBSHash = "7251ADC0F732CF409EE462E335BB99544F2DD40F"; 24 | PublisherName = "Microsoft Corporation"; 25 | ProductName = "Microsoft® Visual Studio® 2008"; 26 | FileName = "MFC90U.DLL"; 27 | } 28 | 29 | @{ 30 | label = "MSVC runtime DLL"; 31 | IssuerName = "Microsoft Code Signing PCA 2011"; 32 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 33 | PublisherName = "Microsoft Corporation"; 34 | ProductName = "Microsoft® Visual Studio® 2008"; 35 | FileName = "MSVCP90.DLL"; 36 | } 37 | 38 | @{ 39 | label = "MSVC runtime DLL"; 40 | IssuerName = "Microsoft Code Signing PCA 2011"; 41 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 42 | PublisherName = "Microsoft Corporation"; 43 | ProductName = "Microsoft® Visual Studio® 2008"; 44 | FileName = "MSVCR90.DLL"; 45 | } 46 | 47 | ########################################################################### 48 | # Visual Studio 2010 49 | ########################################################################### 50 | 51 | @{ 52 | label = "MSVC runtime DLL"; 53 | IssuerName = "Microsoft Code Signing PCA"; 54 | IssuerTBSHash = "7251ADC0F732CF409EE462E335BB99544F2DD40F"; 55 | PublisherName = "Microsoft Corporation"; 56 | ProductName = "Microsoft® Visual Studio® 2010"; 57 | FileName = "msvcp100.dll"; 58 | } 59 | 60 | @{ 61 | label = "MSVC runtime DLL"; 62 | IssuerName = "Microsoft Code Signing PCA 2011"; 63 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 64 | PublisherName = "Microsoft Corporation"; 65 | ProductName = "Microsoft® .NET Framework"; 66 | FileName = "MSVCR100_CLR0400.DLL"; 67 | } 68 | 69 | ########################################################################### 70 | # Visual Studio 2012 71 | ########################################################################### 72 | 73 | @{ 74 | label = "MFC runtime DLL"; 75 | IssuerName = "Microsoft Code Signing PCA"; 76 | IssuerTBSHash = "27543A3F7612DE2261C7228321722402F63A07DE"; 77 | PublisherName = "Microsoft Corporation"; 78 | ProductName = "Microsoft® Visual Studio® 2012"; 79 | FileName = "MFC110.DLL"; 80 | } 81 | 82 | @{ 83 | label = "MFC runtime DLL"; 84 | IssuerName = "Microsoft Code Signing PCA 2011"; 85 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 86 | PublisherName = "Microsoft Corporation"; 87 | ProductName = "Microsoft® Visual Studio® 2012"; 88 | FileName = "MFC110.DLL"; 89 | } 90 | 91 | @{ 92 | label = "MSVC runtime DLL"; 93 | IssuerName = "Microsoft Code Signing PCA"; 94 | IssuerTBSHash = "27543A3F7612DE2261C7228321722402F63A07DE"; 95 | PublisherName = "Microsoft Corporation"; 96 | ProductName = "Microsoft® Visual Studio® 2012"; 97 | FileName = "MSVCP110.DLL"; 98 | } 99 | 100 | @{ 101 | label = "MSVC runtime DLL"; 102 | IssuerName = "Microsoft Code Signing PCA 2011"; 103 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 104 | PublisherName = "Microsoft Corporation"; 105 | ProductName = "Microsoft® Visual Studio® 2012"; 106 | FileName = "MSVCP110.DLL"; 107 | } 108 | 109 | @{ 110 | label = "MSVC runtime DLL"; 111 | IssuerName = "Microsoft Code Signing PCA"; 112 | IssuerTBSHash = "27543A3F7612DE2261C7228321722402F63A07DE"; 113 | PublisherName = "Microsoft Corporation"; 114 | ProductName = "Microsoft® Visual Studio® 2012"; 115 | FileName = "MSVCR110.DLL"; 116 | } 117 | 118 | @{ 119 | label = "MSVC runtime DLL"; 120 | IssuerName = "Microsoft Code Signing PCA 2011"; 121 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 122 | PublisherName = "Microsoft Corporation"; 123 | ProductName = "Microsoft® Visual Studio® 2012"; 124 | FileName = "MSVCR110.DLL"; 125 | } 126 | 127 | ########################################################################### 128 | # Visual Studio 2013 129 | ########################################################################### 130 | 131 | @{ 132 | label = "MFC runtime DLL"; 133 | IssuerName = "Microsoft Code Signing PCA"; 134 | IssuerTBSHash = "27543A3F7612DE2261C7228321722402F63A07DE"; 135 | PublisherName = "Microsoft Corporation"; 136 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 137 | FileName = "MFC120.DLL"; 138 | } 139 | 140 | @{ 141 | label = "MFC runtime DLL"; 142 | IssuerName = "Microsoft Code Signing PCA 2011"; 143 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 144 | PublisherName = "Microsoft Corporation"; 145 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 146 | FileName = "MFC120.DLL"; 147 | } 148 | 149 | @{ 150 | label = "MFC runtime DLL"; 151 | IssuerName = "Microsoft Code Signing PCA"; 152 | IssuerTBSHash = "27543A3F7612DE2261C7228321722402F63A07DE"; 153 | PublisherName = "Microsoft Corporation"; 154 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 155 | FileName = "MFC120U.DLL"; 156 | } 157 | 158 | @{ 159 | label = "MFC runtime DLL"; 160 | IssuerName = "Microsoft Code Signing PCA 2011"; 161 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 162 | PublisherName = "Microsoft Corporation"; 163 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 164 | FileName = "MFC120U.DLL"; 165 | } 166 | 167 | @{ 168 | label = "MSVC runtime DLL"; 169 | IssuerName = "Microsoft Code Signing PCA"; 170 | IssuerTBSHash = "27543A3F7612DE2261C7228321722402F63A07DE"; 171 | PublisherName = "Microsoft Corporation"; 172 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 173 | FileName = "MSVCP120.DLL"; 174 | } 175 | 176 | @{ 177 | label = "MSVC runtime DLL"; 178 | IssuerName = "Microsoft Code Signing PCA 2011"; 179 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 180 | PublisherName = "Microsoft Corporation"; 181 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 182 | FileName = "MSVCP120.DLL"; 183 | } 184 | 185 | @{ 186 | label = "MSVC runtime DLL"; 187 | IssuerName = "Microsoft Code Signing PCA"; 188 | IssuerTBSHash = "27543A3F7612DE2261C7228321722402F63A07DE"; 189 | PublisherName = "Microsoft Corporation"; 190 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 191 | FileName = "MSVCR120.DLL"; 192 | } 193 | 194 | @{ 195 | label = "MSVC runtime DLL"; 196 | IssuerName = "Microsoft Code Signing PCA 2011"; 197 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 198 | PublisherName = "Microsoft Corporation"; 199 | ProductName = "MICROSOFT® VISUAL STUDIO® 2013"; 200 | FileName = "MSVCR120.DLL"; 201 | } 202 | 203 | ########################################################################### 204 | # Visual Studio 2015 205 | ########################################################################### 206 | 207 | @{ 208 | label = "MSVC runtime DLL"; 209 | IssuerName = "Microsoft Code Signing PCA 2011"; 210 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 211 | PublisherName = "Microsoft Corporation"; 212 | ProductName = "MICROSOFT® VISUAL STUDIO® 2015"; 213 | FileName = "MSVCP140.DLL"; 214 | } 215 | 216 | @{ 217 | label = "MSVC runtime DLL"; 218 | IssuerName = "Microsoft Code Signing PCA 2011"; 219 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 220 | PublisherName = "Microsoft Corporation"; 221 | ProductName = "MICROSOFT® VISUAL STUDIO® 2015"; 222 | FileName = "VCRUNTIME140.DLL"; 223 | } 224 | 225 | @{ 226 | label = "MFC runtime DLL"; 227 | IssuerName = "Microsoft Code Signing PCA 2011"; 228 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 229 | PublisherName = "Microsoft Corporation"; 230 | ProductName = "MICROSOFT® VISUAL STUDIO® 2015"; 231 | FileName = "MFC140U.DLL"; 232 | } 233 | 234 | ########################################################################### 235 | # Visual Studio 2017 236 | ########################################################################### 237 | 238 | @{ 239 | label = "MSVC runtime DLL"; 240 | IssuerName = "Microsoft Code Signing PCA 2011"; 241 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 242 | PublisherName = "Microsoft Corporation"; 243 | ProductName = "MICROSOFT® VISUAL STUDIO® 2017"; 244 | FileName = "MSVCP140.DLL"; 245 | } 246 | 247 | @{ 248 | label = "MSVC runtime DLL"; 249 | IssuerName = "Microsoft Code Signing PCA 2011"; 250 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 251 | PublisherName = "Microsoft Corporation"; 252 | ProductName = "MICROSOFT® VISUAL STUDIO® 2017"; 253 | FileName = "VCRUNTIME140.DLL"; 254 | } 255 | 256 | @{ 257 | label = "MFC runtime DLL"; 258 | IssuerName = "Microsoft Code Signing PCA 2011"; 259 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 260 | PublisherName = "Microsoft Corporation"; 261 | ProductName = "MICROSOFT® VISUAL STUDIO® 2017"; 262 | FileName = "MFC140.DLL"; 263 | } 264 | 265 | ########################################################################### 266 | # Visual Studio 10 267 | ########################################################################### 268 | 269 | @{ 270 | label = "MFC runtime DLL"; 271 | IssuerName = "Microsoft Code Signing PCA 2011"; 272 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 273 | PublisherName = "Microsoft Corporation"; 274 | ProductName = "MICROSOFT® VISUAL STUDIO® 10"; 275 | FileName = "MFC100U.DLL"; 276 | } 277 | 278 | ########################################################################### 279 | # Visual Studio 2015, 2017, 2019 280 | ########################################################################### 281 | 282 | @{ 283 | label = "MSVC runtime DLL"; 284 | IssuerName = "Microsoft Code Signing PCA 2011"; 285 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 286 | PublisherName = "Microsoft Corporation"; 287 | ProductName = "Microsoft® Visual Studio®"; 288 | FileName = "MSVCP140.DLL"; 289 | } 290 | 291 | @{ 292 | label = "MSVC runtime DLL"; 293 | IssuerName = "Microsoft Code Signing PCA 2011"; 294 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 295 | PublisherName = "Microsoft Corporation"; 296 | ProductName = "Microsoft® Visual Studio®"; 297 | FileName = "VCRUNTIME140.DLL"; 298 | } 299 | 300 | @{ 301 | label = "MFC runtime DLL"; 302 | IssuerName = "Microsoft Code Signing PCA 2011"; 303 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 304 | PublisherName = "Microsoft Corporation"; 305 | ProductName = "Microsoft® Visual Studio®"; 306 | FileName = "MFC140U.DLL"; 307 | } 308 | 309 | -------------------------------------------------------------------------------- /AaronLocker/CustomizationInputs/WDACTrustedSigners.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Customizable script used by Create-Policies-WDAC.ps1 that identifies publishers or publisher+product/file combinations to trust. 4 | 5 | .DESCRIPTION 6 | WDACTrustedSigners.ps1 outputs a sequence of hashtables that specify a label, and either a signer or file rule. 7 | 8 | Each hashtable has a "label" property that is incorporated into the rule name and description, where appropriate. 9 | 10 | Each hashtable also has either information to formulate a signer rule for WDAC or an "exemplar" property: 11 | All information needed to formulate a signer rule can be found on WDAC block signature correlation events (EventID 3089) or by querying the certificate directly. 12 | * "IssuerName" is the common name (CN) of the intermediate cert in the cert chain and is found as the Issuer on a leaf certificate. 13 | * "IssuerTBSHash" is the TBS hash value of the intermediate cert in the cert chain. 14 | * "PublisherName" is the CN of the leaf certificate 15 | When using PublisherName, you can also add optional properties: 16 | * "ProductName", to restrict trust just to that product by that publisher (e.g. "Microsoft Teams") 17 | * "FileName" is the original filename property of the signed file and can be used to authorize only specific binaries signed by the Publisher. 18 | * "FileVersion" is the minimum allowed file version for the named binary or all binaries from the specified Publisher. 19 | * "exemplar" is the path to a signed file; all information to construct the rule is extracted from that file's signature and signed attributes. 20 | When using exemplar, you can also add optional properties: 21 | * "level" is the WDAC rule level used with New-CIPolicyRule and defaults to Publisher 22 | * "useProduct" boolean value indicating whether to restrict publisher trust only to that file's product name. 23 | 24 | Examples showing possible combinations: 25 | 26 | # Trust everything by a specific publisher 27 | @{ 28 | label = "Trust all Contoso"; 29 | IssuerName = "Symantec Class 3 SHA256 Code Signing CA - G2"; 30 | IssuerTBSHash = "7F25CBD37DCDC0E0D93E0D477C4BC0C54231379E6CAF1023841E1F0D96467A6C"; 31 | PublisherName = "Contoso Software"; 32 | } 33 | 34 | # Trust any version of a specific signed file by a specific publisher 35 | @{ 36 | label = "Trust Contoso's SAMPLE.DLL"; 37 | IssuerName = "Symantec Class 3 SHA256 Code Signing CA - G2"; 38 | IssuerTBSHash = "7F25CBD37DCDC0E0D93E0D477C4BC0C54231379E6CAF1023841E1F0D96467A6C"; 39 | PublisherName = "Contoso Software"; 40 | FileName = "SAMPLE.DLL"; 41 | } 42 | 43 | # Trust a specific product published by a specific publisher 44 | @{ 45 | label = "Trust all CUSTOMAPP files published by Contoso"; 46 | IssuerName = "Symantec Class 3 SHA256 Code Signing CA - G2"; 47 | IssuerTBSHash = "7F25CBD37DCDC0E0D93E0D477C4BC0C54231379E6CAF1023841E1F0D96467A6C"; 48 | PublisherName = "Contoso Software"; 49 | ProductName = "CUSTOMAPP"; 50 | } 51 | 52 | # Trust only files with version greater or equal to 10.0.0.0 published by a specific publisher 53 | @{ 54 | label = "Trust all files with version 10.0.0.0 or greater published by Contoso"; 55 | IssuerName = "Symantec Class 3 SHA256 Code Signing CA - G2"; 56 | IssuerTBSHash = "7F25CBD37DCDC0E0D93E0D477C4BC0C54231379E6CAF1023841E1F0D96467A6C"; 57 | PublisherName = "Contoso Software"; 58 | FileVersion = "10.0.0.0"; 59 | } 60 | 61 | # Trust only versions of a specific signed file greater or equal to 10.0.0.0 by a specific publisher 62 | @{ 63 | label = "Trust Contoso's SAMPLE.DLL version 10.0.0.0 or greater"; 64 | IssuerName = "Symantec Class 3 SHA256 Code Signing CA - G2"; 65 | IssuerTBSHash = "7F25CBD37DCDC0E0D93E0D477C4BC0C54231379E6CAF1023841E1F0D96467A6C"; 66 | PublisherName = "Contoso Software"; 67 | FileName = "SAMPLE.DLL"; 68 | FileVersion = "10.0.0.0"; 69 | } 70 | 71 | # Trust everything signed by the same publisher as the exemplar file (Autoruns.exe) 72 | @{ 73 | label = "Trust the publisher of Autoruns.exe"; 74 | exemplar = "C:\Program Files\Sysinternals\Autoruns.exe"; 75 | } 76 | 77 | # Trust everything with the same publisher and product as the exemplar file (LuaBuglight.exe) 78 | @{ 79 | label = "Trust everything with the same publisher and product as LuaBuglight.exe"; 80 | exemplar = "C:\Program Files\Utils\LuaBuglight.exe"; 81 | useProduct = $true 82 | } 83 | #> 84 | 85 | @{ 86 | # Allow Microsoft-signed files with the Microsoft Teams product name. 87 | label = "Microsoft Teams"; 88 | IssuerName = "Microsoft Code Signing PCA 2011"; 89 | IssuerTBSHash = "F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E"; 90 | PublisherName = "Microsoft Corporation"; 91 | ProductName = "MICROSOFT TEAMS"; 92 | } 93 | 94 | # Uncomment this block if Google Chrome is installed to ProgramFiles. 95 | # Google Chrome runs some code in the user profile even when Chrome is installed to Program Files. 96 | # This creates publisher rules that allow those components to run. 97 | # Note that PublisherName used to be "O=GOOGLE INC, L=MOUNTAIN VIEW, S=CALIFORNIA, C=US" 98 | <# 99 | @{ 100 | label = "Google Chrome SWReporter tool"; 101 | RuleCollection = "Exe"; 102 | PublisherName = "O=GOOGLE LLC, L=MOUNTAIN VIEW, S=CA, C=US"; 103 | ProductName = "SOFTWARE REPORTER TOOL"; 104 | BinaryName = "SOFTWARE_REPORTER_TOOL.EXE"; 105 | } 106 | @{ 107 | label = "Google Chrome Cleanup"; 108 | RuleCollection = "Dll"; 109 | PublisherName = "O=ESET, SPOL. S R.O., L=BRATISLAVA, S=SLOVAKIA, C=SK"; 110 | ProductName = "CHROME CLEANUP"; 111 | } 112 | @{ 113 | label = "Google Chrome Protector"; 114 | RuleCollection = "Dll"; 115 | PublisherName = "O=ESET, SPOL. S R.O., L=BRATISLAVA, S=SLOVAKIA, C=SK"; 116 | ProductName = "CHROME PROTECTOR"; 117 | } 118 | #> 119 | 120 | # Allow MSVC/MFC redistributable DLLs. Dot-source the MSVC/MFC DLL include file in this directory 121 | . ([System.IO.Path]::Combine( [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path), "WDACTrustedSigners-MsvcMfc.ps1")) 122 | -------------------------------------------------------------------------------- /AaronLocker/ExportPolicy-ToExcel.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Turns AppLocker policy into a more human-readable Excel worksheet. 4 | 5 | .DESCRIPTION 6 | The script gets AppLocker policy from one of four sources, imports it into a new Excel instance, and formats it. 7 | 8 | The four source options are: 9 | * Current effective policy (default behavior -- use no parameters); 10 | * Current local policy (use -Local switch); 11 | * Exported AppLocker policy in an XML file (use -AppLockerXML parameter with file path); 12 | * Output previously captured from ExportPolicy-ToCsv.ps1 (use -AppLockerCSV with file path); 13 | 14 | This script depends on ExportPolicy-ToCsv.ps1, which should be in the Support subdirectory. 15 | It also depends on Microsoft Excel's being installed. 16 | 17 | The three command line options (-Local, -AppLockerXML, -AppLockerCSV) are mutually exclusive: only one can be used at a time. 18 | 19 | .PARAMETER Local 20 | If this switch is specified, the script processes the computer's local AppLocker policy. 21 | If no parameters are specified or this switch is set to -Local:$false, the script processes the computer's effective AppLocker policy. 22 | 23 | .PARAMETER AppLockerXML 24 | If this parameter is specified, AppLocker policy is read from the specified exported XML policy file. 25 | 26 | .PARAMETER AppLockerCSV 27 | If this parameter is specified, AppLocker policy is read from the specified CSV file previously created from ExportPolicy-ToCsv.ps1 output. 28 | 29 | .PARAMETER SaveWorkbook 30 | If set, saves workbook to same directory as input file with same file name and default Excel file extension. 31 | 32 | .EXAMPLE 33 | .\ExportPolicy-ToExcel.ps1 34 | 35 | Generates an Excel worksheet representing the computer's effective AppLocker policy. 36 | 37 | .EXAMPLE 38 | .\Support\ExportPolicy-ToCsv.ps1 | Out-File .\AppLocker.csv; .\ExportPolicy-ToExcel.ps1 -AppLockerCSV .\AppLocker.csv 39 | 40 | Generates an Excel worksheet representing AppLocker policy previously generated from ExportPolicy-ToCsv.ps1 output. 41 | 42 | .EXAMPLE 43 | Get-AppLockerPolicy -Local -Xml | Out-File .\AppLocker.xml; .\ExportPolicy-ToExcel.ps1 -AppLockerXML .\AppLocker.xml 44 | 45 | Generates an Excel worksheet representing AppLocker policy exported from a system into an XML file. 46 | 47 | #> 48 | 49 | #TODO: Add option to get AppLocker policy from AD GPO, if/when ExportPolicy-ToCsv.ps1 adds it. 50 | 51 | [CmdletBinding(DefaultParameterSetName = "LocalPolicy")] 52 | param( 53 | # If specified, inspects local AppLocker policy rather than effective policy or an XML file 54 | [parameter(ParameterSetName = "LocalPolicy")] 55 | [switch] 56 | $Local = $false, 57 | 58 | # Optional: path to XML file containing AppLocker policy 59 | [parameter(ParameterSetName = "SavedXML")] 60 | [String] 61 | $AppLockerXML, 62 | 63 | # If specified, uses CSV previously collected instead of running ExportPolicy-ToCsv.ps1 64 | [parameter(ParameterSetName = "SavedCSV")] 65 | [String] 66 | $AppLockerCSV, 67 | 68 | [parameter(ParameterSetName = "SavedXML")] 69 | [parameter(ParameterSetName = "SavedCSV")] 70 | [switch] 71 | $SaveWorkbook 72 | ) 73 | 74 | $rootDir = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path) 75 | # Get configuration settings and global functions from .\Support\Config.ps1) 76 | # Dot-source the config file. 77 | . $rootDir\Support\Config.ps1 78 | 79 | $OutputEncodingPrevious = $OutputEncoding 80 | $OutputEncoding = [System.Text.ASCIIEncoding]::Unicode 81 | 82 | 83 | $tabname = "AppLocker policy" 84 | $filename = $tempfile = $xlFname = [String]::Empty 85 | 86 | $linebreakSeq = "^|^" 87 | 88 | if ($AppLockerCSV.Length -gt 0) { 89 | $filename = $AppLockerCSV 90 | $tabname = [System.IO.Path]::GetFileName($AppLockerCSV) 91 | if ($SaveWorkbook) { 92 | $xlFname = [System.IO.Path]::ChangeExtension($AppLockerCSV, ".xlsx") 93 | } 94 | } 95 | else { 96 | $filename = $tempfile = [System.IO.Path]::GetTempFileName() 97 | 98 | if ($AppLockerXML.Length -gt 0) { 99 | & $ps1_ExportPolicyToCSV -AppLockerPolicyFile $AppLockerXML -linebreakSeq $linebreakSeq | Out-File $tempfile -Encoding unicode 100 | $tabname = [System.IO.Path]::GetFileNameWithoutExtension($AppLockerXML) 101 | if ($SaveWorkbook) { 102 | $xlFname = [System.IO.Path]::ChangeExtension($AppLockerXML, ".xlsx") 103 | } 104 | } 105 | else { 106 | & $ps1_ExportPolicyToCSV -Local:$Local -linebreakSeq $linebreakSeq | Out-File $tempfile -Encoding unicode 107 | if ($Local) { 108 | $tabname = "AppLocker policy - Local" 109 | } 110 | else { 111 | $tabname = "AppLocker policy - Effective" 112 | } 113 | } 114 | } 115 | 116 | if ($xlFname.Length -gt 0) { 117 | # Ensure absolute path 118 | if (!([System.IO.Path]::IsPathRooted($xlFname))) { 119 | $xlFname = [System.IO.Path]::Combine((Get-Location).Path, $xlFname) 120 | } 121 | } 122 | 123 | CreateExcelFromCsvFile $filename $tabname $linebreakSeq $xlFname 124 | 125 | # Delete the temp file 126 | if ($tempfile.Length -gt 0) { Remove-Item $tempfile } 127 | 128 | $OutputEncoding = $OutputEncodingPrevious 129 | -------------------------------------------------------------------------------- /AaronLocker/GPOConfiguration/Clear-GPOAppLockerPolicy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Revert AppLocker policy in GPO to "not configured". 4 | 5 | .PARAMETER GpoName 6 | Name of the group policy object to set the AppLocker policy on. 7 | 8 | .PARAMETER GpoGuid 9 | GUID of the group policy object to set the AppLocker policy on. 10 | #> 11 | [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")] 12 | param( 13 | [Parameter(Mandatory = $true, ParameterSetName = 'GpoName')] 14 | [string] 15 | $GpoName, 16 | 17 | [Parameter(Mandatory = $true, ParameterSetName = 'GpoGUID')] 18 | [guid] 19 | $GpoGUID, 20 | 21 | [string] 22 | $Server, 23 | 24 | # If set, applies enforcing rules. Otherwise, applies auditing rules. 25 | [switch] 26 | $Enforce = $false 27 | ) 28 | 29 | #################################################################################################### 30 | # Ensure the AppLocker assembly is loaded. (Scripts sometimes run into TypeNotFound errors if not.) 31 | #################################################################################################### 32 | [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel") 33 | 34 | if ($GpoGUID) { 35 | $Gpo = Get-GPO -Guid $GpoGUID -ErrorAction "Stop" 36 | } 37 | else { 38 | $Gpo = Get-GPO -Name $GpoName -ErrorAction "Stop" 39 | } 40 | $Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain() 41 | $Server = $Domain.DomainControllers[0].Name 42 | if ($PSCmdlet.ShouldProcess($Gpo.DisplayName, "Clear AppLocker policy")) { 43 | Write-Information -InformationAction "Continue" -MessageData "Clearing AppLocker policy on $($Gpo.DisplayName) in domain $($Domain.Name)" -ForegroundColor Cyan 44 | $AppLockerPolicy = [Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.AppLockerPolicy]::new() 45 | Set-AppLockerPolicy -PolicyObject $AppLockerPolicy -Ldap "LDAP://$Server/$($Gpo.Path)" 46 | } 47 | -------------------------------------------------------------------------------- /AaronLocker/GPOConfiguration/Set-GPOAppLockerPolicy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Set the most-recently generated AppLocker policy on AD Group Policy. 4 | 5 | .DESCRIPTION 6 | Applies the most recent generated "Audit" or "Enforce" rules to a Group Policy object. 7 | Applies the "Audit" rules by default; to apply the "Enforce" rules, use the 8 | -Enforce switch. 9 | 10 | .PARAMETER GpoName 11 | Name of the group policy object to set the AppLocker policy on. 12 | 13 | .PARAMETER GpoGuid 14 | GUID of the group policy object to set the AppLocker policy on. 15 | 16 | .PARAMETER Enforce 17 | If this switch is set, this script applies the "Enforce" rules to Group Policy object. 18 | If this switch is $false, this script applies the "Audit" rules to Group 19 | Policy object. 20 | 21 | #> 22 | [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")] 23 | param( 24 | [Parameter(Mandatory = $true, ParameterSetName = 'GpoName')] 25 | [string] 26 | $GpoName, 27 | 28 | [Parameter(Mandatory = $true, ParameterSetName = 'GpoGUID')] 29 | [guid] 30 | $GpoGUID, 31 | 32 | # If set, applies enforcing rules. Otherwise, applies auditing rules. 33 | [switch] 34 | $Enforce = $false 35 | ) 36 | 37 | $rootDir = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path) + "\.." 38 | 39 | # Dot-source the config file. 40 | . $rootDir\Support\Config.ps1 41 | 42 | if ($Enforce) { 43 | $policyFile = RulesFileEnforceLatest 44 | } 45 | else { 46 | $policyFile = RulesFileAuditLatest 47 | } 48 | 49 | if ($null -eq $policyFile) { 50 | Write-Error "No policy file found" 51 | } 52 | else { 53 | if ($GpoGUID) { 54 | $Gpo = Get-GPO -Guid $GpoGUID -ErrorAction "Stop" 55 | } 56 | else { 57 | $Gpo = Get-GPO -Name $GpoName -ErrorAction "Stop" 58 | } 59 | $Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain() 60 | $Server = $Domain.DomainControllers[0].Name 61 | if ($PSCmdlet.ShouldProcess($Gpo.DisplayName, "Set AppLocker policy using $policyFile")) { 62 | Write-Information -InformationAction "Continue" -MessageData "Applying $policyFile to $($Gpo.DisplayName) in domain $($Domain.Name)" -ForegroundColor Cyan 63 | Set-AppLockerPolicy -XmlPolicy $policyFile -Ldap "LDAP://$Server/$($Gpo.Path)" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /AaronLocker/Generate-EventWorkbook.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Produces a multi-tab Excel workbook containing summary and details of AppLocker events to support advanced analysis. 4 | 5 | .DESCRIPTION 6 | Converts output from the Get-AppLockerEvents.ps1 or Save-WEFEvents.ps1 scripts to a multi-tab Excel workbook supporting numerous views of the data, many including graphs. 7 | Worksheets include: 8 | * Summary tab showing date/time ranges of the reported events and other summary information. 9 | * Numbers of distinct users running files from each high-level location such as user profile, hot/removable, non-default root directories, etc. 10 | * Numbers of distinct users running files from each observed publisher. 11 | * Numbers of distinct users running each observed file (by GenericPath). 12 | * All combinations of publishers/products for signed files in events. 13 | * All combinations of publishers/products and generic file paths ("generic" meaning that user-specific paths are replaced with %LOCALAPPDATA%, %USERPROFILE%, etc., as appropriate). 14 | * Paths of unsigned files, with filename alone, file type, and file hash. 15 | * Files and publishers grouped by user. 16 | * Full details from Get-AppLockerEvents.ps1. 17 | With the -RawEventCounts switch, the workbook adds sheets showing raw event counts for each machine, publisher, and user. 18 | These separate tabs enable quick determination of the files running afoul of AppLocker rules and help quickly determine whether/how to adjust the rules. 19 | 20 | .PARAMETER AppLockerEventsCsvFile 21 | Optional path to CSV file produced by Get-AppLockerEvents.ps1 or Save-WEFEvents.ps1. 22 | If not specified, this script invokes Get-AppLockerEvents.ps1 on the local computer and processes its output. 23 | 24 | .PARAMETER SaveWorkbook 25 | If AppLockerEventsCsvFile is specified and this option is set, the script saves the workbook to the same directory 26 | as the input file and with the same file name but with the default Excel file extension. 27 | 28 | .PARAMETER RawEventCounts 29 | If the -RawEventCounts switch is specified, workbook includes additional worksheets focused on raw event counts per machine, per user, and per publisher. 30 | #> 31 | 32 | [CmdletBinding(DefaultParameterSetName = "GenerateTempCsv")] 33 | param( 34 | # Path to CSV file produced by Get-AppLockerEvents.ps1 35 | [parameter(ParameterSetName = "NamedCsvFile", Mandatory = $true)] 36 | [String] 37 | $AppLockerEventsCsvFile, 38 | 39 | [parameter(ParameterSetName = "NamedCsvFile")] 40 | [switch] 41 | $SaveWorkbook, 42 | 43 | [switch] 44 | $RawEventCounts 45 | ) 46 | 47 | $rootDir = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path) 48 | # Get configuration settings and global functions from .\Support\Config.ps1) 49 | # Dot-source the config file. Contains Excel-generation scripts. 50 | . $rootDir\Support\Config.ps1 51 | 52 | $OutputEncodingPrevious = $OutputEncoding 53 | $OutputEncoding = [System.Text.ASCIIEncoding]::Unicode 54 | 55 | $tempfile = [string]::Empty 56 | 57 | if ($AppLockerEventsCsvFile) { 58 | if (!(Test-Path($AppLockerEventsCsvFile))) { 59 | Write-Warning "File not found: $AppLockerEventsCsvFile" 60 | return 61 | } 62 | 63 | # Get absolute path to input file. (Note that [System.IO.Path]::GetFullName doesn't do this...) 64 | $AppLockerEventsCsvFileFullPath = $AppLockerEventsCsvFile 65 | if (!([System.IO.Path]::IsPathRooted($AppLockerEventsCsvFile))) { 66 | $AppLockerEventsCsvFileFullPath = [System.IO.Path]::Combine((Get-Location).Path, $AppLockerEventsCsvFile) 67 | } 68 | $dataSourceName = [System.IO.Path]::GetFileName($AppLockerEventsCsvFile) 69 | } 70 | else { 71 | $tempfile = [System.IO.Path]::GetTempFileName() 72 | $AppLockerEventsCsvFileFullPath = $AppLockerEventsCsvFile = $tempfile 73 | $dataSourceName = "(Get-AppLockerEvents.ps1 output)" 74 | & $rootDir\Get-AppLockerEvents.ps1 | Out-File $tempfile -Encoding unicode 75 | } 76 | 77 | 78 | Write-Verbose -Message "Reading data from $AppLockerEventsCsvFile" 79 | $csvFull = @(Get-Content $AppLockerEventsCsvFile) 80 | $dataUnfiltered = @($csvFull | ConvertFrom-Csv -Delimiter "`t") 81 | $dataFiltered = @($dataUnfiltered | Where-Object { $_.EventType -ne $sFiltered }) 82 | $eventsSigned = @($dataFiltered | Where-Object { $_.PublisherName -ne $sUnsigned -and $_.PublisherName -ne $sNoPublisher }) 83 | $eventsUnsigned = @($dataFiltered | Where-Object { $_.PublisherName -eq $sUnsigned -or $_.PublisherName -eq $sNoPublisher }) 84 | 85 | if ($dataUnfiltered.Length -eq 0) { 86 | Write-Warning "No data. Exiting." 87 | return 88 | } 89 | 90 | $nEvents = $dataFiltered.Length 91 | $nSignedEvents = $eventsSigned.Length 92 | $nUnsignedEvents = $eventsUnsigned.Length 93 | 94 | if (CreateExcelApplication) { 95 | # Array to set sort order descending on Count and then ascending on Name 96 | $CountDescNameAsc = @( @{ Expression = "Count"; Descending = $true }, @{ Expression = "Name"; Descending = $false } ) 97 | 98 | # Lines of text for the summary page 99 | $tabname = "Summary" 100 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 101 | [System.Collections.ArrayList]$text = @() 102 | $text.Add( "Summary information" ) | Out-Null 103 | $text.Add( "" ) | Out-Null 104 | $text.Add( "Data source:`t" + $dataSourceName ) | Out-Null 105 | if ($nEvents -gt 0) { 106 | $dtsort = ($dataFiltered.EventTime | Sort-Object); 107 | $dtFirst = ([datetime]($dtsort[0])).ToString() 108 | $dtLast = ([datetime]($dtsort[$dtsort.Length - 1])).ToString() 109 | } 110 | else { 111 | $dtFirst = $dtLast = "N/A" 112 | } 113 | $text.Add( "First event:`t" + $dtFirst ) | Out-Null 114 | $text.Add( "Last event:`t" + $dtLast ) | Out-Null 115 | $text.Add( "" ) | Out-Null 116 | $text.Add( "Number of events:`t" + $nEvents.ToString() ) | Out-Null 117 | $text.Add( "Number of signed-file events:`t" + $nSignedEvents.ToString() ) | Out-Null 118 | $text.Add( "Number of unsigned-file events:`t" + $nUnsignedEvents.ToString() ) | Out-Null 119 | # Make sure the result of the pipe is an array, even if only one item. 120 | $text.Add( "Number of machines reporting events:`t" + ( @($dataUnfiltered.MachineName | Group-Object)).Count.ToString() ) | Out-Null 121 | $text.Add( "Number of users reporting events:`t" + ( @($dataFiltered.UserName | Group-Object)).Count.ToString() ) | Out-Null 122 | AddWorksheetFromText -text $text -tabname $tabname 123 | 124 | if ($nEvents -gt 0) { 125 | # Users per location: 126 | $tabname = "# Users per Location" 127 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 128 | $csv = @($dataFiltered | Select-Object Location, UserName -Unique | Group-Object Location | Select-Object Name, Count | Sort-Object -Property $CountDescNameAsc | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 129 | # Change the headers 130 | $csv[0] = "Location" + "`t" + "# of distinct users" 131 | AddWorksheetFromCsvData -csv $csv -tabname $tabname -CrLfEncoded "" -AddChart 132 | } 133 | 134 | if ($nEvents -gt 0) { 135 | # Users per publisher: 136 | $tabname = "# Users per Publisher" 137 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 138 | $csv = @($dataFiltered | Select-Object PublisherName, UserName -Unique | Group-Object PublisherName | Select-Object Name, Count | Sort-Object -Property $CountDescNameAsc | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 139 | # Change the headers 140 | $csv[0] = "PublisherName" + "`t" + "# of distinct users" 141 | AddWorksheetFromCsvData -csv $csv -tabname $tabname -CrLfEncoded "" -AddChart 142 | } 143 | 144 | if ($nSignedEvents -gt 0) { 145 | # Publisher/product combinations: 146 | $tabname = "Publisher-product combinations" 147 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 148 | $csv = @($eventsSigned | Select-Object PublisherName, ProductName | Sort-Object PublisherName, ProductName -Unique | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 149 | AddWorksheetFromCsvData -csv $csv -tabname $tabname 150 | } 151 | 152 | if ($nEvents -gt 0) { 153 | # Users per file: 154 | $tabname = "# Users per File" 155 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 156 | $csv = @($dataFiltered | Select-Object Location, GenericPath, UserName -Unique | Group-Object Location, GenericPath | Select-Object Name, Count | Sort-Object -Property $CountDescNameAsc | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 157 | # Change the headers 158 | $csv[0] = "Location, GenericPath" + "`t" + "# of distinct users" 159 | AddWorksheetFromCsvData -csv $csv -tabname $tabname -CrLfEncoded "" -AddChart 160 | } 161 | 162 | if ($nSignedEvents -gt 0) { 163 | # Publisher/product/file combinations: 164 | $tabname = "Signed file info" 165 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 166 | $csv = @($eventsSigned | Select-Object PublisherName, ProductName, Location, GenericPath, FileName, FileType | Sort-Object PublisherName, ProductName, Location, GenericPath -Unique | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 167 | AddWorksheetFromCsvData -csv $csv -tabname $tabname 168 | } 169 | 170 | if ($nUnsignedEvents -gt 0) { 171 | # Analysis of unsigned files: 172 | $tabname = "Unsigned file info" 173 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 174 | $csv = @($eventsUnsigned | Select-Object Location, GenericPath, FileName, FileType, Hash | Sort-Object Location, GenericPath -Unique | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 175 | AddWorksheetFromCsvData -csv $csv -tabname $tabname 176 | } 177 | 178 | if ($nEvents -gt 0) { 179 | # Files by user 180 | $tabname = "Files by User" 181 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 182 | $csv = @($dataFiltered | Select-Object UserName, Location, GenericPath, FileType, PublisherName, ProductName | Sort-Object UserName, Location, GenericPath -Unique | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 183 | AddWorksheetFromCsvData -csv $csv -tabname $tabname 184 | } 185 | 186 | if ($nEvents -gt 0) { 187 | # Files by user (details) 188 | $tabname = "Files by User (details)" 189 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 190 | $csv = @($dataFiltered | Select-Object UserName, MachineName, EventTimeXL, FileType, GenericPath, PublisherName, ProductName | Sort-Object UserName, MachineName, EventTimeXL, FileType, GenericPath -Unique | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 191 | AddWorksheetFromCsvData -csv $csv -tabname $tabname 192 | } 193 | 194 | # All event data 195 | AddWorksheetFromCsvFile -filename $AppLockerEventsCsvFileFullPath -tabname "Full details" 196 | 197 | if ($RawEventCounts) { 198 | # Events per machine: 199 | $tabname = "# Events per Machine" 200 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 201 | $csv = @($dataFiltered.MachineName | Group-Object | Select-Object Name, Count | Sort-Object -Property $CountDescNameAsc | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 202 | if ($csv.Length -eq 0) { $csv = @("header") } # No events - insert dummy header row, replaced in a moment 203 | $csv += @($dataUnfiltered | Where-Object { $_.EventType -eq $sFiltered } | ForEach-Object { $_.MachineName + "`t0" }) 204 | # Change the headers 205 | if ($csv.Length -gt 0 ) { $csv[0] = "MachineName" + "`t" + "Event count" } 206 | AddWorksheetFromCsvData -csv $csv -tabname $tabname -CrLfEncoded "" -AddChart 207 | 208 | if ($nEvents -gt 0) { 209 | # Counts of each publisher: 210 | $tabname = "# Events per Publisher" 211 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 212 | $csv = @($dataFiltered.PublisherName | Group-Object | Select-Object Name, Count | Sort-Object -Property $CountDescNameAsc | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 213 | # Change the headers 214 | if ($csv.Length -gt 0 ) { $csv[0] = "PublisherName" + "`t" + "Events" } 215 | AddWorksheetFromCsvData -csv $csv -tabname $tabname -AddChart 216 | } 217 | 218 | if ($nEvents -gt 0) { 219 | # Events per user: 220 | $tabname = "# Events per User" 221 | Write-Verbose -Message "Gathering data for `"$tabname`"..." 222 | $csv = @($dataFiltered.UserName | Group-Object | Select-Object Name, Count | Sort-Object -Property $CountDescNameAsc | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation) 223 | # Change the headers 224 | $csv[0] = "UserName" + "`t" + "Events" 225 | AddWorksheetFromCsvData -csv $csv -tabname $tabname -CrLfEncoded "" -AddChart 226 | } 227 | } 228 | 229 | SelectFirstWorksheet 230 | 231 | if ($SaveWorkbook) { 232 | $xlFname = [System.IO.Path]::ChangeExtension($AppLockerEventsCsvFileFullPath, ".xlsx") 233 | SaveWorkbook -filename $xlFname 234 | } 235 | 236 | ReleaseExcelApplication 237 | } 238 | 239 | if ($tempfile.Length -gt 0) { 240 | Remove-Item $tempfile 241 | } 242 | 243 | $OutputEncoding = $OutputEncodingPrevious 244 | 245 | 246 | -------------------------------------------------------------------------------- /AaronLocker/LocalConfiguration/ApplyPolicyToLocalGPO.ps1: -------------------------------------------------------------------------------- 1 |  2 | <# 3 | .SYNOPSIS 4 | Applies the most-recently generated AppLocker rules to local Group Policy. 5 | 6 | .DESCRIPTION 7 | Applies the most recent generated "Audit" or "Enforce" rules to local Group Policy. 8 | Applies the "Enforce" rules by default; to apply the "Audit" rules, use the -AuditOnly switch. 9 | Requires administrative rights. 10 | 11 | .PARAMETER AuditOnly 12 | If this switch is set, this script applies the "Audit" rules to local Group Policy. 13 | If this switch is $false, this script applies the "Enforce" rules to local Group Policy. 14 | 15 | #> 16 | 17 | param( 18 | # If set, applies auditing rules. Otherwise, applies enforcing rules. 19 | [switch] $AuditOnly = $false 20 | ) 21 | 22 | $rootDir = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path) + "\.." 23 | 24 | # Dot-source the config file. 25 | . $rootDir\Support\Config.ps1 26 | 27 | if ($AuditOnly) { 28 | $policyFile = RulesFileAuditLatest 29 | } 30 | else { 31 | $policyFile = RulesFileEnforceLatest 32 | } 33 | 34 | if ($null -eq $policyFile) { 35 | Write-Error "No policy file found" 36 | } 37 | else { 38 | Write-Verbose -Message "Applying $policyFile" 39 | Set-AppLockerPolicy -XmlPolicy $policyFile 40 | } 41 | 42 | -------------------------------------------------------------------------------- /AaronLocker/LocalConfiguration/ClearApplockerLogs.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Clears events from local AppLocker event logs. 4 | Requires administrative rights. 5 | #> 6 | 7 | wevtutil.exe clear-log "Microsoft-Windows-AppLocker/EXE and DLL" 8 | wevtutil.exe clear-log "Microsoft-Windows-AppLocker/MSI and Script" 9 | wevtutil.exe clear-log "Microsoft-Windows-AppLocker/Packaged app-Deployment" 10 | wevtutil.exe clear-log "Microsoft-Windows-AppLocker/Packaged app-Execution" 11 | -------------------------------------------------------------------------------- /AaronLocker/LocalConfiguration/ClearLocalAppLockerPolicy.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Revert local AppLocker policy to "not configured". 4 | Requires administrative rights. 5 | #> 6 | 7 | #################################################################################################### 8 | # Ensure the AppLocker assembly is loaded. (Scripts sometimes run into TypeNotFound errors if not.) 9 | #################################################################################################### 10 | [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel") 11 | 12 | Set-AppLockerPolicy -PolicyObject ([Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.AppLockerPolicy]::new()) 13 | -------------------------------------------------------------------------------- /AaronLocker/LocalConfiguration/ConfigureForAppLocker.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Basic one-time single-computer configuration changes for AppLocker. 4 | Requires administrative rights. 5 | 6 | .DESCRIPTION 7 | Configures the Application Identity service (appidsvc) for automatic start 8 | Starts the Application Identity service 9 | Sets the maximum log size for each of the AppLocker event logs to 1GB. 10 | 11 | #> 12 | 13 | # Configure AppIdSvc for Automatic start 14 | SC.EXE config AppIdSvc start= auto 15 | 16 | # Start the service if not already running 17 | SC.exe start appidsvc 18 | 19 | # Set the primary AppLocker event log sizes to 1GB 20 | 21 | $logName = 'Microsoft-Windows-AppLocker/EXE and DLL' 22 | $log = New-Object System.Diagnostics.Eventing.Reader.EventLogConfiguration $logName 23 | $log.MaximumSizeInBytes = (1024 * 1024 * 1024) 24 | $log.SaveChanges() 25 | 26 | $logName = 'Microsoft-Windows-AppLocker/MSI and Script' 27 | $log = New-Object System.Diagnostics.Eventing.Reader.EventLogConfiguration $logName 28 | $log.MaximumSizeInBytes = (1024 * 1024 * 1024) 29 | $log.SaveChanges() 30 | 31 | #These event logs don't exist on Windows 7: ignore any errors. 32 | try { 33 | $logName = 'Microsoft-Windows-AppLocker/Packaged app-Deployment' 34 | $log = New-Object System.Diagnostics.Eventing.Reader.EventLogConfiguration $logName 35 | $log.MaximumSizeInBytes = (1024 * 1024 * 1024) 36 | $log.SaveChanges() 37 | 38 | $logName = 'Microsoft-Windows-AppLocker/Packaged app-Execution' 39 | $log = New-Object System.Diagnostics.Eventing.Reader.EventLogConfiguration $logName 40 | $log.MaximumSizeInBytes = (1024 * 1024 * 1024) 41 | $log.SaveChanges() 42 | } 43 | catch { 44 | Write-Information -MessageData "Error saving config for packaged apps." -InformationAction "Continue" 45 | } 46 | -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/AppLockerRules-Microsoft OneDrive Hash Rules.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/MergeRules-Static/AppLockerRules-Microsoft OneDrive Hash Rules.xml -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/AppLockerRules-Microsoft OneDrive Publisher Rules.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/MergeRules-Static/AppLockerRules-Microsoft OneDrive Publisher Rules.xml -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/AppLockerRules-Microsoft Teams Hash Rules.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/MergeRules-Static/AppLockerRules-Microsoft Teams Hash Rules.xml -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/AppLockerRules-Microsoft Teams Publisher Rules.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/MergeRules-Static/AppLockerRules-Microsoft Teams Publisher Rules.xml -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/AppLockerRules-Teams Update Publisher Rules.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/MergeRules-Static/AppLockerRules-Teams Update Publisher Rules.xml -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/Deny-DeployAgent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/Deny-OldBginfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/Deny-WindowsTemp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/OneDrive-InitialInstall-Win10v1607.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/MergeRules-Static/OneDrive-InitialInstall-Win10v1607.xml -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/OneDrive-InitialInstall-Win10v1803.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/MergeRules-Static/OneDrive-InitialInstall-Win10v1803.xml -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/OneDrive-InitialInstall-Win10v1809.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/MergeRules-Static/OneDrive-InitialInstall-Win10v1809.xml -------------------------------------------------------------------------------- /AaronLocker/MergeRules-Static/OneDrive-SharePoint.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/MergeRules-Static/OneDrive-SharePoint.xml -------------------------------------------------------------------------------- /AaronLocker/Save-WEFEvents.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Captures forwarded events to a CSV file with the timestamp embedded in the file name. 4 | Intended to be executed on a Windows Event Collector server. 5 | 6 | .PARAMETER rootdir 7 | Directory in which to create the CSV file. 8 | 9 | .PARAMETER daysBack 10 | Number of days ago from which to start retrieving data. E.g., "-daysBack 5" pulls data from the last 5 days. 11 | 12 | .PARAMETER label 13 | Name to insert into the filename (can be useful to distinguish sources). 14 | 15 | .PARAMETER EventLogName 16 | WEC log name to use instead of ForwardedEvents. 17 | 18 | #> 19 | 20 | param( 21 | [parameter(Mandatory = $false)] 22 | [string] 23 | $rootdir, 24 | 25 | [parameter(Mandatory = $false)] 26 | [int] 27 | $daysBack = 0, 28 | 29 | [parameter(Mandatory = $false)] 30 | [string] 31 | $label = "", 32 | 33 | [parameter(Mandatory = $false)] 34 | [string] 35 | $EventLogName = "ForwardedEvents" 36 | 37 | ) 38 | 39 | if (!$rootdir) { $rootdir = "." } 40 | 41 | $strTimestamp = [datetime]::Now.ToString("yyyyMMdd-HHmm") 42 | if ($label.Length -eq 0) { 43 | $filenameFull = [System.IO.Path]::Combine($rootdir, "ForwardedEvents-" + $strTimestamp + ".csv") 44 | } 45 | else { 46 | $filenameFull = [System.IO.Path]::Combine($rootdir, "ForwardedEvents-" + $label + "-" + $strTimestamp + ".csv") 47 | } 48 | 49 | $OutputEncoding = [System.Text.ASCIIEncoding]::Unicode 50 | 51 | if ($daysBack -gt 0) { 52 | $csvFull = .\Get-AppLockerEvents.ps1 -EventLogNames @($EventLogName) -NoAutoNGEN -FromDateTime ([datetime]::Now.AddDays(-$daysBack)) 53 | } 54 | else { 55 | $csvFull = .\Get-AppLockerEvents.ps1 -EventLogNames @($EventLogName) -NoAutoNGEN 56 | } 57 | 58 | $csvFull | Out-File -Encoding unicode $filenameFull 59 | Write-Verbose -Message "Events written to $filenameFull" 60 | 61 | -------------------------------------------------------------------------------- /AaronLocker/Scan-Directories.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Scan directories to identify files that might need additional AppLocker rules. 4 | 5 | .DESCRIPTION 6 | Produces tab-delimited CSV or an Excel worksheet listing files in various directories that might need additional AppLocker rules to allow them to execute. 7 | Optionally, the script can list non-standard directories in the %SystemDrive% root directory. These directories might require additional scanning. 8 | 9 | The script searches specified directory hierarchies for MSIs and scripts (according to file extension), and EXE/DLL files regardless of extension. That is, a file can be identified as a Portable Executable (PE) file (typically an EXE or DLL) even if it has a non-standard extension or no extension. 10 | 11 | Output columns include: 12 | * IsSafeDir - indicates whether the file's parent directory is "safe" (not user-writable) or "unsafe" (user-writable); 13 | * File type - EXE/DLL, MSI, or Script; 14 | * File extension - the file's extension; 15 | * File name - the file name without path information; 16 | * File path - Full path to the file; 17 | * Parent directory - The file's parent directory; 18 | * Publisher name, Product name, Binary name, Version - signature and version resource information that can be used in publisher rules; 19 | * Hash - the file's hash; 20 | * CreationTime, LastAccessTime, LastWriteTime - the file's timestamps according to the file system; 21 | * File size. 22 | 23 | Directories that can be searched: 24 | * WritableWindir - writable subdirectories of the %windir% directory, based on results of the last scan performed by Create-Policies.ps1; 25 | * WritablePF - writable subdirectories of the %ProgramFiles% directories, based on results of the last scan performed by Create-Policies.ps1; 26 | * SearchProgramData - the %ProgramData% directory hierarchy; 27 | * SearchOneUserProfile - the current user's profile directory; 28 | * SearchAllUserProfiles - the root directory of user profiles (C:\Users); 29 | * DirsToSearch - one or more caller-specified, comma-separated directory paths. 30 | 31 | Results can be imported into Microsoft Excel and analyzed. 32 | 33 | Note that results from this script do not necessarily require that rules be created: 34 | this is just an indicator about files that *might* need rules, if the files need to be allowed. 35 | 36 | .PARAMETER WritableWindir 37 | If this switch is specified, searches user-writable subdirectories under %windir% according to results of the last scan performed by Create-Policies.ps1. 38 | 39 | .PARAMETER WritablePF 40 | If this switch is specified, searches user-writable subdirectories under the %ProgramFiles% directories according to results of the last scan performed by Create-Policies.ps1. 41 | 42 | .PARAMETER SearchProgramData 43 | If this switch is specified, searches the %ProgramData% directory hierarchy, which can contain a mix of "safe" and "unsafe" directories. 44 | 45 | .PARAMETER SearchOneUserProfile 46 | If this switch is specified, searches the user's profile directory. 47 | 48 | .PARAMETER SearchAllUserProfiles 49 | If this switch is specified, searches from the root directory of all users' profiles (C:\Users) 50 | 51 | .PARAMETER SearchNonDefaultRootDirs 52 | If this switch is specified, search all non-standard directories in the %SystemDrive% root directory. These directories often contain LOB applications. 53 | 54 | .PARAMETER DirsToSearch 55 | Specifies one or more directories to search. 56 | 57 | .PARAMETER NoPEFiles 58 | If this switch is specified, does not search for Portable Executable files (EXE/DLL files) 59 | 60 | .PARAMETER NoScripts 61 | If this switch is specified, does not search for script files. 62 | 63 | .PARAMETER NoMSIs 64 | If this switch is specified, does not search for MSI files. 65 | 66 | .PARAMETER JS 67 | If this switch is specified, report .js files as script files; otherwise, skip .js files entirely. 68 | 69 | .PARAMETER DirectoryNamesOnly 70 | If this switch is specified, reports the names and "safety" of directories that contain files of interest but no file information. 71 | 72 | .PARAMETER Excel 73 | If this switch is specified, outputs to formatted Excel worksheet instead of to pipeline 74 | 75 | .PARAMETER GridView 76 | If this switch is specified, outputs to PowerShell GridView 77 | 78 | .PARAMETER FindNonDefaultRootDirs 79 | If this switch is specified, identifies non-standard directories in the %SystemDrive% root directory. These directories often contain LOB applications. 80 | This switch cannot be used with any other options 81 | 82 | .PARAMETER Verbose 83 | Shows progress through directory scans, and other verbose diagnostics. 84 | 85 | 86 | .EXAMPLE 87 | Scan-Directories.ps1 -SearchOneUserProfile -DirsToSearch H:\ 88 | 89 | Searches the user's profile directory and the H: drive. 90 | 91 | #> 92 | 93 | #TODO: Need automation to turn selected results into rules. 94 | 95 | param( 96 | [parameter(ParameterSetName = "SearchDirectories")] 97 | [switch] 98 | $WritableWindir = $false, 99 | 100 | [parameter(ParameterSetName = "SearchDirectories")] 101 | [switch] 102 | $WritablePF = $false, 103 | 104 | [parameter(ParameterSetName = "SearchDirectories")] 105 | [switch] 106 | $SearchProgramData = $false, 107 | 108 | [parameter(ParameterSetName = "SearchDirectories")] 109 | [switch] 110 | $SearchOneUserProfile = $false, 111 | 112 | [parameter(ParameterSetName = "SearchDirectories")] 113 | [switch] 114 | $SearchAllUserProfiles = $false, 115 | 116 | [parameter(ParameterSetName = "SearchDirectories")] 117 | [switch] 118 | $SearchNonDefaultRootDirs = $false, 119 | 120 | [parameter(ParameterSetName = "SearchDirectories", Mandatory = $false)] 121 | [String[]] 122 | $DirsToSearch, 123 | 124 | [parameter(ParameterSetName = "SearchDirectories")] 125 | [switch] 126 | $NoPEFiles = $false, 127 | 128 | [parameter(ParameterSetName = "SearchDirectories")] 129 | [switch] 130 | $NoScripts = $false, 131 | 132 | [parameter(ParameterSetName = "SearchDirectories")] 133 | [switch] 134 | $NoMSIs = $false, 135 | 136 | [parameter(ParameterSetName = "SearchDirectories")] 137 | [switch] 138 | $JS = $false, 139 | 140 | [parameter(ParameterSetName = "SearchDirectories")] 141 | [switch] 142 | $DirectoryNamesOnly = $false, 143 | 144 | [parameter(ParameterSetName = "SearchDirectories")] 145 | [switch] 146 | $Excel = $false, 147 | 148 | [parameter(ParameterSetName = "SearchDirectories")] 149 | [switch] 150 | $GridView = $false, 151 | 152 | [parameter(ParameterSetName = "NonDefaultRootDirs")] 153 | [switch] 154 | $FindNonDefaultRootDirs = $false 155 | ) 156 | 157 | ### ====================================================================== 158 | 159 | Set-StrictMode -Version Latest 160 | 161 | 162 | ### ====================================================================== 163 | ### The FindNonDefaultRootDirs is a standalone option that cannot be used with other switches. 164 | ### It searches the SystemDrive root directory and enumerates non-default directory names. 165 | ### SearchNonDefaultRootDirs also uses that information. 166 | $nondefaultRootDirs = $null 167 | if ($FindNonDefaultRootDirs -or $SearchNonDefaultRootDirs) { 168 | $defaultRootDirs = 169 | '$Recycle.Bin', 170 | 'Config.Msi', 171 | 'MSOCache', 172 | 'MSOTraceLite', 173 | 'OneDriveTemp', 174 | 'PerfLogs', 175 | 'Program Files', 176 | 'Program Files (x86)', 177 | 'ProgramData', 178 | 'Recovery', 179 | 'System Volume Information', 180 | 'Users', 181 | 'Windows', 182 | 'Windows.old' 183 | 184 | # Enumerate root-level directories whether hidden or not, but exclude junctions and symlinks. 185 | # Output the ones that don't exist in a default Windows installation. 186 | $nondefaultRootDirs = (Get-ChildItem -Directory -Force ($env:SystemDrive + "\") | 187 | Where-Object { !$_.Attributes.HasFlag([System.IO.FileAttributes]::ReparsePoint) -and !($_ -in $defaultRootDirs) }) 188 | 189 | if ($FindNonDefaultRootDirs) { 190 | $nondefaultRootDirs | ForEach-Object { $_.FullName } 191 | return 192 | } 193 | } 194 | 195 | 196 | ### ====================================================================== 197 | 198 | $rootDir = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path) 199 | # Dot-source the config file. 200 | . $rootDir\Support\Config.ps1 201 | 202 | # Define some constants 203 | Set-Variable UnsafeDir -Option Constant -Value "UnsafeDir" 204 | Set-Variable SafeDir -Option Constant -Value "SafeDir" 205 | Set-Variable UnknownDir -Option Constant -Value "UnknownDir" 206 | 207 | 208 | $scriptExtensions = 209 | ".bat", 210 | ".cmd", 211 | ".vbs", 212 | ".wsf", 213 | ".wsh", 214 | ".ps1" 215 | # Include .js files only if specifically requested. Too many false positives otherwise; AppLocker restrictions applied only within Windows Script Host. 216 | if ($JS) { 217 | $scriptExtensions += ".js" 218 | } 219 | $MsiExtensions = 220 | ".msi", 221 | ".msp", 222 | ".mst" 223 | 224 | # Hashtable: key is path to inspect; value is indicator whether safe/unsafe 225 | $dirsToInspect = @{} 226 | 227 | # Writable directories under \Windows; known to be unsafe paths 228 | if ($WritableWindir) { 229 | if (!(Test-Path($windirTxt))) { 230 | Write-Warning "$windirTxt does not exist yet. Run Create-Policies.ps1." 231 | } 232 | else { 233 | Get-Content $windirTxt | ForEach-Object { 234 | $dirsToInspect.Add($_, $UnsafeDir) 235 | } 236 | } 237 | } 238 | 239 | # Writable directories under ProgramFiles; known to be unsafe paths 240 | if ($WritablePF) { 241 | if (!(Test-Path($PfTxt))) { 242 | Write-Warning "$PfTxt does not exist yet. Run Create-Policies.ps1." 243 | } 244 | elseif (!(Test-Path($Pf86Txt))) { 245 | Write-Warning "$Pf86Txt does not exist yet. Run Create-Policies.ps1." 246 | } 247 | else { 248 | Get-Content $PfTxt, $Pf86Txt | ForEach-Object { 249 | $dirsToInspect.Add($_, $UnsafeDir) 250 | } 251 | } 252 | } 253 | 254 | if ($SearchProgramData) { 255 | # Probably a mix of safe and unsafe paths 256 | $dirsToInspect.Add($env:ProgramData, $UnknownDir) 257 | } 258 | 259 | if ($SearchOneUserProfile) { 260 | #Assume all unsafe paths 261 | $dirsToInspect.Add($env:USERPROFILE, $UnsafeDir) 262 | } 263 | 264 | if ($SearchAllUserProfiles) { 265 | #Assume all unsafe paths 266 | # No special folder or environment variable available. Get user-profiles' root directory from the parent directory of the "all users" profile directory 267 | $UsersRoot = [System.IO.Path]::GetDirectoryName($env:PUBLIC) 268 | # Skip app-compat junctions (most disallow FILE_LIST_DIRECTORY) 269 | # Skip symlinks -- "All Users" is a symlinkd for \ProgramData but unlike most app-compat junctions it can be listed/traversed. 270 | # This code prevents that. 271 | Get-ChildItem -Force -Directory $UsersRoot | Where-Object { !$_.Attributes.HasFlag([System.IO.FileAttributes]::ReparsePoint) } | ForEach-Object { 272 | $dirsToInspect.Add($_.FullName, $UnsafeDir) 273 | } 274 | } 275 | 276 | if ($SearchNonDefaultRootDirs) { 277 | $nondefaultRootDirs | ForEach-Object { $dirsToInspect.Add($_.FullName, $UnknownDir) } 278 | } 279 | 280 | if ($DirsToSearch) { 281 | $DirsToSearch | ForEach-Object { $dirsToInspect.Add($_, $UnknownDir) } 282 | } 283 | 284 | [System.Collections.ArrayList]$csv = @() 285 | 286 | # Output column headers 287 | if ($DirectoryNamesOnly) { 288 | $csv.Add( 289 | "IsSafeDir" + "`t" + 290 | "Parent directory") | Out-Null 291 | } 292 | else { 293 | $csv.Add( 294 | "IsSafeDir" + "`t" + 295 | "File type" + "`t" + 296 | "File extension" + "`t" + 297 | "File name" + "`t" + 298 | "File path" + "`t" + 299 | "Parent directory" + "`t" + 300 | "Publisher name" + "`t" + 301 | "Product name" + "`t" + 302 | "Binary name" + "`t" + 303 | "Version" + "`t" + 304 | "Hash" + "`t" + 305 | "CreationTime" + "`t" + 306 | "LastAccessTime" + "`t" + 307 | "LastWriteTime" + "`t" + 308 | "File size" ) | Out-Null 309 | } 310 | 311 | 312 | function InspectFiles([string]$directory, [string]$safety, [ref] [string[]]$writableDirs) { 313 | $doNoMore = $false 314 | 315 | # Don't waste cycles looking at file types that are not of interest. (A .txt should never be a PE...) 316 | Get-ChildItem -File $directory -Force -ErrorAction SilentlyContinue -PipelineVariable file | 317 | Where-Object { $file.Extension -notin $NeverExecutableExts } | 318 | ForEach-Object { 319 | 320 | # Work around Get-AppLockerFileInformation bug that vomits on zero-length input files 321 | if ($_.Length -gt 0 -and !$doNoMore) { 322 | $filetype = $null 323 | if ((!($NoScripts)) -and ($file.Extension -in $scriptExtensions)) { 324 | $filetype = "Script" 325 | } 326 | elseif ((!($NoMSIs)) -and ($file.Extension -in $MsiExtensions)) { 327 | $filetype = "MSI" 328 | } 329 | # Don't waste cycles inspecting .js files here if they haven't been evaluated. 330 | elseif (!($NoPEFiles) -and ($file.Extension -ne ".js")) { 331 | $filetype = IsWin32Executable($file.FullName) 332 | } 333 | 334 | # Output 335 | if ($null -ne $filetype) { 336 | $fullname = $file.FullName 337 | $fileext = $file.Extension 338 | $filename = $file.Name 339 | $parentDir = [System.IO.Path]::GetDirectoryName($fullname) 340 | $pubName = $prodName = $binName = $binVer = [String]::Empty 341 | $alfi = Get-AppLockerFileInformation $file.FullName -ErrorAction SilentlyContinue -ErrorVariable alfiErr 342 | # Diagnostics. Seeing sharing violations on some operations 343 | if ($alfiErr.Count -gt 0) { 344 | Write-Information -InformationAction "Continue" -MessageData ($file.FullName + "`tLength = " + $file.Length.ToString()) -ForegroundColor Yellow -BackgroundColor Black 345 | $alfiErr | ForEach-Object { Write-Error -Message $_.Exception.Message } 346 | } 347 | if ($null -ne $alfi) { 348 | $pub = $alfi.Publisher 349 | if ($null -ne $pub) { 350 | $pubName = $pub.PublisherName 351 | $prodName = $pub.ProductName 352 | $binName = $pub.BinaryName 353 | $binVer = $pub.BinaryVersion 354 | } 355 | $hash = $alfi.Hash.HashDataString 356 | } 357 | $safetyOut = $safety 358 | if ($safety -eq $UnknownDir) { 359 | #$dbgInfo = $fullname + "`t" + $parentDir 360 | if ($parentDir -in $writableDirs.Value) { 361 | #$dbgInfo = $UnsafeDir + "`t" + $dbgInfo 362 | $safetyOut = $UnsafeDir 363 | } 364 | else { 365 | #$dbgInfo = ($SafeDir + "`t" + $dbgInfo) 366 | $safetyOut = $SafeDir 367 | } 368 | #$dbgInfo 369 | } 370 | 371 | if ($DirectoryNamesOnly) { 372 | $safetyOut + "`t" + 373 | $parentDir 374 | 375 | # Found one file - don't need to continue inspection of files in this directory 376 | $doNoMore = $true 377 | } 378 | else { 379 | $safetyOut + "`t" + 380 | $filetype + "`t" + 381 | $fileext + "`t" + 382 | $filename + "`t" + 383 | $fullname + "`t" + 384 | $parentDir + "`t" + 385 | $pubName + "`t" + 386 | $prodName + "`t" + 387 | $binName + "`t" + 388 | $binVer + "`t" + 389 | $hash + "`t" + 390 | $file.CreationTime + "`t" + 391 | $file.LastAccessTime + "`t" + 392 | $file.LastWriteTime + "`t" + 393 | $file.Length 394 | } 395 | } 396 | } 397 | } 398 | } 399 | 400 | function InspectDirectories([string]$directory, [string]$safety, [ref][string[]]$writableDirs) { 401 | InspectFiles $directory $safety $writableDirs 402 | 403 | Get-ChildItem -Directory $directory -Force -ErrorAction SilentlyContinue | ForEach-Object { 404 | $subdir = $_ 405 | # Decide here whether to recurse into the subdirectory: 406 | # * Skip junctions and symlinks (typically app-compat junctions). 407 | # * Can add criteria here to skip browser caches, etc. 408 | if (!$subdir.Attributes.HasFlag([System.IO.FileAttributes]::ReparsePoint)) { 409 | Write-Verbose ("... subdir " + $subdir.FullName) 410 | InspectDirectories $subdir.FullName $safety $writableDirs 411 | } 412 | else { 413 | Write-Verbose ("SKIPPING " + $subdir.FullName) 414 | } 415 | } 416 | } 417 | 418 | 419 | # Scanning requires that AccessChk.exe be available. 420 | # If accesschk.exe is in the rootdir, temporarily add the rootdir to the path. 421 | # (Previous implementation invoked Get-Command to see whether accesschk.exe was in the path, and only if that failed looked for 422 | # accesschk.exe in the rootdir. However, there was no good way to keep Get-Command from displaying a "Suggestion" message in that 423 | # scenario.) 424 | # Variable for restoring original Path, if necessary. 425 | $origPath = "" 426 | # Check for accesschk.exe in the rootdir. 427 | if (Test-Path -Path $rootDir\AccessChk.exe) { 428 | # Found it in this script's directory. Temporarily prepend it to the path. 429 | $origPath = $env:Path 430 | $env:Path = "$rootDir;" + $origPath 431 | } 432 | 433 | # Exclude known admins from analysis 434 | [System.Collections.ArrayList]$knownAdmins = @() 435 | $knownAdmins.AddRange( @(& $ps1_KnownAdmins) ) 436 | 437 | # Capture into hash tables, separate file name, type, and parent path 438 | $dirsToInspect.Keys | ForEach-Object { 439 | 440 | $dirToInspect = $_ 441 | $safety = $dirsToInspect[$dirToInspect] 442 | if ($safety -eq $UnknownDir) { 443 | Write-Verbose -Message "about to inspect $dirToInspect for writable directories..." 444 | if ($Null -eq (Get-Command AccessChk.exe -ErrorAction "SilentlyContinue")) { 445 | $errMsg = "Scanning for writable subdirectories requires that Sysinternals AccessChk.exe be in the Path or in the same directory with this script.`n" + 446 | "AccessChk.exe was not found.`n" + 447 | "(See .\Support\DownloadAccesschk.ps1 for help.)`n" + 448 | "Exiting..." 449 | Write-Error $errMsg 450 | return 451 | } 452 | $writableDirs = [ref] ( & $ps1_EnumWritableDirs -RootDirectory $dirToInspect -KnownAdmins $knownAdmins) 453 | if ($null -eq $writableDirs) { 454 | $writableDirs = [ref]@() 455 | } 456 | } 457 | else { 458 | $writableDirs = [ref]@() 459 | } 460 | 461 | Write-Verbose -Message "About to inspect $dirToInspect..." 462 | $csv.AddRange( @(InspectDirectories $dirToInspect $safety $writableDirs) ) 463 | } 464 | 465 | # Restore original Path if it was altered for AccessChk.exe 466 | if ($origPath.Length -gt 0) { 467 | $env:Path = $origPath 468 | } 469 | 470 | 471 | if ($Excel) { 472 | $OutputEncodingPrevious = $OutputEncoding 473 | $OutputEncoding = [System.Text.ASCIIEncoding]::Unicode 474 | 475 | $tempfile = [System.IO.Path]::GetTempFileName() 476 | $tabname = "Consider for potential rules" 477 | $csv | Out-File $tempfile -Encoding unicode 478 | CreateExcelFromCsvFile $tempfile $tabname # $linebreakSeq 479 | 480 | Remove-Item $tempfile 481 | $OutputEncoding = $OutputEncodingPrevious 482 | } 483 | elseif ($GridView) { 484 | $csv | ConvertFrom-Csv -Delimiter "`t" | Out-GridView -Title $MyInvocation.MyCommand.Name 485 | } 486 | else { 487 | # Just output the CSV raw 488 | $csv 489 | } 490 | 491 | 492 | <# Informational: 493 | 494 | Get-AppLockerFileInformation -Directory searches for these file extensions: 495 | *.com 496 | *.exe 497 | *.dll 498 | *.ocx 499 | *.msi 500 | *.msp 501 | *.mst 502 | *.bat 503 | *.cmd 504 | *.js 505 | *.ps1 506 | *.vbs 507 | *.appx 508 | #> 509 | -------------------------------------------------------------------------------- /AaronLocker/ScanResults/ExeDenyListData.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/ScanResults/ExeDenyListData.txt -------------------------------------------------------------------------------- /AaronLocker/ScanResults/Writable_Full_PF.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /AaronLocker/ScanResults/Writable_Full_PF86.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | NT AUTHORITY\Authenticated Users 4 | 5 | 6 | -------------------------------------------------------------------------------- /AaronLocker/ScanResults/Writable_Full_windir.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | NT AUTHORITY\Authenticated Users 4 | 5 | 6 | BUILTIN\Users 7 | 8 | 9 | NT AUTHORITY\Authenticated Users 10 | 11 | 12 | BUILTIN\Users 13 | 14 | 15 | NT AUTHORITY\Authenticated Users 16 | 17 | 18 | NT AUTHORITY\Authenticated Users 19 | 20 | 21 | Everyone 22 | BUILTIN\Users 23 | 24 | 25 | BUILTIN\Users 26 | 27 | 28 | NT AUTHORITY\Authenticated Users 29 | 30 | 31 | -------------------------------------------------------------------------------- /AaronLocker/ScanResults/Writable_PF.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/ScanResults/Writable_PF.txt -------------------------------------------------------------------------------- /AaronLocker/ScanResults/Writable_PF86.txt: -------------------------------------------------------------------------------- 1 | C:\Program Files (x86)\Microsoft\Edge\Application\SetupMetrics\* 2 | -------------------------------------------------------------------------------- /AaronLocker/ScanResults/Writable_windir.txt: -------------------------------------------------------------------------------- 1 | C:\Windows\debug\WIA\* 2 | C:\Windows\debug\WIA:* 3 | C:\Windows\Registration\CRMLog\* 4 | C:\Windows\System32\Microsoft\Crypto\RSA\MachineKeys\* 5 | C:\Windows\System32\Microsoft\Crypto\RSA\MachineKeys:* 6 | C:\Windows\System32\spool\drivers\color\* 7 | C:\Windows\System32\Tasks\* 8 | C:\Windows\System32\Tasks_Migrated\* 9 | C:\Windows\SysWOW64\Tasks\* 10 | C:\Windows\Tasks\* 11 | C:\Windows\tracing\* 12 | C:\Windows\tracing:* 13 | -------------------------------------------------------------------------------- /AaronLocker/Support/Config.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Defines variables for path names and other configuration settings. Intended to be dot-sourced into other scripts, and not run directly. 4 | Also loads global support functions. 5 | 6 | .DESCRIPTION 7 | Defines variables for path names and other configuration settings. Intended to be dot-sourced into other scripts, and not run directly. 8 | Also loads global support functions. 9 | 10 | Variable $rootDir must already have been set prior to calling this script. 11 | #> 12 | 13 | # Verify that $rootDir has been defined and is an existing directory. 14 | if ($null -eq $rootDir -or !(Test-Path($rootDir))) { 15 | Write-Error ('Script error: variable $rootDir is not defined prior to invoking ' + $MyInvocation.MyCommand.Path) 16 | return 17 | } 18 | 19 | ####### Establish directory paths 20 | $customizationInputsDir = [System.IO.Path]::Combine($rootDir, "CustomizationInputs") 21 | $mergeRulesDynamicDir = [System.IO.Path]::Combine($rootDir, "MergeRules-Dynamic") 22 | $mergeRulesStaticDir = [System.IO.Path]::Combine($rootDir, "MergeRules-Static") 23 | $outputsDir = [System.IO.Path]::Combine($rootDir, "Outputs") 24 | $supportDir = [System.IO.Path]::Combine($rootDir, "Support") 25 | $scanResultsDir = [System.IO.Path]::Combine($rootDir, "ScanResults") 26 | 27 | ####### INPUTS 28 | 29 | # Script inputs 30 | $ps1_GetExeFilesToDenyList = [System.IO.Path]::Combine($customizationInputsDir, "GetExeFilesToDenyList.ps1") 31 | $ps1_GetSafePathsToAllow = [System.IO.Path]::Combine($customizationInputsDir, "GetSafePathsToAllow.ps1") 32 | $ps1_UnsafePathsToBuildRulesFor = [System.IO.Path]::Combine($customizationInputsDir, "UnsafePathsToBuildRulesFor.ps1") 33 | $fname_TrustedSigners = "TrustedSigners.ps1" 34 | $ps1_TrustedSigners = [System.IO.Path]::Combine($customizationInputsDir, $fname_TrustedSigners) 35 | $ps1_TrustedSignersWDAC = [System.IO.Path]::Combine($customizationInputsDir, "WDACTrustedSigners.ps1") 36 | $ps1_HashRuleData = [System.IO.Path]::Combine($customizationInputsDir, "HashRuleData.ps1") 37 | $ps1_KnownAdmins = [System.IO.Path]::Combine($customizationInputsDir, "KnownAdmins.ps1") 38 | $ps1_CreatePoliciesAppLocker = [System.IO.Path]::Combine($supportDir, "Create-Policies-AppLocker.ps1") 39 | $ps1_CreatePoliciesWDAC = [System.IO.Path]::Combine($supportDir, "Create-Policies-WDAC.ps1") 40 | 41 | # File prefixes for AppLocker and WDAC 42 | $rulesFileBase = "AppLockerRules-" 43 | $WDACrulesFileBase = "WDACRules-" 44 | # Path to results from scanning files listed in GetExeFilesToDenyList 45 | $ExeDenyListData = [System.IO.Path]::Combine($scanResultsDir, "ExeDenyListData.txt") 46 | # Paths to "full" results of all user-writable directories under Windir and the ProgramFiles directories. 47 | # Written to when Rescan enabled; used to create the next set of files 48 | $windirFullXml = [System.IO.Path]::Combine($scanResultsDir, "Writable_Full_windir.xml") 49 | $PfFullXml = [System.IO.Path]::Combine($scanResultsDir, "Writable_Full_PF.xml") 50 | $Pf86FullXml = [System.IO.Path]::Combine($scanResultsDir, "Writable_Full_PF86.xml") 51 | # Paths to filtered results with redundancies removed. 52 | # Written to when Rescan enabled; read from when building rule set. 53 | $windirTxt = [System.IO.Path]::Combine($scanResultsDir, "Writable_windir.txt") 54 | $PfTxt = [System.IO.Path]::Combine($scanResultsDir, "Writable_PF.txt") 55 | $Pf86Txt = [System.IO.Path]::Combine($scanResultsDir, "Writable_PF86.txt") 56 | 57 | 58 | ####### SUPPORT 59 | $defRulesXml = [System.IO.Path]::Combine($supportDir, "DefaultRulesWithPlaceholders.xml") 60 | $ps1_EnumWritableDirs = [System.IO.Path]::Combine($supportDir, "Enum-WritableDirs.ps1") 61 | $ps1_BuildRulesForFilesInWritableDirectories = [System.IO.Path]::Combine($supportDir, "BuildRulesForFilesInWritableDirectories.ps1") 62 | $ps1_ExportPolicyToCSV = [System.IO.Path]::Combine($supportDir, "ExportPolicy-ToCsv.ps1") 63 | $ps1_ExportPolicyToExcel = [System.IO.Path]::Combine($rootDir, "ExportPolicy-ToExcel.ps1") 64 | 65 | 66 | ####### OUTPUTS AND TIMESTAMPS 67 | # Paths to result files containing AppLocker policy rules. 68 | # Policy rules file have timestamp embedded into file name so previous ones don't get overwritten and so that alphabetic sort shows which is newest. 69 | # Example filenames: 70 | # AppLockerRules-20180518-1151-Audit.xml 71 | # AppLockerRules-20180518-1151-Enforce.xml 72 | $dtNow = [datetime]::Now 73 | $strRuleDocTimestamp = $dtNow.ToString("yyyy-MM-dd HH:mm") 74 | $strFnameTimestamp = $dtNow.ToString("yyyyMMdd-HHmm") 75 | $strTimestampForHashRule = $dtNow.ToString("yyyyMMddHHmmss") 76 | $rulesFileAuditSuffix = "-Audit.xml" 77 | $rulesFileEnforceSuffix = "-Enforce.xml" 78 | $rulesFileAuditNew = [System.IO.Path]::Combine($outputsDir, $rulesFileBase + $strFnameTimestamp + $rulesFileAuditSuffix) 79 | $rulesFileEnforceNew = [System.IO.Path]::Combine($outputsDir, $rulesFileBase + $strFnameTimestamp + $rulesFileEnforceSuffix) 80 | $WDACrulesFileAuditNew = [System.IO.Path]::Combine($outputsDir, $WDACrulesFileBase + $strFnameTimestamp + "-Allow" + $rulesFileAuditSuffix) 81 | $WDACrulesFileEnforceNew = [System.IO.Path]::Combine($outputsDir, $WDACrulesFileBase + $strFnameTimestamp + "-Allow" + $rulesFileEnforceSuffix) 82 | $WDACDenyrulesFileAuditNew = [System.IO.Path]::Combine($outputsDir, $WDACrulesFileBase + $strFnameTimestamp + "-Deny" + $rulesFileAuditSuffix) 83 | $WDACDenyrulesFileEnforceNew = [System.IO.Path]::Combine($outputsDir, $WDACrulesFileBase + $strFnameTimestamp + "-Deny" + $rulesFileEnforceSuffix) 84 | # Get latest audit and enforce policy files, or $null if none found. 85 | function RulesFileAuditLatest() { 86 | Get-ChildItem $([System.IO.Path]::Combine($outputsDir, $rulesFileBase + "*" + $rulesFileAuditSuffix)) | ForEach-Object { $_.FullName } | Sort-Object | Select-Object -Last 1 87 | } 88 | function RulesFileEnforceLatest() { 89 | Get-ChildItem $([System.IO.Path]::Combine($outputsDir, $rulesFileBase + "*" + $rulesFileEnforceSuffix)) | ForEach-Object { $_.FullName } | Sort-Object | Select-Object -Last 1 90 | } 91 | function WDACRulesFileAuditLatest() { 92 | Get-ChildItem $([System.IO.Path]::Combine($outputsDir, $WDACrulesFileBase + "*" + $rulesFileAuditSuffix)) -Exclude *DENY* | ForEach-Object { $_.FullName } | Sort-Object | Select-Object -Last 1 93 | } 94 | function WDACRulesFileEnforceLatest() { 95 | Get-ChildItem $([System.IO.Path]::Combine($outputsDir, $WDACrulesFileBase + "*" + $rulesFileEnforceSuffix)) -Exclude *DENY* | ForEach-Object { $_.FullName } | Sort-Object | Select-Object -Last 1 96 | } 97 | function WDACDenyRulesFileAuditLatest() { 98 | Get-ChildItem $([System.IO.Path]::Combine($outputsDir, $WDACrulesFileBase + "*Deny" + $rulesFileAuditSuffix)) | ForEach-Object { $_.FullName } | Sort-Object | Select-Object -Last 1 99 | } 100 | function WDACDenyRulesFileEnforceLatest() { 101 | Get-ChildItem $([System.IO.Path]::Combine($outputsDir, $WDACrulesFileBase + "*Deny" + $rulesFileEnforceSuffix)) | ForEach-Object { $_.FullName } | Sort-Object | Select-Object -Last 1 102 | } 103 | 104 | 105 | ####### GLOBAL FUNCTIONS 106 | # Incorporate global support functions 107 | . $rootDir\Support\SupportFunctions.ps1 108 | -------------------------------------------------------------------------------- /AaronLocker/Support/DefaultRulesWithPlaceholders.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/AaronLocker/Support/DefaultRulesWithPlaceholders.xml -------------------------------------------------------------------------------- /AaronLocker/Support/DownloadAccesschk.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Download Sysinternals accesschk.exe into the parent directory above this script's directory. 4 | 5 | TODO: Maybe add an optional target directory. 6 | TODO: Maybe add a required -AcceptEula switch 7 | #> 8 | 9 | # Identify the directory above this script's directory (presumably the main "AaronLocker" directory). 10 | $thisDir = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path) 11 | $targetDir = [System.IO.Directory]::GetParent($thisDir).FullName 12 | 13 | Invoke-WebRequest -Uri https://live.sysinternals.com/accesschk.exe -OutFile (Join-Path $targetDir "accesschk.exe") 14 | #TODO: Verify that Invoke-Request succeeded. 15 | 16 | #TODO: Set the LastWriteTime to match (might need to copy from UNC path instead of https to get that) 17 | -------------------------------------------------------------------------------- /AaronLocker/Support/Enum-WritableDirs.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Enumerates "user-writable" subdirectories. 4 | 5 | .DESCRIPTION 6 | Enumerates subdirectories that are writable by accounts other than a set of 7 | known admin or admin-equivalent entities (including members of the local 8 | Administrators group). The goal is to list user-writable directories in 9 | which end user program execution should be disallowed via AppLocker. 10 | You should run this script with administrative rights to avoid access- 11 | denied errors. 12 | 13 | NOTE: Requires Sysinternals AccessChk.exe: 14 | https://technet.microsoft.com/sysinternals/accesschk 15 | https://download.sysinternals.com/files/AccessChk.zip 16 | NOTE: Requires Windows PowerShell 5.1 or newer (relies on Get-LocalGroup and 17 | Get-LocalGroupMember cmdlets). 18 | 19 | Note: this script does not discover user-writable files. A user-writable 20 | file in a non-writable directory presents a similar risk, as a non-admin 21 | can overwrite it with arbitrary content and execute it. 22 | 23 | .LINK 24 | Sysinternals AccessChk available here: 25 | https://technet.microsoft.com/sysinternals/accesschk 26 | https://download.sysinternals.com/files/AccessChk.zip 27 | https://live.sysinternals.com/accesschk.exe 28 | or run .\Support\DownloadAccesschk.ps1, which downloads AccessChk.exe to the main AaronLocker directory. 29 | 30 | .PARAMETER RootDirectory 31 | The starting directory for the permission enumeration. 32 | 33 | .PARAMETER ShowGrantees 34 | If set, output includes the names of the non-admin entities that have write 35 | permissions 36 | 37 | .PARAMETER DontFilterNTService 38 | By default, this script ignores access granted to NT SERVICE\ accounts (SID 39 | beginning with S-1-5-80-). If this switch is set, this script does not 40 | ignore that access, except for access granted to NT SERVICE\TrustedInstaller. 41 | 42 | .PARAMETER OutputXML 43 | If set, output is formatted as XML. 44 | 45 | .PARAMETER KnownAdmins 46 | Optional: additional list of known administrative users and groups. 47 | 48 | .EXAMPLE 49 | 50 | .\Enum-WritableDirs.ps1 C:\Windows\System32 51 | 52 | Output: 53 | C:\Windows\System32\FxsTmp 54 | C:\Windows\System32\Tasks 55 | C:\Windows\System32\Com\dmp 56 | C:\Windows\System32\Microsoft\Crypto\RSA\MachineKeys 57 | C:\Windows\System32\spool\PRINTERS 58 | C:\Windows\System32\spool\SERVERS 59 | C:\Windows\System32\spool\drivers\color 60 | C:\Windows\System32\Tasks\Microsoft IT Diagnostics Utility 61 | C:\Windows\System32\Tasks\Microsoft IT VPN 62 | C:\Windows\System32\Tasks\WPD 63 | C:\Windows\System32\Tasks\Microsoft\Windows\RemoteApp and Desktop Connections Update 64 | C:\Windows\System32\Tasks\Microsoft\Windows\SyncCenter 65 | C:\Windows\System32\Tasks\Microsoft\Windows\WCM 66 | C:\Windows\System32\Tasks\Microsoft\Windows\PLA\System 67 | 68 | .EXAMPLE 69 | .\Enum-WritableDirs.ps1 C:\Windows\System32 -ShowGrantees 70 | 71 | Output: 72 | C:\Windows\system32\FxsTmp 73 | BUILTIN\Users 74 | C:\Windows\system32\Tasks 75 | NT AUTHORITY\Authenticated Users 76 | C:\Windows\system32\Com\dmp 77 | BUILTIN\Users 78 | C:\Windows\system32\Microsoft\Crypto\RSA\MachineKeys 79 | Everyone 80 | C:\Windows\system32\spool\PRINTERS 81 | BUILTIN\Users 82 | C:\Windows\system32\spool\SERVERS 83 | BUILTIN\Users 84 | C:\Windows\system32\spool\drivers\color 85 | BUILTIN\Users 86 | C:\Windows\system32\Tasks\Microsoft IT Diagnostics Utility 87 | NT AUTHORITY\Authenticated Users 88 | C:\Windows\system32\Tasks\Microsoft IT VPN 89 | NT AUTHORITY\Authenticated Users 90 | C:\Windows\system32\Tasks\WPD 91 | NT AUTHORITY\Authenticated Users 92 | aaronmar5\aaronmaradmin 93 | C:\Windows\system32\Tasks\Microsoft\Windows\RemoteApp and Desktop Connections Update 94 | NT AUTHORITY\Authenticated Users 95 | C:\Windows\system32\Tasks\Microsoft\Windows\SyncCenter 96 | BUILTIN\Users 97 | C:\Windows\system32\Tasks\Microsoft\Windows\WCM 98 | BUILTIN\Users 99 | C:\Windows\system32\Tasks\Microsoft\Windows\PLA\System 100 | Everyone 101 | 102 | .EXAMPLE 103 | $x = [xml](.\Enum-WritableDirs.ps1 C:\Windows\System32 -ShowGrantees -OutputXML) 104 | $x.root.dir | Sort-Object name 105 | 106 | Output: 107 | name Grantee 108 | ---- ------- 109 | C:\Windows\System32\Com\dmp BUILTIN\Users 110 | C:\Windows\System32\FxsTmp BUILTIN\Users 111 | C:\Windows\System32\Microsoft\Crypto\RSA\MachineKeys Everyone 112 | C:\Windows\System32\spool\drivers\color BUILTIN\Users 113 | C:\Windows\System32\spool\PRINTERS BUILTIN\Users 114 | C:\Windows\System32\spool\SERVERS BUILTIN\Users 115 | C:\Windows\System32\Tasks NT AUTHORITY\Authenticated Users 116 | C:\Windows\System32\Tasks\Microsoft IT Diagnostics Utility NT AUTHORITY\Authenticated Users 117 | C:\Windows\System32\Tasks\Microsoft IT VPN NT AUTHORITY\Authenticated Users 118 | C:\Windows\System32\Tasks\Microsoft\Windows\PLA\System Everyone 119 | C:\Windows\System32\Tasks\Microsoft\Windows\RemoteApp and Desktop Connection... NT AUTHORITY\Authenticated Users 120 | C:\Windows\System32\Tasks\Microsoft\Windows\SyncCenter BUILTIN\Users 121 | C:\Windows\System32\Tasks\Microsoft\Windows\WCM BUILTIN\Users 122 | C:\Windows\System32\Tasks\WPD {NT AUTHORITY\Authenticated Users, vm-t2408\admin} 123 | 124 | #> 125 | 126 | param( 127 | # Name of root directory for permission search 128 | [parameter(Mandatory = $true)] 129 | [String] 130 | $RootDirectory, 131 | 132 | # If -ShowGrantees on the command line, shows which non-admin accounts 133 | # are granted access 134 | [switch] 135 | $ShowGrantees = $false, 136 | 137 | # If -DontFilterNTService set, does not filter out access granted to 138 | # NT SERVICE\ accounts (other than TrustedInstaller) 139 | [switch] 140 | $DontFilterNTService = $false, 141 | 142 | # If -OutputXML, output is XML 143 | [switch] 144 | $OutputXML = $false, 145 | 146 | # If provided, adds list of known administrative users/groups to ignore write permissions granted to 147 | [parameter(Mandatory = $false)] 148 | [String[]] 149 | $KnownAdmins 150 | ) 151 | 152 | # If RootDirectory has a trailing backslash, remove it (AccessChk doesn't handle it correctly). 153 | if ($RootDirectory.EndsWith("\")) { $RootDirectory = $RootDirectory.Substring(0, $RootDirectory.Length - 1) } 154 | 155 | # Entities for which to ignore write permissions. 156 | # TrustedInstaller is always ignored; other NT SERVICE\ accounts are filtered 157 | # out later (too many to list and too many unknown). 158 | # The Package SIDs below (S-1-15-2-*) are associated with microsoft.windows.fontdrvhost and 159 | # are not a problem. AppContainers never grant additional access; they only reduce access. 160 | $FilterOut0 = @" 161 | S-1-3-0 162 | S-1-5-6 163 | S-1-5-18 164 | S-1-5-19 165 | S-1-5-20 166 | S-1-5-32-544 167 | S-1-5-32-549 168 | S-1-5-32-550 169 | S-1-5-32-551 170 | S-1-5-32-577 171 | S-1-5-32-559 172 | S-1-5-32-568 173 | NT SERVICE\TrustedInstaller 174 | S-1-15-2-1430448594-2639229838-973813799-439329657-1197984847-4069167804-1277922394 175 | S-1-15-2-95739096-486727260-2033287795-3853587803-1685597119-444378811-2746676523 176 | "@ 177 | # Filter all the above plus caller-supplied "known admins" 178 | $FilterOut = ($FilterOut0.Split("`n`r") + $KnownAdmins | Where-Object { $_.Length -gt 0 }) -join "," 179 | # Add all members of the local Administrators group, as the Effective Permissions 180 | # APIs consider them to be administrators also. 181 | # For some reason, Get-LocalGroup/Get-LocalGroupMember aren't available on WMFv5.0 on Win7; 182 | # Verify whether command exists before using it. The commands are available on Win7 in v5.1. 183 | if ($null -ne (Get-Command Get-LocalGroupMember -ErrorAction SilentlyContinue)) { 184 | #TODO: Detect and handle case where this cmdlet fails - disconnected and the admins group contains domain SIDs that can't be resolved. 185 | #FWIW, NET LOCALGROUP Administrators doesn't report these entries either. 186 | #Also fails on AAD-joined, with unresolved SIDs beginning with S-1-12-1-... 187 | Get-LocalGroupMember -SID S-1-5-32-544 -ErrorAction SilentlyContinue | ForEach-Object { $FilterOut += "," + $_.SID.Value } 188 | } 189 | 190 | $currfile = "" 191 | 192 | if ($OutputXML) { "" } 193 | 194 | $bInElem = $false 195 | 196 | AccessChk.exe /accepteula -nobanner -w -d -s -f $FilterOut $RootDirectory | ForEach-Object { 197 | if ($_.StartsWith(" ") -or $_.Length -eq 0) { 198 | if ($_.StartsWith(" RW ") -or $_.StartsWith(" W ")) { 199 | $grantee = $_.Substring(5).Trim() 200 | if ($DontFilterNTService -or (!$grantee.StartsWith("NT SERVICE\") -and !$grantee.StartsWith("S-1-5-80-"))) { 201 | if ($currfile.Length -gt 0) { 202 | if ($OutputXML) { 203 | # Path name has to be escaped for XML 204 | "" 205 | } 206 | else { 207 | $currfile 208 | } 209 | $currfile = "" 210 | $bInElem = $true 211 | } 212 | if ($ShowGrantees) { 213 | if ($OutputXML) { 214 | "" + $grantee + "" 215 | } 216 | else { 217 | " " + $grantee 218 | } 219 | } 220 | } 221 | } 222 | } 223 | else { 224 | if ($bInElem -and $OutputXML) { "" } 225 | $currfile = $_ 226 | $bInElem = $false 227 | } 228 | } 229 | 230 | if ($bInElem -and $OutputXML) { "" } 231 | if ($OutputXML) { "" } 232 | -------------------------------------------------------------------------------- /AaronLocker/Support/ExportPolicy-ToCsv.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Turn AppLocker policy into more human-readable CSV. 4 | 5 | .DESCRIPTION 6 | Script reads AppLocker policy from local policy, effective policy, or an XML file, and renders it as a tab-delimited CSV that can be pasted into Microsoft Excel, with easy sorting and filtering. 7 | 8 | If neither -AppLockerPolicyFile or -Local is specified, the script processes the current computer's effective policy. 9 | 10 | If -LineBreakSeq is not specified, CRLF and LF sequences in attribute values are replaced with "^|^". The linebreak sequence can be replaced after importing results into Excel (in the Find/Replace dialog, replace the sequence with Ctrl+Shift+J). 11 | 12 | .PARAMETER AppLockerPolicyFile 13 | If this optional string parameter is specified, AppLocker policy is read from the specified XML file. 14 | 15 | .PARAMETER Local 16 | If this switch is specified, the script processes the current computer's local policy. 17 | 18 | .PARAMETER LineBreakSeq 19 | If this optional string parameter is specified, CRLF and LF sequences in attribute values are replaced with the specified sequence. "^|^" is the default. 20 | 21 | .EXAMPLE 22 | 23 | ExportPolicy-ToCsv.ps1 | clip.exe 24 | 25 | Renders effective AppLocker policy to tab-delimited CSV and writes that output to the clipboard using the built-in Windows clip.exe utility. 26 | Paste the output directly into an Excel spreadsheet, replace "^|^" with Ctrl+Shift+J, add filtering, freeze the top row, and autosize. 27 | 28 | #TODO: Add option to get AppLocker policy from AD GPO 29 | E.g., 30 | Get-AppLockerPolicy -Domain -LDAP "LDAP://DC13.Contoso.com/CN={31B2F340-016D-11D2-945F-00C04FB984F9},CN=Policies,CN=System,DC=Contoso,DC=com" 31 | Figure out how to tie Get-GPO in with this... 32 | #> 33 | 34 | param( 35 | # Optional: path to XML file containing AppLocker policy 36 | [parameter(Mandatory = $false)] 37 | [String] $AppLockerPolicyFile, 38 | 39 | # If specified, inspects local AppLocker policy rather than effective policy or an XML file 40 | [switch] $Local = $false, 41 | 42 | # Optional: specify character sequence to replace line breaks 43 | [parameter(Mandatory = $false)] 44 | [String] $LineBreakSeq = "^|^" 45 | ) 46 | 47 | $tab = "`t" 48 | 49 | if ($AppLockerPolicyFile.Length -gt 0) { 50 | # Get policy from a file 51 | $x = [xml](Get-Content $AppLockerPolicyFile) 52 | } 53 | elseif ($Local) { 54 | # Inspect local policy 55 | $x = [xml](Get-AppLockerPolicy -Local -Xml) 56 | } 57 | else { 58 | # Inspect effective policy 59 | $x = [xml](Get-AppLockerPolicy -Effective -Xml) 60 | } 61 | 62 | # CSV Headers 63 | "FileType" + $tab + 64 | "Enforce" + $tab + 65 | "RuleType" + $tab + 66 | "UserOrGroup" + $tab + 67 | "Action" + $tab + 68 | "RuleInfo" + $tab + 69 | "Exceptions" + $tab + 70 | "Name" + $tab + 71 | "Description" 72 | 73 | $x.AppLockerPolicy.RuleCollection | ForEach-Object { 74 | $filetype = $_.Type 75 | $enforce = $_.EnforcementMode 76 | 77 | if ($_.ChildNodes.Count -eq 0) { 78 | $filetype + $tab + 79 | $enforce + $tab + 80 | "N/A" + $tab + 81 | "N/A" + $tab + 82 | "N/A" + $tab + 83 | "N/A" + $tab + 84 | "N/A" + $tab + 85 | "N/A" + $tab + 86 | "N/A" 87 | } 88 | else { 89 | $_.ChildNodes | ForEach-Object { 90 | 91 | $childNode = $_ 92 | switch ( $childNode.LocalName ) { 93 | "FilePublisherRule" { 94 | $RuleType = "Publisher" 95 | $condition = $childNode.Conditions.FilePublisherCondition 96 | $ruleInfo = 97 | "Publisher: " + $condition.PublisherName + $LineBreakSeq + 98 | "Product: " + $condition.ProductName + $LineBreakSeq + 99 | "BinaryName: " + $condition.BinaryName + $LineBreakSeq + 100 | "LowVersion: " + $condition.BinaryVersionRange.LowSection + $LineBreakSeq + 101 | "HighVersion: " + $condition.BinaryVersionRange.HighSection 102 | } 103 | "FilePathRule" { 104 | $RuleType = "Path" 105 | $ruleInfo = $childNode.Conditions.FilePathCondition.Path 106 | } 107 | "FileHashRule" { 108 | $RuleType = "Hash" 109 | $condition = $childNode.Conditions.FileHashCondition.FileHash 110 | $ruleInfo = $condition.SourceFileName + "; length = " + $condition.SourceFileLength 111 | } 112 | default { $RuleType = $_.LocalName; $condition = $ruleInfo = [string]::Empty; } 113 | } 114 | 115 | $exceptions = [string]::Empty 116 | if ($null -ne $childNode.Exceptions) { 117 | 118 | # Output exceptions with a designated separator character sequence that can be replaced with line feeds in Excel 119 | [System.Collections.ArrayList]$arrExceptions = @() 120 | if ($null -ne $childNode.Exceptions.FilePathCondition) { 121 | $arrExceptions.Add( "[----- Path exceptions -----]" ) | Out-Null 122 | $arrExceptions.AddRange( @($childNode.Exceptions.FilePathCondition.Path | Sort-Object) ) 123 | } 124 | if ($null -ne $childNode.Exceptions.FilePublisherCondition) { 125 | $arrExceptions.Add( "[----- Publisher exceptions -----]" ) | Out-Null 126 | $arrExceptions.AddRange( @($childNode.Exceptions.FilePublisherCondition | 127 | ForEach-Object { 128 | $s = $_.BinaryName + ": " + $_.PublisherName + "; " + $_.ProductName 129 | $bvrLow = $_.BinaryVersionRange.LowSection 130 | $bvrHigh = $_.BinaryVersionRange.HighSection 131 | if ($bvrLow -ne "*" -or $bvrHigh -ne "*") { $s += "; ver " + $bvrLow + " to " + $bvrHigh } 132 | $s 133 | } | Sort-Object) ) 134 | } 135 | if ($null -ne $childNode.Exceptions.FileHashCondition) { 136 | $arrExceptions.Add( "[----- Hash exceptions -----]" ) | Out-Null 137 | $arrExceptions.AddRange( @($childNode.Exceptions.FileHashCondition.FileHash | ForEach-Object { $_.SourceFileName + "; length = " + $_.SourceFileLength } | Sort-Object) ) 138 | } 139 | $exceptions = $arrExceptions -join $LineBreakSeq 140 | } 141 | 142 | # Replace CRLF with line-break replacement string; then replace any left-over LF characters with it. 143 | $name = $_.Name.Replace("`r`n", $LineBreakSeq).Replace("`n", $LineBreakSeq) 144 | $description = $_.Description.Replace("`r`n", $LineBreakSeq).Replace("`n", $LineBreakSeq) 145 | 146 | # Get user/group name if possible; otherwise show SID. #was: $userOrGroup = $_.UserOrGroupSid 147 | $oSID = New-Object -TypeName System.Security.Principal.SecurityIdentifier($_.UserOrGroupSid) 148 | $oUser = $null 149 | try { $oUser = $oSID.Translate([System.Security.Principal.NTAccount]) } catch { $oUser = $Null } 150 | if ($null -ne $oUser) { 151 | $userOrGroup = $oUser.Value 152 | } 153 | else { 154 | $userOrGroup = $_.UserOrGroupSid 155 | } 156 | $action = $_.Action 157 | 158 | $filetype + $tab + 159 | $enforce + $tab + 160 | $RuleType + $tab + 161 | $userOrGroup + $tab + 162 | $action + $tab + 163 | $ruleInfo + $tab + 164 | $exceptions + $tab + 165 | $name + $tab + 166 | $description 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /AaronLocker/Support/Get-AaronLockerTimestamp.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets custom timestamp field from AaronLocker-generated AppLocker rule set. 4 | 5 | .DESCRIPTION 6 | Retrieves an AppLocker policy, and reports the AaronLocker-generated timestamp, if found. 7 | 8 | AaronLocker inserts a "timestamp" rule that shows when the rule set was generated and helps associate it with a rule file with the same timestamp. 9 | 10 | This script can inspect local policy, effective policy, or an AppLocker policy XML file. 11 | 12 | The AaronLocker-generated timestamp is stored in the name and description of an Exe "deny" hash rule with a bogus hash value and 13 | applied to the "CREATOR OWNER" user. Because "CREATOR OWNER" never appears in a user's access token, the rule will never be applied. 14 | The "Deny" and "CREATOR OWNER" attributes make it stand out and easily visible in an AaronLocker rule set. 15 | 16 | .PARAMETER Local 17 | If this switch is specified, the script processes the computer's local AppLocker policy. 18 | If no parameters are specified or this switch is set to -Local:$false, the script processes the computer's effective AppLocker policy. 19 | 20 | .PARAMETER AppLockerXML 21 | If this parameter is specified, AppLocker policy is read from the specified exported XML policy file. 22 | 23 | .EXAMPLE 24 | .\Support\Get-AaronLockerTimestamp.ps1 25 | 26 | Gets the custom timestamp field from the computer's effective AppLocker policy. 27 | 28 | #> 29 | 30 | [CmdletBinding(DefaultParameterSetName = "LocalPolicy")] 31 | param( 32 | # If specified, inspects local AppLocker policy rather than effective policy or an XML file 33 | [parameter(ParameterSetName = "LocalPolicy")] 34 | [switch] 35 | $Local = $false, 36 | 37 | # Optional: path to XML file containing AppLocker policy 38 | [parameter(ParameterSetName = "SavedXML")] 39 | [String] 40 | $AppLockerXML 41 | ) 42 | 43 | if ($AppLockerXML.Length -gt 0) { 44 | $xmlPolicy = [xml](Get-Content -Path $AppLockerXML) 45 | } 46 | elseif ($Local) { 47 | $xmlPolicy = [xml](Get-AppLockerPolicy -Local -Xml) 48 | } 49 | else { 50 | $xmlPolicy = [xml](Get-AppLockerPolicy -Effective -Xml) 51 | } 52 | 53 | if ($null -ne $xmlPolicy) { 54 | $node = $xmlPolicy.SelectNodes("AppLockerPolicy/RuleCollection[@Type='Exe']/FileHashRule[@UserOrGroupSid='S-1-3-0']") 55 | if ($null -eq $node -or 0 -eq $node.Count) { 56 | Write-Warning "Policy does not include timestamp" 57 | } 58 | else { 59 | $node.Name 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /AaronLocker/Support/Set-OutputEncodingToUnicode.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Sets the output encoding for the current session to Unicode, so that piped output retains Unicode encoding. 4 | 5 | #> 6 | 7 | $global:OutputEncoding = [System.Text.ASCIIEncoding]::Unicode 8 | 9 | -------------------------------------------------------------------------------- /AaronLocker/Support/SupportFunctions.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Global support functions. Intended to be dot-sourced into other scripts, and not run directly. 4 | 5 | .DESCRIPTION 6 | Global support functions. Intended to be dot-sourced into other scripts, and not run directly. 7 | 8 | Functions to save XML consistently as Unicode: 9 | SaveXmlDocAsUnicode([System.Xml.XmlDocument] $xmlDoc, [string] $xmlFilename) 10 | SaveAppLockerPolicyAsUnicodeXml([Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.AppLockerPolicy]$ALPolicy, [string]$xmlFilename) 11 | 12 | Functions to create Excel spreadsheets/workbooks: 13 | CreateExcelApplication() 14 | ReleaseExcelApplication() 15 | SelectFirstWorksheet() 16 | SaveWorkbook([string]$filename) 17 | AddNewWorksheet([string]$tabname) 18 | AddWorksheetFromText([string[]]$text, [string]$tabname) 19 | AddWorksheetFromCsvFile([string]$filename, [string]$tabname, [string]$CrLfEncoded, [switch]$AddChart) 20 | AddWorksheetFromCsvData([string[]]$csv, [string]$tabname, [string]$CrLfEncoded, [switch]$AddChart) 21 | CreateExcelFromCsvFile([string]$filename, [string]$tabname, [string]$CrLfEncoded, [string]$saveAsName) 22 | 23 | Function to determine whether a file is a Win32 EXE, a Win32 DLL, or neither 24 | IsWin32Executable([string]$filename) 25 | 26 | Global variables defining known file extensions 27 | #> 28 | 29 | #pragma once :-) 30 | if (Test-Path("function:\SaveXmlDocAsUnicode")) { 31 | return 32 | } 33 | 34 | #################################################################################################### 35 | # Ensure the AppLocker assembly is loaded. (Scripts sometimes run into TypeNotFound errors if not.) 36 | #################################################################################################### 37 | 38 | [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel") 39 | 40 | #################################################################################################### 41 | # Global functions to save XML consistently as Unicode 42 | #################################################################################################### 43 | 44 | # Note that the "#pragma once" thing at the beginning of this file depends on this function name 45 | function SaveXmlDocAsUnicode([System.Xml.XmlDocument] $xmlDoc, [string] $xmlFilename) { 46 | $xws = [System.Xml.XmlWriterSettings]::new() 47 | $xws.Encoding = [System.Text.Encoding]::Unicode 48 | $xws.Indent = $true 49 | $xw = [System.Xml.XmlWriter]::Create($xmlFilename, $xws) 50 | $xmlDoc.Save($xw) 51 | $xw.Close() 52 | } 53 | 54 | function SaveAppLockerPolicyAsUnicodeXml([Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.AppLockerPolicy]$ALPolicy, [string]$xmlFilename) { 55 | SaveXmlDocAsUnicode -xmlDoc ([xml]($ALPolicy.ToXml())) -xmlFilename $xmlFilename 56 | } 57 | 58 | #################################################################################################### 59 | # Global functions to create Excel spreadsheets from CSV data 60 | #################################################################################################### 61 | 62 | # Global variable treated as a singleton class instance, because managing this variable is a PITA otherwise. 63 | # Not intended to be used by anything other than the functions defined below. 64 | $ExcelAppInstance = $null 65 | 66 | # Create global instance of Excel application. Call ReleaseExcelApplication when done using it. 67 | function CreateExcelApplication() { 68 | Write-Verbose -Message "Starting Excel..." 69 | $global:ExcelAppInstance = New-Object -ComObject excel.application 70 | if ($null -ne $global:ExcelAppInstance) { 71 | $global:ExcelAppInstance.Visible = $true 72 | return $true 73 | } 74 | else { 75 | Write-Error "Apparently Excel is not installed. Can't create an Excel document without it. Exiting..." 76 | return $false 77 | } 78 | } 79 | 80 | # Release global instance of Excel application. Make sure to call after CreateExcelApplication. 81 | function ReleaseExcelApplication() { 82 | Write-Verbose -Message "Releasing Excel..." 83 | $dummy = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($global:ExcelAppInstance) 84 | $global:ExcelAppInstance = $null 85 | } 86 | 87 | function SelectFirstWorksheet() { 88 | if ($null -eq $global:ExcelAppInstance) { return } 89 | if ($global:ExcelAppInstance.Workbooks.Count -eq 0) { return } 90 | $dummy = $global:ExcelAppInstance.Workbooks[1].Sheets(1).Select() 91 | } 92 | 93 | function SaveWorkbook([string]$filename) { 94 | Write-Verbose -Message "Saving workbook as `"$filename`"..." 95 | if ($null -eq $global:ExcelAppInstance) { return } 96 | if ($global:ExcelAppInstance.Workbooks.Count -eq 0) { return } 97 | $global:ExcelAppInstance.Workbooks[1].SaveAs($filename) 98 | } 99 | 100 | # Add a new named worksheet with the Excel instance created through CreateExcelApplication 101 | function AddNewWorksheet([string]$tabname) { 102 | if ($null -eq $global:ExcelAppInstance) { return $null } 103 | 104 | if ($global:ExcelAppInstance.Workbooks.Count -eq 0) { 105 | $workbook = $global:ExcelAppInstance.Workbooks.Add(5) 106 | $worksheet = $workbook.Sheets(1) 107 | } 108 | else { 109 | $workbook = $global:ExcelAppInstance.Workbooks[1] 110 | $worksheet = $workbook.Worksheets.Add([System.Type]::Missing, $workbook.Worksheets[$workbook.Worksheets.Count]) 111 | } 112 | if ($tabname.Length -gt 0) { 113 | # Excel limits tab names to 31 characters 114 | if ($tabname.Length -gt 31) { 115 | $tabname = $tabname.Substring(0, 31) 116 | } 117 | $worksheet.Name = $tabname 118 | } 119 | 120 | $worksheet 121 | } 122 | 123 | # Add a new named worksheet from lines of text (not CSV) 124 | # Supports multi-column text; if text has tab characters, splits across cells in the row 125 | # TODO: Add support for more than 26 columns (e.g., AA1, AB1, AA2, ...) 126 | function AddWorksheetFromText([string[]]$text, [string]$tabname) { 127 | Write-Verbose -Message "Populating tab `"$tabname`"..." 128 | 129 | if ($null -eq $global:ExcelAppInstance) { return $null } 130 | 131 | $worksheet = AddNewWorksheet($tabname) 132 | $worksheet.UsedRange.VerticalAlignment = -4160 # xlTop 133 | 134 | $row = [int]1 135 | foreach ($line in $text) { 136 | $iCol = [int][char]'A' 137 | $lineparts = $line.Split("`t") 138 | foreach ( $part in $lineparts ) { 139 | $cell = ([char]$iCol).ToString() + $row.ToString() 140 | $worksheet.Range($cell).FormulaR1C1 = $part 141 | $iCol++ 142 | } 143 | $row++ 144 | } 145 | 146 | $dummy = $worksheet.Cells.EntireColumn.AutoFit() 147 | 148 | # Release COM interface references 149 | $dummy = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet) 150 | } 151 | 152 | # Add a new named worksheet from CSV data in the specified file, optionally replacing encoded CrLf with CrLf. 153 | function AddWorksheetFromCsvFile([string]$filename, [string]$tabname, [string]$CrLfEncoded, [switch]$AddChart) { 154 | Write-Verbose -Message "Populating tab `"$tabname`"..." 155 | 156 | if ($null -eq $global:ExcelAppInstance) { return $null } 157 | 158 | $worksheet = AddNewWorksheet($tabname) 159 | 160 | ### Build the QueryTables.Add command 161 | ### QueryTables does the same as when clicking "Data -> From Text" in Excel 162 | $TxtConnector = ("TEXT;" + $filename) 163 | $Connector = $worksheet.QueryTables.add($TxtConnector, $worksheet.Range("A1")) 164 | $query = $worksheet.QueryTables.item($Connector.name) 165 | $query.TextFileTabDelimiter = $true 166 | 167 | ### Execute & delete the import query 168 | $dummy = $query.Refresh() 169 | $query.Delete() 170 | 171 | if ($CrLfEncoded.Length -gt 0) { 172 | # Replace linebreak-replacement sequence in CSV with CRLF. 173 | $dummy = $worksheet.UsedRange.Replace($CrLfEncoded, "`r`n") 174 | } 175 | 176 | # Formatting: autofilter, font size, vertical alignment, freeze top row 177 | $dummy = $worksheet.Cells.AutoFilter() 178 | $worksheet.Cells.Font.Size = 9.5 179 | $worksheet.UsedRange.VerticalAlignment = -4160 # xlTop 180 | $global:ExcelAppInstance.ActiveWindow.SplitColumn = 0 181 | $global:ExcelAppInstance.ActiveWindow.SplitRow = 1 182 | $global:ExcelAppInstance.ActiveWindow.FreezePanes = $true 183 | #$global:ExcelAppInstance.ActiveWindow.Zoom = 80 184 | 185 | $dummy = $worksheet.Range("A2").Select() 186 | 187 | # Formatting: autosize column widths, then set maximum width (except on last column) 188 | $maxWidth = 50 189 | $maxHeight = 120 190 | 191 | $dummy = $worksheet.Cells.EntireColumn.AutoFit() 192 | # Don't set max width if 3 columns or fewer 193 | if ($worksheet.UsedRange.Columns.Count -gt 3) { 194 | $ix = 1 195 | # Do this until the next to last column; don't set max width on the last column 196 | while ( $worksheet.Cells(1, $ix + 1).Text.Length -gt 0) { 197 | $cells = $worksheet.Cells(1, $ix) 198 | if ($cells.ColumnWidth -gt $maxWidth) { $cells.ColumnWidth = $maxWidth } 199 | $ix++ 200 | } 201 | } 202 | 203 | # Formatting: autosize row heights, then set maximum height (if CrLf replacement on) 204 | $dummy = $worksheet.Cells.EntireRow.AutoFit() 205 | # If line breaks added, limit autofit row height to 206 | if ($CrLfEncoded.Length -gt 0) { 207 | $ix = 1 208 | while ( $worksheet.Cells($ix, 1).Text.Length -gt 0) { 209 | $cells = $worksheet.Cells($ix, 1) 210 | if ($cells.RowHeight -gt $maxHeight) { $cells.RowHeight = $maxHeight } 211 | $ix++ 212 | } 213 | } 214 | 215 | if ($AddChart) { 216 | # If lots of input data, limit number of entries in chart 217 | $chartLimit = 20 218 | 219 | $oShape = $worksheet.Shapes.AddChart2(216, 57) 220 | # Have to cast from double to single to avoid runtime errors 221 | $oShape.Left = [System.Single]($worksheet.Range("D2").Left) 222 | $oShape.Top = [System.Single]($worksheet.Range("D2").Top) 223 | $oShape.Visible = $true 224 | 225 | $oChartObject = $worksheet.ChartObjects(1) 226 | $oChart = $oChartObject.Chart 227 | $rowCount = $worksheet.UsedRange.Rows.Count 228 | $oChart.ChartTitle.Text = $tabname 229 | if ($rowCount -le $chartLimit) { 230 | # Use whatever data is there 231 | $oChart.SetSourceData($worksheet.UsedRange, 2) # 2 = xlColumns 232 | } 233 | else { 234 | # Build chart from top $chartLimit entries, then sum the rest into "Others" 235 | $sSeries1 = [System.Text.StringBuilder]::new() 236 | $sSeries2 = [System.Text.StringBuilder]::new() 237 | 2 .. ($chartLimit + 1) | ForEach-Object { 238 | if ($_ -gt 2) { 239 | [void]$sSeries1.Append(",") 240 | [void]$sSeries2.Append(",") 241 | } 242 | [void]$sSeries1.Append("`"" + $worksheet.Range('$A' + $_).Text + "`"") 243 | [void]$sSeries2.Append($worksheet.Range('$B' + $_).Text) 244 | } 245 | [void]$sSeries1.Append(",`"Others`"") 246 | [void]$sSeries2.Append("," + $global:ExcelAppInstance.WorksheetFunction.Sum($worksheet.Range('$B' + ($chartLimit + 2) + ":B" + $rowCount))) 247 | $sSeries = 248 | "=SERIES(`"" + $worksheet.Range('$B1').Text + "`"," + 249 | "{" + $sSeries1.ToString() + "},{" + $sSeries2.ToString() + "},1)" 250 | 251 | $oChart.SeriesCollection(1).Formula = $sSeries 252 | #$oChart.SetSourceData($worksheet.Range('$A$1:$B$' + ($chartLimit + 1).ToString()), 2) # 2 = xlColumns 253 | # $oChart.ChartTitle.Text = "Top " + $chartLimit.ToString() + " " + $tabname 254 | $oShape.Height = $oShape.Height * 2 255 | } 256 | 257 | $oAxes = $oChart.Axes(1) 258 | $oAxes.ReversePlotOrder = $true 259 | $oAxes.TickLabelSpacing = 1 260 | 261 | $dummy = $worksheet.Range("A2").Select() 262 | 263 | # Release COM interface references 264 | $dummy = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($oAxes) 265 | $dummy = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($oChartObject) 266 | $dummy = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($oShape) 267 | } 268 | 269 | # Release COM interface references 270 | $dummy = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($query) 271 | $dummy = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Connector) 272 | $dummy = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet) 273 | } 274 | 275 | # Add a new named worksheet from in-memory CSV data (string array), optionally replacing encoded CrLf with CrLf. 276 | function AddWorksheetFromCsvData([string[]]$csv, [string]$tabname, [string]$CrLfEncoded, [switch]$AddChart) { 277 | Write-Verbose -Message "Preparing data for tab `"$tabname`"..." 278 | 279 | if ($null -eq $global:ExcelAppInstance) { return $null } 280 | 281 | if ($null -ne $csv -and $csv.Length -gt 0) { 282 | $OutputEncodingPrevious = $OutputEncoding 283 | $OutputEncoding = [System.Text.ASCIIEncoding]::Unicode 284 | 285 | $tempfile = [System.IO.Path]::GetTempFileName() 286 | $csv | Out-File $tempfile -Encoding unicode 287 | AddWorksheetFromCsvFile -filename $tempfile -tabname $tabname -CrLfEncoded $CrLfEncoded -AddChart:$AddChart 288 | Remove-Item $tempfile 289 | 290 | $OutputEncoding = $OutputEncodingPrevious 291 | } 292 | else { 293 | $worksheet = AddNewWorksheet -tabname $tabname 294 | $dummy = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet) 295 | } 296 | } 297 | 298 | # Create a new Excel workbook with one named worksheet containing CSV data from the specified file, 299 | # optionally replacing encoded CrLf with CrLf. 300 | function CreateExcelFromCsvFile([string]$filename, [string]$tabname, [string]$CrLfEncoded, [string]$saveAsName) { 301 | 302 | if (CreateExcelApplication) { 303 | AddWorksheetFromCsvFile -filename $filename -tabname $tabname -CrLfEncoded $CrLfEncoded 304 | if ($saveAsName.Length -gt 0) { 305 | SaveWorkbook -filename $saveAsName 306 | } 307 | ReleaseExcelApplication 308 | } 309 | } 310 | 311 | #################################################################################################### 312 | # Global function to determine whether a file is a Win32 EXE, a Win32 DLL, or neither 313 | #################################################################################################### 314 | 315 | # Returns "EXE", "DLL", or nothing 316 | function IsWin32Executable([string]$filename) { 317 | 318 | # sizes, offsets, and values for PE header structures 319 | Set-Variable sizeofImageDosHeader -Option Constant -Value 64 320 | Set-Variable sizeofImageNtHeaders64 -Option Constant -Value 264 321 | Set-Variable offset_e_lfanew -Option Constant -Value 60 322 | Set-Variable offset_FileHeader -Option Constant -Value 4 323 | Set-Variable offset_FileHeader_Characteristics -Option Constant -Value 18 324 | Set-Variable offset_OptionalHeader -Option Constant -Value 24 325 | Set-Variable offset_OptionalHeader_Subsystem -Option Constant -Value 68 326 | Set-Variable IMAGE_SUBSYSTEM_WINDOWS_GUI -Option Constant -Value 2 327 | Set-Variable IMAGE_SUBSYSTEM_WINDOWS_CUI -Option Constant -Value 3 328 | 329 | 330 | # Read first 64 bytes (size of IMAGE_DOS_HEADER) 331 | # Always make sure returned data is an array, even if the file contains exactly one byte 332 | $bytesImageDosHeader = @(Get-Content -Encoding Byte -TotalCount $sizeofImageDosHeader $filename -ErrorAction SilentlyContinue) 333 | if ($null -eq $bytesImageDosHeader -or $bytesImageDosHeader.Length -lt $sizeofImageDosHeader) { 334 | Write-Verbose "$filename : Non-existent or unreadable file, or less than $sizeofImageDosHeader bytes." 335 | #Write-Output "" 336 | return; 337 | } 338 | 339 | # Verify that the first two bytes are "MZ" 340 | $dosSig = "" + [char]($bytesImageDosHeader[0]) + [char]($bytesImageDosHeader[1]) 341 | if ($dosSig -ne "MZ") { 342 | Write-Verbose "$filename : Not a PE file; first two bytes are not MZ." 343 | #Write-Output "" 344 | return; 345 | } 346 | 347 | # Read the IMAGE_DOS_HEADER e_lfanew attribute to determine the offset into the file where the IMAGE_NT_HEADERS begin 348 | # This line of code adapted from Matt Graeber, http://www.exploit-monday.com/2013/03/ParsingBinaryFileFormatsWithPowerShell.html 349 | $offsetImageNtHeaders = [Int32]('0x{0}' -f (( $bytesImageDosHeader[ ($offset_e_lfanew + 3) .. $offset_e_lfanew] | ForEach-Object { $_.ToString('X2') }) -join '')) 350 | 351 | # Read up to where the NT headers are, and then the size of IMAGE_NT_HEADERS64 which should be more than we need 352 | $totalToRead = $offsetImageNtHeaders + $sizeofImageNtHeaders64 353 | $bytesImageNtHeaders = Get-Content -Encoding Byte -TotalCount $totalToRead $filename -ErrorAction SilentlyContinue 354 | if ($bytesImageNtHeaders.Length -lt $totalToRead) { 355 | Write-Verbose "$filename : Not a PE file; less than $totalToRead bytes." 356 | #Write-Output "" 357 | return; 358 | } 359 | 360 | # Verify that the PE signature is present there. (Actually is "PE\0\0" but just going to look for the first two bytes.) 361 | $peSig = "" + [char]($bytesImageNtHeaders[$offsetImageNtHeaders]) + [char]($bytesImageNtHeaders[$offsetImageNtHeaders + 1]) 362 | if ($peSig -ne "PE") { 363 | Write-Verbose "$filename : Not a PE file; 'PE' signature bytes not found." 364 | #Write-Output "" 365 | return; 366 | } 367 | 368 | # Get the offset of the "Characteristics" attribute in the file header 369 | $offsChar = $offsetImageNtHeaders + $offset_FileHeader + $offset_FileHeader_Characteristics 370 | # Read the two-byte Characteristics 371 | $characteristics = [UInt16]('0x{0}' -f (( $bytesImageNtHeaders[($offsChar + 1)..$offsChar] | ForEach-Object { $_.ToString('X2') }) -join '')) 372 | 373 | # Get the offset of the two-byte "Subsystem" attribute in the optional headers, and read that attribute 374 | $offsSubsystem = $offsetImageNtHeaders + $offset_OptionalHeader + $offset_OptionalHeader_Subsystem 375 | $subsystem = [UInt16]('0x{0}' -f (( $bytesImageNtHeaders[($offsSubsystem + 1)..$offsSubsystem] | ForEach-Object { $_.ToString('X2') }) -join '')) 376 | 377 | # Verify that Subsystem is IMAGE_SUBSYSTEM_WINDOWS_GUI or IMAGE_SUBSYSTEM_WINDOWS_CUI 378 | if ($subsystem -ne $IMAGE_SUBSYSTEM_WINDOWS_GUI -and $subsystem -ne $IMAGE_SUBSYSTEM_WINDOWS_CUI) { 379 | Write-Verbose "$filename : Not a Win32 EXE or DLL; Subsystem = $subsystem." 380 | #Write-Output "" 381 | return; 382 | } 383 | 384 | if ($characteristics -band 0x2000) { 385 | Write-Verbose "$filename : Win32 DLL; Subsystem = $subsystem." 386 | Write-Output "DLL" 387 | } 388 | else { 389 | Write-Verbose "$filename : Win32 EXE; Subsystem = $subsystem." 390 | Write-Output "EXE" 391 | } 392 | 393 | #if ($characteristics -band 0x0001) {"IMAGE_FILE_RELOCS_STRIPPED"} 394 | #if ($characteristics -band 0x0002) {"IMAGE_FILE_EXECUTABLE_IMAGE"} 395 | #if ($characteristics -band 0x0004) {"IMAGE_FILE_LINE_NUMS_STRIPPED"} 396 | #if ($characteristics -band 0x0008) {"IMAGE_FILE_LOCAL_SYMS_STRIPPED"} 397 | #if ($characteristics -band 0x0010) {"IMAGE_FILE_AGGRESIVE_WS_TRIM"} 398 | #if ($characteristics -band 0x0020) {"IMAGE_FILE_LARGE_ADDRESS_AWARE"} 399 | #if ($characteristics -band 0x0080) {"IMAGE_FILE_BYTES_REVERSED_LO"} 400 | #if ($characteristics -band 0x0100) {"IMAGE_FILE_32BIT_MACHINE"} 401 | #if ($characteristics -band 0x0200) {"IMAGE_FILE_DEBUG_STRIPPED"} 402 | #if ($characteristics -band 0x0400) {"IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP"} 403 | #if ($characteristics -band 0x0800) {"IMAGE_FILE_NET_RUN_FROM_SWAP"} 404 | #if ($characteristics -band 0x1000) {"IMAGE_FILE_SYSTEM"} 405 | #if ($characteristics -band 0x2000) {"IMAGE_FILE_DLL"} 406 | #if ($characteristics -band 0x4000) {"IMAGE_FILE_UP_SYSTEM_ONLY"} 407 | #if ($characteristics -band 0x8000) {"IMAGE_FILE_BYTES_REVERSED_HI"} 408 | } 409 | 410 | 411 | #################################################################################################### 412 | # Helper function used to replace current username with another in paths. 413 | #################################################################################################### 414 | function RenamePaths($paths, $forUsername) { 415 | # Warning: if $forUsername is "Users" that will be a problem. 416 | $forUsername = "\" + $forUsername 417 | # Look for username bracketed by backslashes, or at end of the path. 418 | $CurrentName = "\" + $env:USERNAME.ToLower() + "\" 419 | $CurrentNameFinal = "\" + $env:USERNAME.ToLower() 420 | 421 | $paths | ForEach-Object { 422 | $origTargetDir = $_ 423 | # Temporarily remove trailing \* if present; can't GetFullPath with that. 424 | if ($origTargetDir.EndsWith("\*")) { 425 | $bAppend = "\*" 426 | $targetDir = $origTargetDir.Substring(0, $origTargetDir.Length - 2) 427 | } 428 | else { 429 | $bAppend = "" 430 | $targetDir = $origTargetDir 431 | } 432 | # GetFullPath in case the provided name is 8.3-shortened. 433 | $targetDir = [System.IO.Path]::GetFullPath($targetDir).ToLower() 434 | if ($targetDir.Contains($CurrentName) -or $targetDir.EndsWith($CurrentNameFinal)) { 435 | $targetDir.Replace($CurrentNameFinal, $forUsername) + $bAppend 436 | } 437 | else { 438 | $origTargetDir 439 | } 440 | } 441 | } 442 | 443 | 444 | #################################################################################################### 445 | # Global variables - known file extensions 446 | #################################################################################################### 447 | # 448 | # With the -Directory switch, the Get-AppLockerFileInformation cmdlet inspects files with the extensions shown below (GetAlfiDefaultExts). 449 | # Create-Policies.ps1 (via BuildRulesForFilesInWritableDirectories.ps1) and Scan-Directories.ps1 inspect the content of other files to 450 | # determine whether any of them are Portable Executable files with non-standard extensions. To save the cost of reading in lots of files that 451 | # are never PE files (never should be, anyway), those scripts consume this script's output and doesn't inspect files with these extensions. 452 | # 453 | # NOTE THAT IF YOU EDIT THE NeverExecutableExts ARRAY: 454 | # * Make sure the script returns one array of strings: comma after each one except the last. 455 | # * Each extension must begin with a ".". 456 | # * Extensions cannot contain embedded dot characters. For example, a file named "lpc.win32.bundle" has the extension ".bundle" and not ".win32.bundle" 457 | # * Do NOT add any of the extensions that Get-AppLockerFileInformation searches. 458 | # * Order doesn't matter. 459 | # * Do not edit the GetAlfiDefaultExts array. 460 | # 461 | 462 | Set-Variable -Name GetAlfiDefaultExts -Option Constant -Value ".com", ".exe", ".dll", ".ocx", ".msi", ".msp", ".mst", ".bat", ".cmd", ".js", ".ps1", ".vbs", ".appx" 463 | Set-Variable -Name NeverExecutableExts -Option Constant -Value ` 464 | ".admx", ".adml", ".opax", ".opal", ".etl", ".evtx", ".msc", ".pdb", ".chm", ".hlp", 465 | ".gif", ".jpg", ".jpeg", ".png", ".bmp", ".svg", ".ico", ".pfm", ".ttf", ".fon", ".otf", ".cur", 466 | ".html", ".htm", ".hta", ".css", ".json", ".txt", ".log", ".xml", ".xsl", ".ini", ".csv", ".reg", ".mof", 467 | ".pdf", ".tif", ".tiff", ".xps", ".rtf", ".lnk", ".url", ".inf", 468 | ".odl", ".odlgz", ".odlsent", # OneDrive data files 469 | ".mui", # .mui is a DLL but it is always loaded as data-only, so no need for AppLocker rules 470 | ".doc", ".docx", ".docm", ".dot", ".dotx", ".dotm", # Microsoft Word 471 | ".xls", ".xlsx", ".xlsm", ".xlt", ".xltx", ".xltm", # Microsoft Excel 472 | ".ppt", ".pptx", ".pptm", ".pot", ".potx", ".potm", ".pps", ".ppsx", # Microsoft PowerPoint 473 | ".zip", ".7z", ".tar", 474 | ".wav", ".wmv", ".mp3", ".mp4", ".mpg", ".mpeg", ".avi", ".mov" 475 | 476 | Set-Variable -Name sNoPublisher -Option Constant -Value "-" 477 | Set-Variable -Name sUnsigned -Option Constant -Value "[not signed]" 478 | Set-Variable -Name sFiltered -Option Constant -Value "FILTERED" 479 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Aaron Parker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MergeRules-Static/AppLockerRules-Cisco WebEx Installer Publisher Rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /MergeRules-Static/AppLockerRules-Cisco WebEx Publisher Rules.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/MergeRules-Static/AppLockerRules-Cisco WebEx Publisher Rules.xml -------------------------------------------------------------------------------- /MergeRules-Static/AppLockerRules-GoToMeeting Publisher Rules.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/MergeRules-Static/AppLockerRules-GoToMeeting Publisher Rules.xml -------------------------------------------------------------------------------- /MergeRules-Static/AppLockerRules-Logitech Options Publisher Rules.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/MergeRules-Static/AppLockerRules-Logitech Options Publisher Rules.xml -------------------------------------------------------------------------------- /MergeRules-Static/AppLockerRules-Zoom Publisher Rules.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/MergeRules-Static/AppLockerRules-Zoom Publisher Rules.xml -------------------------------------------------------------------------------- /MergeRules-Static/README.md: -------------------------------------------------------------------------------- 1 | # Vendor Publisher Rules 2 | 3 | Rule sets for various publishers. These files should be copied into the `AaronLocker/MergeRules-Static` directory before running `Create-Policies.ps1`. 4 | -------------------------------------------------------------------------------- /MergeRules-Static/_AppLockerRules-Template Publisher Rules.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/MergeRules-Static/_AppLockerRules-Template Publisher Rules.xml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AppLocker 2 | 3 | AppLocker baseline configuration using the [AaronLocker](https://github.com/microsoft/AaronLocker) module with customisations. Used for testing AppLocker and Microsoft Defender Application Control with Windows 10, Intune etc. 4 | 5 | ## Additional configurations 6 | 7 | ### Inbox executables 8 | 9 | Additional inbox executables that Microsoft recommends blocking are found here: [Microsoft recommended block rules](https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/applications-that-can-bypass-appcontrol). These files can be tested for and formatting for adding to `GetExeFilesToDenyList.ps1` with: 10 | 11 | ```powershell 12 | $Files = @("addinprocess.exe", 13 | "addinprocess32.exe", 14 | "addinutil.exe", 15 | "aspnet_compiler.exe", 16 | "bash.exe", 17 | "bginfo.exe", 18 | "cdb.exe", 19 | "cscript.exe", 20 | "csi.exe", 21 | "dbghost.exe", 22 | "dbgsvc.exe", 23 | "dbgsrv.exe", 24 | "dnx.exe", 25 | "dotnet.exe", 26 | "fsi.exe", 27 | "fsiAnyCpu.exe", 28 | "infdefaultinstall.exe", 29 | "kd.exe", 30 | "kill.exe", 31 | "lxssmanager.dll", 32 | "lxrun.exe", 33 | "Microsoft.Build.dll", 34 | "Microsoft.Build.Framework.dll", 35 | "Microsoft.Workflow.Compiler.exe", 36 | "msbuild.exe", 37 | "msbuild.dll", 38 | "mshta.exe", 39 | "ntkd.exe", 40 | "ntsd.exe", 41 | "powershellcustomhost.exe", 42 | "rcsi.exe", 43 | "runscripthelper.exe", 44 | "texttransform.exe", 45 | "visualuiaverifynative.exe", 46 | "system.management.automation.dll", 47 | "webclnt.dll", 48 | "davsvc.dll", 49 | "wfc.exe", 50 | "windbg.exe", 51 | "wmic.exe", 52 | "wscript.exe", 53 | "wsl.exe", 54 | "wslconfig.exe", 55 | "wslhost.exe") 56 | 57 | $Executables = @() 58 | Get-ChildItem -Path "$Env:SystemRoot\Microsoft.NET" -Include $files -Recurse -ErrorAction "SilentlyContinue" | ForEach-Object { $Executables += $_ } 59 | $Executables | Select-Object -ExpandProperty "FullName" | Select-Object -Unique | Sort-Object | Set-Clipboard 60 | ``` 61 | -------------------------------------------------------------------------------- /configs/README.md: -------------------------------------------------------------------------------- 1 | # Configs 2 | 3 | Configurations for fresh installs of Windows versions. These configurations have been generated from Windows 11 and Windows 10 Entra joined devices, provisioned via Windows Autopilot. The latest Microsoft 365 Apps for enterprise, Microsoft Teams and OneDrive are installed on each. 4 | -------------------------------------------------------------------------------- /configs/Windows10/22H2/AppLockerRules-20231105-2334-Audit.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/configs/Windows10/22H2/AppLockerRules-20231105-2334-Audit.xml -------------------------------------------------------------------------------- /configs/Windows10/22H2/AppLockerRules-20231105-2334-Enforce.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/configs/Windows10/22H2/AppLockerRules-20231105-2334-Enforce.xml -------------------------------------------------------------------------------- /configs/Windows11/22H2/AppLockerRules-20231105-2333-Audit.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/configs/Windows11/22H2/AppLockerRules-20231105-2333-Audit.xml -------------------------------------------------------------------------------- /configs/Windows11/22H2/AppLockerRules-20231105-2333-Enforce.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronparker/AppLocker/e3b02d829792efc4d0c5c16a258fe8699ca84893/configs/Windows11/22H2/AppLockerRules-20231105-2333-Enforce.xml -------------------------------------------------------------------------------- /configs/Windows11/22H2/AppLockerRules-20231105-AppX-Enforce.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /configs/Windows11/22H2/AppLockerRules-20231105-Msi-Enforce.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /configs/Windows11/22H2/AppLockerRules-20231105-Script-Enforce.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /lolbins.txt: -------------------------------------------------------------------------------- 1 | # https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/design/applications-that-can-bypass-appcontrol 2 | addinprocess.exe 3 | addinprocess32.exe 4 | addinutil.exe 5 | aspnet_compiler.exe 6 | bash.exe 7 | bginfo.exe 8 | cdb.exe 9 | cscript.exe 10 | csi.exe 11 | dbghost.exe 12 | dbgsvc.exe 13 | dbgsrv.exe 14 | dnx.exe 15 | dotnet.exe 16 | fsi.exe 17 | fsiAnyCpu.exe 18 | infdefaultinstall.exe 19 | kd.exe 20 | kill.exe 21 | lxssmanager.dll 22 | lxrun.exe 23 | Microsoft.Build.dll 24 | Microsoft.Build.Framework.dll 25 | Microsoft.Workflow.Compiler.exe 26 | msbuild.exe 27 | msbuild.dll 28 | mshta.exe 29 | ntkd.exe 30 | ntsd.exe 31 | powershellcustomhost.exe 32 | rcsi.exe 33 | runscripthelper.exe 34 | texttransform.exe 35 | visualuiaverifynative.exe 36 | system.management.automation.dll 37 | webclnt.dll 38 | davsvc.dll 39 | wfc.exe 40 | windbg.exe 41 | wmic.exe 42 | wscript.exe 43 | wsl.exe 44 | wslconfig.exe 45 | wslhost.exe 46 | -------------------------------------------------------------------------------- /scripts/Get-Signatures.ps1: -------------------------------------------------------------------------------- 1 | # Specify paths 2 | $Paths = @("$Env:LocalAppData\DocNGen", "$Env:LocalAppData\assembly\dl3", "$Env:LocalAppData\Apps\2.0") 3 | 4 | $Paths = "$Env:LocalAppData\1Password" 5 | # Get the files and present in Out-GridView 6 | Get-ChildItem -Path $Paths -Recurse -Include "*.dll","*.exe" | ForEach-Object { 7 | Get-AuthenticodeSignature -FilePath $_.FullName | Select-Object -Property "Path", "Status", "StatusMessage" 8 | } | Out-GridView 9 | -------------------------------------------------------------------------------- /scripts/mdac.ps1.ps: -------------------------------------------------------------------------------- 1 | Set-StrictMode -Version 'Latest' 2 | $ErrorActionPreference = 'Stop' 3 | $( try { 4 | $guid = '{d26bff32-33a2-48a3-b037-10357ee48427}' 5 | $xml = "C:\Windows\System32\CodeIntegrity\CiPolicies\Active\${guid}.xml" 6 | $binary = "C:\Windows\System32\CodeIntegrity\CiPolicies\Active\${guid}.cip" 7 | Copy-Item -LiteralPath 'C:\Windows\schemas\CodeIntegrity\ExamplePolicies\DefaultWindows_Enforced.xml' -Destination $xml 8 | Set-RuleOption -FilePath $xml -Option 0 9 | Set-RuleOption -FilePath $xml -Option 6 10 | Set-RuleOption -FilePath $xml -Option 9 11 | Set-RuleOption -FilePath $xml -Option 16 12 | Set-RuleOption -FilePath $xml -Option 18 13 | Set-RuleOption -FilePath $xml -Option 5 -Delete 14 | Set-RuleOption -FilePath $xml -Option 10 15 | Merge-CIPolicy -PolicyPaths $xml -OutputFilePath $xml -Rules $( @( New-CIPolicyRule -FilePathRule 'C:\Windows\*' 16 | New-CIPolicyRule -FilePathRule 'C:\Program Files\*' 17 | New-CIPolicyRule -FilePathRule 'C:\Program Files (x86)\*' 18 | New-CIPolicyRule -FilePathRule '%WINDIR%\debug\WIA\*' -Deny 19 | New-CIPolicyRule -FilePathRule '%WINDIR%\PLA\Reports\*' -Deny 20 | New-CIPolicyRule -FilePathRule '%WINDIR%\PLA\Rules\*' -Deny 21 | New-CIPolicyRule -FilePathRule '%WINDIR%\PLA\Templates\*' -Deny 22 | New-CIPolicyRule -FilePathRule '%WINDIR%\Registration\CRMLog\*' -Deny 23 | New-CIPolicyRule -FilePathRule '%WINDIR%\System32\Com\dmp\*' -Deny 24 | New-CIPolicyRule -FilePathRule '%WINDIR%\System32\FxsTmp\*' -Deny 25 | New-CIPolicyRule -FilePathRule '%WINDIR%\System32\LogFiles\WMI\*' -Deny 26 | New-CIPolicyRule -FilePathRule '%WINDIR%\System32\Microsoft\Crypto\RSA\MachineKeys\*' -Deny 27 | New-CIPolicyRule -FilePathRule '%WINDIR%\System32\spool\drivers\color\*' -Deny 28 | New-CIPolicyRule -FilePathRule '%WINDIR%\System32\spool\PRINTERS\*' -Deny 29 | New-CIPolicyRule -FilePathRule '%WINDIR%\System32\spool\SERVERS\*' -Deny 30 | New-CIPolicyRule -FilePathRule '%WINDIR%\System32\Tasks_Migrated\*' -Deny 31 | New-CIPolicyRule -FilePathRule '%WINDIR%\System32\Tasks\*' -Deny 32 | New-CIPolicyRule -FilePathRule '%WINDIR%\SysWOW64\Com\dmp\*' -Deny 33 | New-CIPolicyRule -FilePathRule '%WINDIR%\SysWOW64\FxsTmp\*' -Deny 34 | New-CIPolicyRule -FilePathRule '%WINDIR%\SysWOW64\Tasks\*' -Deny 35 | New-CIPolicyRule -FilePathRule '%WINDIR%\Tasks\*' -Deny 36 | New-CIPolicyRule -FilePathRule '%WINDIR%\Temp\*' -Deny 37 | New-CIPolicyRule -FilePathRule '%WINDIR%\tracing\*' -Deny 38 | ) | ForEach-Object -Process { $_ 39 | } 40 | ) 41 | $doc = [xml]::new() 42 | $doc.Load( $xml ) 43 | $nsmgr = [System.Xml.XmlNamespaceManager]::new( $doc.NameTable ) 44 | $nsmgr.AddNamespace( 'pol', 'urn:schemas-microsoft-com:sipolicy' ) 45 | $doc.SelectSingleNode( '/pol:SiPolicy/pol:PolicyID', $nsmgr ).InnerText = $guid 46 | $doc.SelectSingleNode( '/pol:SiPolicy/pol:BasePolicyID', $nsmgr ).InnerText = $guid 47 | $node = $doc.SelectSingleNode( '//pol:SigningScenario[@Value="12"]/pol:ProductSigners/pol:AllowedSigners', $nsmgr ) 48 | $node.ParentNode.RemoveChild( $node ) 49 | $doc.Save( $xml ) 50 | ConvertFrom-CIPolicy -XmlFilePath $xml -BinaryFilePath $binary 51 | } 52 | catch { 53 | $_ 54 | } ) *>&1 | Out-File -Append -FilePath "$env:TEMP\wdac.log" 55 | --------------------------------------------------------------------------------