├── .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 |
--------------------------------------------------------------------------------