Module/Rule.Permission/Convert/Methods.ps1
# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. #region Method Functions <# .SYNOPSIS Parses the rawString from the rule to retrieve the PermissionTargetPath #> function Get-PermissionTargetPath { [CmdletBinding()] [OutputType([string])] param ( [Parameter(Mandatory = $true)] [AllowEmptyString()] [string[]] $StigString ) Write-Verbose "[$($MyInvocation.MyCommand.Name)]" switch ($stigString) { # Do not use $env: for environment variables. They will not be able to be converted to text for XML. # Get path for permissions that pertains to event logs { $stigString -match $regularExpression.WinEvtDirectory } { $parentheseMatch = $stigString | Select-String -Pattern $regularExpression.eventLogName if ( $stigString -match $regularExpression.dnsServerLog ) { $childPath = 'DNS Server.evtx' } else { $childPath = $parentheseMatch.Matches.Groups[-1].Value.trim() } $permissionTargetPath = '%windir%\SYSTEM32\WINEVT\LOGS\' + $childPath break } # Get path for permissions that pertains to eventvwr.exe { $stigString -match $regularExpression.eventViewer } { $permissionTargetPath = '%windir%\SYSTEM32\eventvwr.exe' break } # Get path that pertains to C:\ { $stigString -match $regularExpression.cDrive } { $permissionTargetPath = '%SystemDrive%\' break } # Get path that pertains to Sysvol { $stigString -match $regularExpression.SysVol} { $permissionTargetPath = '%windir%\sysvol' break } # Get path that pertains to C:\Windows { $stigString -match $regularExpression.systemRoot } { $permissionTargetPath = '%windir%' break } # Get path that pertains to registry Installed Components key { $stigString -match $regularExpression.permissionRegistryInstalled } { $permissionTargetPath = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Active Setup\Installed Components\' break } # Get path that pertains to registry Winlogon key { $stigString -match $regularExpression.permissionRegistryWinlogon } { $permissionTargetPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\' break } # Get path that pertains to registry WinReg key { $stigString -match $regularExpression.permissionRegistryWinreg } { $permissionTargetPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg\' break } # Get path that pertains to registry NTDS key { $stigString -match $regularExpression.permissionRegistryNTDS } { $permissionTargetPath = '%windir%\NTDS\*.*' break } # Get path that pertains to both program files directories { $stigString -match $regularExpression.programFiles } { $permissionTargetPath = '%ProgramFiles%;%ProgramFiles(x86)%' break } # Get crypto folder path { $stigString -match $regularExpression.cryptoFolder } { $permissionTargetPath = '%ALLUSERSPROFILE%\Microsoft\Crypto\Keys' break } # Get path that pertains to Admin Shares { $stigString -match $regularExpression.adminShares } { $permissionTargetPath = $null break } # Get Active Directory Path { $stigString -match $regularExpression.ADAuditPath } { $ADPath = (Select-String -InputObject $stigString -Pattern $regularExpression.ADAuditPath) -replace $regularExpression.ADAuditPath, "" -replace " object.*", "" $permissionTargetPath = $aDAuditPath.$($ADPath.Trim()) break } # Get HKLM\Security path { $stigString -match $regularExpression.hklmSecurity -and $stigString -match $regularExpression.hklmSoftware -and $stigString -match $regularExpression.hklmSystem } { $permissionTargetPath = 'HKLM:\SECURITY;HKLM:\SOFTWARE;HKLM:\SYSTEM' break } # Get the individual HKLM paths { $stigString -match $regularExpression.hklmSecurity } { $permissionTargetPath = 'HKLM:\SECURITY' break } { $stigString -match $regularExpression.hklmSoftware } { $permissionTargetPath = 'HKLM:\SOFTWARE' break } { $stigString -match $regularExpression.hklmSystem } { $permissionTargetPath = 'HKLM:\SYSTEM' break } # Get path for C:, Program file, and Windows { $stigString -match $regularExpression.rootOfC -and $stigString -match $regularExpression.winDir -and $stigString -match $regularExpression.programFilesWin10 } { $permissionTargetPath = '%SystemDrive%;%ProgramFiles%;%Windir%' break } { $stigString -match $regularExpression.rootOfC -and $stigString -notmatch $regularExpression.winDir -and $stigString -notmatch $regularExpression.programFileFolder } { $permissionTargetPath = '%SystemDrive%\' break } { $stigString -match $regularExpression.winDir } { $permissionTargetPath = '%Windir%' break } { $stigString -match $regularExpression.programFileFolder } { $permissionTargetPath = '%ProgramFiles%' break } { $stigString -match $regularExpression.programFiles86 } { $permissionTargetPath = '%ProgramFiles(x86)%' break } { $stigString -match $regularExpression.inetpub } { $permissionTargetPath = '%SystemDrive%\inetpub' break } # SQL Server install folder { $stigString -match $regualrExpression.sqlInstallDirectory } { # ToDo since this is going to be an OrgSetting need to populate test string here if possible $permissionTargetPath = $null break } default { break } } return $permissionTargetPath } <# .SYNOPSIS This function calls ConvertTo-AccessControlEntry but allows to get AccessControlEntry objects, however this allows us to handle edge cases in the rawString from the xccdf. #> function Get-PermissionAccessControlEntry { [CmdletBinding()] [OutputType([string])] param ( [Parameter(Mandatory = $true)] [psobject] $StigString ) Write-Verbose "[$($MyInvocation.MyCommand.Name)]" switch ($stigString) { { $stigString -match $regularExpression.permissionRegistryWinlogon } { <# Permission rule that pertains to HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\ This rule has an edge case which specifies the same inheritance to all the principals and is not in the same format as the other rules. #> return ConvertTo-AccessControlEntry -StigString $stigString -inheritanceInput 'This key and subkeys' } { $stigString -match $regularExpression.InheritancePermissionMap } { return ConvertTo-AccessControlEntryIF -StigString $stigString } { $stigString -join " " -match $regularExpression.TypePrincipalAccess } { return ConvertTo-AccessControlEntryGrouped -StigString $stigString } { $stigString -match $regularExpression.cryptoFolder } { $cryptoFolderStigString = "SYSTEM, Administrators - Full Control - This folder, subfolders and files" return ConvertTo-AccessControlEntry -StigString $cryptoFolderStigString } { $stigString -match $regularExpression.inetpub } { # In IIS Server Stig rule V-76745 says creator/owner should have special permissions to subkeys so we ignore it. All rules that are properly documented are converted $inetpubFolderStigString = @() foreach ($line in $stigString) { if ($line -notMatch "Creator/Owner" -and $line -match ":") { $inetpubFolderStigString += ($line -replace ': ', ' - ') -replace '\(built-in security group\)' } } return ConvertTo-AccessControlEntry -StigString $inetpubFolderStigString } { $stigString -match $regularExpression.auditingTab } { return ConvertTo-FileSystemAuditRule -CheckContent $stigString } default { return ConvertTo-AccessControlEntry -StigString $stigString } } } <# .SYNOPSIS This function converts the raw text from the STIG rule to a hashtable with the following keys: Principal,FileSystemRights, and Inheritance. This is to handle scenarios where a target has multiple principals assigned permissions to it. #> function ConvertTo-AccessControlEntryGrouped { [CmdletBinding()] [OutputType([hashtable])] param ( [Parameter(Mandatory = $true)] [psobject] $StigString ) $accessControlEntryPrincipal = $stigString | Select-String -Pattern "Principal\s*-" $accessControlEntryType = $stigString | Select-String -Pattern "Type\s*-" $accessControlEntryAccess = $stigString | Select-String -Pattern "Access\s*-" $accessControlEntryApplies = $stigString | Select-String -Pattern "Applies To\s*-" $accessControlEntrySpecial = $stigString | Select-String -Pattern "\(Access - Special\s*" foreach ($entry in $accessControlEntryType) { $type = ($entry.ToString() -Split "-")[1].Trim() $principalObject = $accessControlEntryPrincipal | Where-Object {$PSItem.LineNumber -gt $entry.LineNumber} | Sort-Object -Property LineNumber |Select-Object -First 1 $principal = ($principalObject.ToString() -split '-')[1].Trim() $rightsObject = $accessControlEntryAccess | Where-Object {$PSItem.LineNumber -gt $entry.LineNumber} | Sort-Object -Property LineNumber | Select-Object -First 1 $rights = ($RightsObject.ToString() -split "-")[1].Trim() $inheritanceObject = $accessControlEntryApplies | Where-Object {$PSItem.LineNumber -gt $RightsObject.LineNumber} | Sort-Object -Property LineNumber | Select-Object -First 1 if ($inheritanceObject) { $inheritance = ($InheritanceObject.ToString() -split "-")[1].Trim() } else { $inheritance = "" } if ($rights -eq "Special") { $specialPermissions = $accessControlEntrySpecial | Where-Object {$PSItem.LineNumber -gt $rightsObject.LineNumber} | Sort-Object -Property LineNumber | Select-Object -First 1 if ($specialPermissions.ToString().Contains(':')) { $rights = ($specialPermissions -split ':')[1].Trim() } else { $rights = ($specialPermissions -split '=')[1].Trim() } $rights = $rights.Substring(0,$rights.Length -1) } $accessControlEntries += [pscustomobject[]]@{ Principal = $principal ForcePrincipal = Get-ForcePrincipal -StigString $stigString Rights = Convert-RightsConstant -RightsString $rights Inheritance = $inheritanceConstant[[string]$inheritance.trim()] Type = $type } } return $accessControlEntries } <# .SYNOPSIS Converts permission rules entries that have an inheritance mapping #> function ConvertTo-AccessControlEntryIF { [CmdletBinding()] [OutputType([hashtable])] param ( [Parameter(Mandatory = $true)] [psobject] $StigString ) $accessControlEntryMatches = $stigString | Select-String -Pattern $regularExpression.InheritancePermissionMap $permissions = $stigString | Select-String -Pattern $regularExpression.PermissionRuleMap foreach ($entry in $accessControlEntryMatches) { $entry = $entry -replace ':', ' - ' -replace '\)\s*\(', ') - (' foreach ($permission in $permissions) { $perm = $permission -split '-' $perm[0] = $perm[0] -replace '\(','\(' -replace '\)','\)' $entry = $entry -replace $perm[0].Trim(), $perm[1].Trim() } $principal, [string]$inheritance, $fileSystemRights = $entry -split $regularExpression.spaceDashSpace if (-not $inheritanceConstant[[string]$inheritance.trim()]) { $inheritance = "" } else { $inheritance = $inheritanceConstant[[string]$inheritance.trim()] } $accessControlEntries += [pscustomobject[]]@{ Principal = $principal.trim() ForcePrincipal = Get-ForcePrincipal -StigString $stigString Rights = Convert-RightsConstant -RightsString $fileSystemRights Inheritance = $inheritance } } return $accessControlEntries } <# .SYNOPSIS Converts the raw text from the STIG rule hashtable with the following keys: Principal,FileSystemRights, and Inheritance. #> function ConvertTo-AccessControlEntry { [CmdletBinding()] [OutputType([hashtable])] param ( [Parameter(Mandatory = $true)] [psobject] $StigString, [Parameter()] [string] $InheritanceInput ) $accessControlEntryMatches = $stigString | Select-String -Pattern $regularExpression.spaceDashSpace if ($stigString -match 'S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681') { $accessControlEntryMatches += 'S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681 - Read - This key and subkeys' } foreach ( $entry in $accessControlEntryMatches ) { if ( $entry -notmatch 'Type|Inherited|Columns|Principal|Applies' ) { <# Access control entries are commonly formatted like so: 'Principal - FileSystemRights - Inheritance we will split on a regex pattern the represents space dash space ( - ) #> $principals, $fileSystemRights, [string]$inheritance = $entry -split $regularExpression.spaceDashSpace if ( $fileSystemRights -match ([RegularExpression]::TextBetweenParentheses) ) { $inheritance = [regex]::Match( $fileSystemRights, ([RegularExpression]::TextBetweenParentheses) ).groups[1].Value $fileSystemRights = ($fileSystemRights -split '\(')[0] } # There is an edge case V-63593 which states the rights should be 'Special' but it doesn't state what the special rights should be so we ignore it. if ( $stigString -match $regularExpression.hklmRootKeys -and $fileSystemRights.Trim() -eq 'Special') { break } <# There is an edge case in V-26070 where the inheritance is specified in the rule outside of the common format V-26070 states the inheritance is to be applied to all the prinicpals. So if an inheritance is passed in from the Inheritance parameter we applied to all the prinicipals. If not we parse the rawString to extract the inheritance. #> if ( $inheritanceInput ) { $inheritance = $inheritanceInput } if ($entry -match 'S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681') { $Principal = 'S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681' } foreach ( $principal in $principals -split ',' ) { $accessControlEntries += [pscustomobject[]]@{ Principal = $principal.trim() ForcePrincipal = Get-ForcePrincipal -StigString $stigString Rights = Convert-RightsConstant -RightsString $fileSystemRights Inheritance = $inheritanceConstant[[string]$inheritance.trim()] } } } } return $accessControlEntries } <# .SYNOPSIS Converts the checkconent from the STIG rule to a hashtable with the following keys: AuditFlags, SystemRights, and Inheritance #> function ConvertTo-FileSystemAuditRule { [CmdletBinding()] [OutputType([pscustomobject])] param ( [Parameter(Mandatory = $true)] [psobject] $CheckContent ) # We are going to set this to pass and if any values are null change it fail $this.ConversionStatus = 'pass' $fileRights = Get-FileSystemAccessValue -CheckContent $CheckContent $principal = ($CheckContent | Select-String -Pattern '(?<=select\sthe\s").*(?="\srow)|(?<=Principal:).*(?=$)').Matches.Value $inheritance = Get-FileSystemInheritance -CheckContent $CheckContent $result = [hashtable]@{ Principal = $principal.trim() Rights = Convert-RightsConstant -RightsString ($fileRights -join ',') Inheritance = $inheritanceConstant[[string]$inheritance.trim()] } if ($null -eq $result.Principal -or $null -eq $result.Rights -or $null -eq $result.Inheritance) { $this.ConversionStatus = 'fail' } return $result } <# .SYNOPSIS Converts strings describing the fileRights permissions to constants that are usable. Additonally this addresses the edge case when the fileRights are seperated by a forward slash "/" #> function Convert-RightsConstant { [CmdletBinding()] [OutputType([array])] param ( [Parameter(Mandatory = $true)] [string] $RightsString ) foreach ( $string in $RightsString ) { $values = @() $rights = $( if ($string.Contains(',') -and $string.Contains('/') -and $this.RawString -match 'Auditing tab') { $string.Split(',') } elseif ($string.Contains('/')) { $string.Split('/') } else { $string.Split(',') } ) foreach ($right in $rights) { switch ($this.dscresource) { 'ActiveDirectoryAuditRuleEntry' { $values += $activeDirectoryRightsConstant[$right.trim()] } 'RegistryAccessEntry' { $values += $registryRightsConstant[$right.trim()] } 'NTFSAccessEntry' { $values += $fileRightsConstant[$right.trim()] } 'FileSystemAuditRuleEntry' { $values += $auditFileSystemRights[$right.trim()] } '(blank)' { $values += $activeDirectoryRightsConstant[$right.trim()] } } } } return $values -join ',' } <# .SYNOPSIS Checks if the permission rule target has multiple paths .PARAMETER PermissionPath Permission rule target path #> function Test-MultiplePermissionRule { [CmdletBinding()] [OutputType([bool])] param ( [Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $PermissionPath ) if ( $PermissionPath -match ';') { return $true } return $false } <# .SYNOPSIS Returns an array of permission rule target paths .PARAMETER PermissionPath Permission rule target path #> function Split-MultiplePermissionRule { [CmdletBinding()] [OutputType([System.Array])] param ( [Parameter(Mandatory = $true)] [AllowEmptyString()] [string[]] $CheckContent ) $result = @() [System.Collections.ArrayList]$contentRanges = @() # Test for multiple paths at HKLMRoot if ($checkContent -match $regularExpression.hklmRootKeys) { $hklmSecurityMatch = $checkContent | Select-String -Pattern $regularExpression.hklmSecurity $hklmSoftwareMatch = $checkContent | Select-String -Pattern $regularExpression.hklmSoftware $hklmSystemMatch = $checkContent | Select-String -Pattern $regularExpression.hklmSystem $lastPermissonMatch = $checkContent | Select-String -Pattern $regularExpression.spaceDashAnythingSpaceDash | Select-Object -Last 1 [void]$contentRanges.Add(($hklmSecurityMatch.LineNumber - 1)..($hklmSoftwareMatch.LineNumber - 2)) [void]$contentRanges.Add(($hklmSoftwareMatch.LineNumber - 1)..($hklmSystemMatch.LineNumber - 2)) [void]$contentRanges.Add(($hklmSystemMatch.LineNumber - 1)..($lastPermissonMatch.LineNumber - 1)) $headerLineRange = 0..($hklmSecurityMatch.LineNumber - 2) $footerLineRange = ($lastPermissonMatch.LineNumber)..($checkContent.Length - 1) } elseif ( $checkContent -match $regularExpression.rootOfC -and $checkContent -match $regularExpression.programFilesWin10 -and $checkContent -match $regularExpression.winDir ) { $rootOfCMatch = $checkContent | Select-String -Pattern $regularExpression.rootOfC | Select-Object -First 1 $programFilesMatch = $checkContent | Select-String -Pattern $regularExpression.programFileFolder $windowsDirectoryMatch = $checkContent | Select-String -Pattern $regularExpression.winDir $icaclsMatch = $checkContent | Select-String -Pattern 'Alternately\suse\sicacls' [void]$contentRanges.Add(($rootOfCMatch.LineNumber - 1)..($programFilesMatch.LineNumber - 2)) [void]$contentRanges.Add(($programFilesMatch.LineNumber - 1)..($windowsDirectoryMatch.LineNumber - 2)) [void]$contentRanges.Add(($windowsDirectoryMatch.LineNumber - 1)..($icaclsMatch.LineNumber - 2)) $headerLineRange = 0..($rootOfCMatch.LineNumber - 2) $footerLineRange = ($icaclsMatch.LineNumber - 1)..($icaclsMatch.LineNumber - 1) } else { $programFileTargets = '^\\Program Files and ','and \\Program Files \(x86\)' foreach ($target in $programFileTargets) { $result += Join-CheckContent -Body ($checkContent -replace $target) } return $result } foreach ($range in $contentRanges) { $result += Join-CheckContent -Header $checkContent[$headerLineRange] -Body $checkContent[$range] -Footer $checkContent[$footerLineRange] } return $result } <# .SYNOPSIS Retrieves the Force Principal attribute #> function Get-ForcePrincipal { [CmdletBinding()] [OutputType([boolean])] param ( [psobject] $stigString ) # Setting default value for the time being. In the future additional logic could be added here in order to dynamically determine what this should be. return $false } <# .SYNOPSIS Converts a string array into a multi-line string object #> function Join-CheckContent { [CmdletBinding()] [OutputType([string])] param ( [Parameter()] [AllowEmptyString()] [string[]] $Header, [Parameter()] [string[]] [AllowEmptyString()] $Body, [Parameter()] [string[]] [AllowEmptyString()] $Footer ) $stringBuilder = [System.Text.StringBuilder]::new() foreach ($line in $Header) { [void]$stringBuilder.AppendLine($line) } foreach ($line in $Body) { [void]$stringBuilder.AppendLine($line) } foreach ($line in $Footer) { [void]$stringBuilder.AppendLine($line) } return $stringBuilder.ToString() } <# .SYNOPSIS Retrieves the file system inheritance setting from the CheckContent #> function Get-FileSystemInheritance { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [AllowEmptyString()] [string[]] $CheckContent ) $results = @() foreach ($line in $CheckContent) { # $inheritanceConstant comes from Rule.Permission\Convert\Data.ps1 foreach ($key in $inheritanceConstant.keys) { $result = $line | Select-String -Pattern $key if ($result) { $results += $result } } } if ($results.count -gt 1) { throw "Multiple results have been found." } return $results.Matches.Value } <# .SYNOPSIS Retrieves the file system access setting from the CheckContent #> function Get-FileSystemAccessValue { [CmdletBinding()] [OutputType([System.String[]])] param ( [Parameter(Mandatory = $true)] [AllowEmptyString()] [string[]] $CheckContent ) $results = @() foreach ($line in $CheckContent) { # $auditFileSystemRights comes from Rule.Permission\Convert\Data.ps1 foreach ($key in $auditFileSystemRights.keys) { $result = $line | Select-String -Pattern $key if ($result) { $results += $result } } } return $results.Matches.Value } #endregion |