|
#Requires -Version 3.0 |
|
|
|
#region Score the identity |
|
Function Get-ApplockerRuleUserOrGroupSidScore { |
|
[CmdletBinding()] |
|
[OutputType([int32])] |
|
Param( |
|
[Parameter(Mandatory)] |
|
[Alias('UserOrGroupSid','SID')] |
|
$InputObject |
|
) |
|
Begin {} |
|
Process { |
|
$InputObject | |
|
ForEach-Object { |
|
Switch -Regex ($_) { |
|
'S-1-1-0' { # EveryOne |
|
Write-Verbose -Message 'Applocker score: 50 ; Everyone' |
|
50 ; break |
|
} |
|
'S-1-5-32-544' { # Administrators |
|
Write-Verbose -Message 'Applocker score: 10 ; Administrators' |
|
10 ; break |
|
} |
|
'S-1-5-32-545' { # Users |
|
Write-Verbose -Message 'Applocker score: 10 ; Users' |
|
10 ; break |
|
} |
|
# S-1-5-11 # Authenticated Users |
|
# S-1-5-32-546 # Guests |
|
# S-1-5-32-547 # Power Users |
|
default { |
|
Write-Verbose -Message 'Applocker score: Id is specific' |
|
10 |
|
} |
|
} |
|
} |
|
} |
|
End {} |
|
} |
|
#endregion |
|
|
|
#region Score the rule type |
|
Function Get-ApplockerRuleTypeScore { |
|
[CmdletBinding()] |
|
[OutputType([int32])] |
|
Param( |
|
[Parameter(Mandatory)] |
|
[string[]]$InputObject |
|
) |
|
Begin {} |
|
Process { |
|
$InputObject | |
|
ForEach-Object { |
|
Switch -Regex ($_) { |
|
'^<FilePath(Rule|Condition)' { Write-Verbose -Message 'Applocker score: 100 ; Path type' ; 100 } |
|
'^<FilePublisher(Rule|Condition)' { Write-Verbose -Message 'Applocker score: 50 ; Path publisher' ; 50} |
|
'^<FileHash(Rule|Condition)' {Write-Verbose -Message 'Applocker score: 50 ; Path Hash' ; 50} |
|
default {} |
|
} |
|
} |
|
} |
|
End {} |
|
} |
|
|
|
#endregion |
|
|
|
#region Score the rule action |
|
Function Get-ApplockerRuleActionScore { |
|
[CmdletBinding()] |
|
[OutputType([int32])] |
|
Param( |
|
[Parameter(Mandatory)] |
|
[string[]]$InputObject |
|
) |
|
Begin {} |
|
Process { |
|
$InputObject | |
|
ForEach-Object { |
|
Switch -Regex ($_) { |
|
'\sAction="Allow"' { |
|
Write-Verbose -Message 'Applocker score: *1 ; Action is Allow' |
|
1 |
|
} |
|
'\sAction="Deny"' { |
|
Write-Verbose -Message 'Applocker score: /10 ; Action is Deny' |
|
0.1 |
|
} |
|
default {} |
|
} |
|
} |
|
} |
|
End {} |
|
} |
|
#endregion |
|
|
|
#region Score the rule conditions |
|
Function Get-ApplockerRuleConditionsScore { |
|
[CmdletBinding()] |
|
[OutputType([int32])] |
|
Param( |
|
[Parameter(Mandatory)] |
|
[string[]]$InputObject |
|
) |
|
Begin {} |
|
Process { |
|
$InputObject | |
|
ForEach-Object { |
|
($( |
|
Switch -Regex ($_) { |
|
'^<FilePath(Rule|Condition)' { |
|
Write-Verbose -Message 'Applocker score: Path type' |
|
|
|
# Depth: count the occurrence of '\' |
|
$depth = 110 - (($_ -split '\\').Count)*10 |
|
Write-Verbose -Message "Depth score is $($depth)" |
|
$depth |
|
|
|
# Evaluate if there's a wildcard in the path |
|
if ($_ -match '\\\*\\') { |
|
50 |
|
} |
|
|
|
Switch -Regex ($_) { |
|
|
|
# Just a specific extension |
|
# .exe, .com *.msi, msp mst ps1 cmd bat vbs js appx msix dll ocx |
|
'\sPath="\*\.([eE][xX][eE]|[cC][oO][mM]|[mM][sS][iI]|[mM][sS][pP]|[mM][sS][tT]|[pP][sS]1|[cC][mM][dD]|[bB][aA][tT]|[vV][bB][sS]|[jJ][sS])"\s' { |
|
Write-Verbose -Message "Path score: 500 (only a *.ext)" |
|
500 |
|
} |
|
|
|
# Path letter |
|
'\sPath="(c|C):\\' { |
|
Write-Verbose -Message "Path score: 100 (Path on C: drive)" |
|
100 |
|
} |
|
'\sPath="[abABd-zD-Z]:\\' { |
|
Write-Verbose -Message "Path score: 100 (Path not on C: drive)" |
|
50 |
|
} |
|
# Extension: just a wildcard |
|
'\sPath="\*\.\*"\s' { # *.* |
|
Write-Verbose -Message "Path score: 1000 (only a *.*)" |
|
1000 ; break |
|
} |
|
|
|
'\sPath="\*"\s' { # * |
|
Write-Verbose -Message "Path score: 1000 (only a *)" |
|
1000 ; break |
|
} |
|
default {} |
|
} |
|
} |
|
'^<FilePublisher(Rule|Condition)' { |
|
Write-Verbose -Message 'Applocker score: Path publisher' |
|
|
|
Switch -Regex ($_) { |
|
'PublisherName="\*"\sProductName="\*"\sBinaryName="\*"\>\<BinaryVersionRange\sLowSection="0\.0\.0\.0"\sHighSection="\*"' { |
|
Write-Verbose -Message "Publisher score: 1000 (default Appx PublisherName=*)" |
|
1000 ; break |
|
} |
|
|
|
'ProductName="\*"\sBinaryName="\*"\>\<BinaryVersionRange\sLowSection="\*"\sHighSection="\*"' { |
|
Write-Verbose -Message "Publisher score: 500 (only a PublisherName)" |
|
500 ; break |
|
} |
|
'\sBinaryName="\*"\>\<BinaryVersionRange\sLowSection="\*" HighSection="\*"' { |
|
Write-Verbose -Message "Publisher score: 500 (PublisherName and ProductName)" |
|
200 ; break |
|
} |
|
'BinaryVersionRange\sLowSection="\*"\sHighSection="\*"' { |
|
Write-Verbose -Message "Publisher score: 100 (PublisherName and ProductName and FileName)" |
|
100 ; break |
|
} |
|
default { |
|
Write-Verbose -Message "Publisher score: 50 (default)" |
|
50 |
|
} |
|
} |
|
} |
|
'^<FileHash(Rule|Condition)' { |
|
Write-Verbose -Message "Hash score: 50" |
|
50 |
|
} |
|
default {} |
|
} |
|
) | Measure-Object -Sum).Sum |
|
} |
|
} |
|
End {} |
|
} |
|
#endregion |
|
|
|
#region Convert to Object from Xml |
|
Function Get-ApplockerRuleObjectFromXml { |
|
[CmdletBinding()] |
|
[OutputType([int32])] |
|
Param( |
|
[Parameter(Mandatory)] |
|
[string[]]$InputObject |
|
) |
|
Begin {} |
|
Process { |
|
$InputObject | |
|
ForEach-Object { |
|
$o = $_ |
|
Switch -Regex ($_) { |
|
'^<FilePathRule' { |
|
try { |
|
[Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.FilePathRule]::FromXml($_) |
|
} catch { |
|
Write-Warning -Message "Failed to translate $($o) to Applocker FilePathRule because $($_.Exception.Message)" |
|
} |
|
break |
|
} |
|
'^<FilePublisherRule' { |
|
try { |
|
[Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.FilePublisherRule]::FromXml($_) |
|
} catch { |
|
Write-Warning -Message "Failed to translate $($o) to Applocker FilePublisherRule because $($_.Exception.Message)" |
|
} |
|
break |
|
} |
|
'^<FileHashRule' { |
|
try { |
|
[Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.FileHashRule]::FromXml($_) |
|
} catch { |
|
Write-Warning -Message "Failed to translate $($o) to Applocker FilePublisherRule because $($_.Exception.Message)" |
|
} |
|
break |
|
} |
|
default { |
|
Write-Warning -Message 'Failed to transtale input into an Applocker rule' |
|
} |
|
} |
|
} |
|
} |
|
End {} |
|
} |
|
#endregion |
|
|
|
Function Get-ApplockerPolicyRuleScore { |
|
<# |
|
.SYNOPSIS |
|
Score an Applocker policy rule |
|
|
|
.DESCRIPTION |
|
Score an Applocker policy rule |
|
|
|
.PARAMETER Type |
|
Parameter that indicates the RuleCollectionType to find a rule |
|
|
|
.PARAMETER Rule |
|
Dynamic parameter that indicates the Name of the rule to score |
|
|
|
.EXAMPLE |
|
Get-ApplockerPolicyRuleScore -Type Script -Rule '(Default Rule) All scripts' |
|
|
|
Id Name Score XML |
|
-- ---- ----- --- |
|
ed97d0cb-15ff-430f-b82c-8d7832957725 (Default Rule) All scripts 1210 <FilePathRule Id |
|
|
|
.EXAMPLE |
|
$rule = Get-ApplockerPolicyRuleScore -Type Script -Rule '(Default Rule) All scripts' -Verbose |
|
Write-Verbose -Message "Final score for '$($rule.Name)' is $($rule.Score)" -Verbose |
|
$rule.Object |
|
|
|
VERBOSE: Dealing with Rule Collection type: Script |
|
VERBOSE: Dealing with Rule Name: (Default Rule) All scripts |
|
VERBOSE: Applocker score: 100 ; Path type |
|
VERBOSE: Applocker score: *1 ; Action is Allow |
|
VERBOSE: Applocker score: 10 ; Administrators |
|
VERBOSE: Applocker score: Path type |
|
VERBOSE: Depth score is 100 |
|
VERBOSE: Path score: 1000 (only a *) |
|
VERBOSE: Final score for '(Default Rule) All scripts' is 1210 |
|
|
|
PathConditions : {*} |
|
PathExceptions : {} |
|
PublisherExceptions : {} |
|
HashExceptions : {} |
|
Id : ed97d0cb-15ff-430f-b82c-8d7832957725 |
|
Name : (Default Rule) All scripts |
|
Description : Allows members of the local Administrators group to run all scripts. |
|
UserOrGroupSid : S-1-5-32-544 |
|
Action : Allow |
|
|
|
#> |
|
[CmdletBinding()] |
|
Param( |
|
[ValidateSet('Exe','Script','Msi','Appx','Dll')] |
|
[Parameter(Mandatory)] |
|
[String]$Type |
|
) |
|
DynamicParam { |
|
|
|
$Dictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary |
|
|
|
#region helper function |
|
Function New-ParameterAttributCollection { |
|
[CmdletBinding(SupportsShouldProcess)] |
|
Param( |
|
[Switch]$Mandatory, |
|
[Switch]$ValueFromPipeline, |
|
[Switch]$ValueFromPipelineByPropertyName, |
|
[String]$ParameterSetName, |
|
|
|
[Parameter()] |
|
[ValidateSet( |
|
'Arguments','Count','Drive','EnumeratedArguments','Length','NotNull', |
|
'NotNullOrEmpty','Pattern','Range','Script','Set','UserDrive' |
|
)][string]$ValidateType, |
|
|
|
[Parameter()] |
|
$ValidationContent |
|
|
|
) |
|
Begin {} |
|
Process { |
|
if ($PSCmdlet.ShouldProcess('Create new Attribute')) { |
|
$c = New-Object System.Collections.ObjectModel.Collection[System.Attribute] |
|
$a = New-Object System.Management.Automation.ParameterAttribute |
|
if ($Mandatory) { |
|
$a.Mandatory = $true |
|
} |
|
if ($ValueFromPipeline) { |
|
$a.ValueFromPipeline = $true |
|
} |
|
if ($ValueFromPipelineByPropertyName) { |
|
$a.ValueFromPipelineByPropertyName=$true |
|
} |
|
if ($ParameterSetName) { |
|
$a.ParameterSetName = $ParameterSetName |
|
} |
|
$c.Add($a) |
|
|
|
if ($ValidateType -and $ValidationContent) { |
|
try { |
|
$c.Add((New-Object "System.Management.Automation.Validate$($ValidateType)Attribute"( |
|
$ValidationContent |
|
))) |
|
} catch { |
|
Throw $_ |
|
} |
|
} |
|
$c |
|
} |
|
} |
|
End {} |
|
} |
|
#endregion |
|
|
|
try { |
|
$ApplockerPolicyXml = [xml](Get-AppLockerPolicy -Effective -Xml -ErrorAction Stop) |
|
} catch { |
|
Throw 'Failed to read the effective Applocker policy into XML' |
|
} |
|
|
|
#region param Rule |
|
$Dictionary.Add( |
|
'Rule', |
|
(New-Object System.Management.Automation.RuntimeDefinedParameter( |
|
'Rule', |
|
[string], |
|
(New-ParameterAttributCollection -Mandatory -ValidateType Set -ValidationContent ( |
|
$ApplockerPolicyXml.SelectNodes("/AppLockerPolicy/RuleCollection[@Type='$($PSBoundParameters['Type'])']").ChildNodes| ForEach-Object { $_.Name } |
|
)) |
|
)) |
|
) |
|
$Dictionary |
|
} |
|
Begin {} |
|
Process { |
|
|
|
Write-Verbose -Message "Dealing with Rule Collection type: $($PSBoundParameters['Type'])" |
|
Write-Verbose -Message "Dealing with Rule Name: $($PSBoundParameters['Rule'])" |
|
|
|
# Select node |
|
$n = $ApplockerPolicyXml.SelectNodes("/AppLockerPolicy/RuleCollection[@Type='$($PSBoundParameters['Type'])']").ChildNodes | |
|
Where-Object { $_.Name -eq "$($PSBoundParameters['Rule'])" } |
|
|
|
try { |
|
$TypeScore = Get-ApplockerRuleTypeScore -InputObject $n.OuterXml |
|
$ActionSore = Get-ApplockerRuleActionScore -InputObject $n.OuterXml |
|
$IdScore = Get-ApplockerRuleUserOrGroupSidScore -InputObject $n.OuterXml |
|
$ConditionScore = Get-ApplockerRuleConditionsScore -InputObject $n.OuterXml |
|
$r = [PSCustomObject]@{ |
|
Id = $n.Id |
|
Name = $n.Name |
|
Score = ($TypeScore+$IdScore+$ConditionScore)*$ActionSore |
|
XML = $n.OuterXml |
|
Object = Get-ApplockerRuleObjectFromXml -InputObject $n.OuterXml |
|
} |
|
|
|
Update-TypeData -TypeName 'Applocker.Rule.Score' -DefaultDisplayPropertySet 'Id','Name','Score','XML' -Force |
|
$r.PSTypeNames.Insert(0,'Applocker.Rule.Score') |
|
$r |
|
} catch { |
|
Throw "Something went wrong while scoring applocker rule: $($_.Exception.Message)" |
|
} |
|
} |
|
End {} |
|
} # endof Get-ApplockerPolicyRuleScore |
|
|
|
Export-ModuleMember -Function 'Get-ApplockerPolicyRuleScore' |