You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

93 lines
6.2 KiB

[CmdletBinding()]
param (
[Parameter(Mandatory)] [string] $ModulePath,
[Parameter(Mandatory)] [string] $OutFile,
[Parameter(Mandatory)] [string] $Variant,
[string] $OtherServer = ''
)
# Runs inside a machine of the operating-system matrix, in Windows PowerShell 5.1 under the token that Probe-EffectiveAccess.ps1 chose for
# the variant, and asks Get-NTFSEffectiveAccess the same question in several ways: for the account of the token, Everyone, the local
# Administrator, and the domain Administrator and Domain Users on a computer in a domain, each for the default server name, localhost, an
# empty name, the names of this computer, and the computers of -OtherServer (a comma-separated list). For every call it writes the result,
# the number of warnings, and the native error with the failing method, so that the failing call of the authorization manager shows. It
# changes nothing but a folder below $env:TEMP.
$ErrorActionPreference = 'Continue'
$ProgressPreference = 'SilentlyContinue'
$stamp = '[{0:HH:mm:ss}]'
function Write-Probe { param ([string] $Message) ($stamp -f [DateTime]::UtcNow) + ' ' + $Message | Add-Content -LiteralPath $OutFile }
$null = New-Item -ItemType Directory -Path (Split-Path -Path $OutFile -Parent) -Force
Set-Content -LiteralPath $OutFile -Value ''
try {
Import-Module -Name (Join-Path -Path $ModulePath -ChildPath 'NTFSSecurity.psd1') -Force -ErrorAction Stop
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object -TypeName 'Security.Principal.WindowsPrincipal' -ArgumentList $identity
$current = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
Write-Probe ('START variant={0} user={1} sid={2} administrator={3} os={4} {5}.{6} dll={7}' -f $Variant, $identity.Name, $identity.User.Value,
$principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator), $current.ProductName, $current.CurrentBuildNumber, $current.UBR,
(Get-FileHash -Algorithm SHA256 -LiteralPath (Join-Path -Path $ModulePath -ChildPath 'NTFSSecurity.dll')).Hash.Substring(0, 12))
$groups = @(& whoami.exe /groups /fo csv | ConvertFrom-Csv)
Write-Probe ('groups={0}; deny only: {1}' -f $groups.Count, ((@($groups | Where-Object -FilterScript { $_.Attributes -match 'deny' } | ForEach-Object -Process { $_.'Group Name' })) -join ', '))
Write-Probe ('integrity: {0}' -f ((@($groups | Where-Object -FilterScript { $_.'Group Name' -like 'Mandatory Label*' } | ForEach-Object -Process { $_.'Group Name' })) -join ', '))
$privileges = @(& whoami.exe /priv /fo csv | ConvertFrom-Csv)
Write-Probe ('privileges present={0} enabled: {1}' -f $privileges.Count, ((@($privileges | Where-Object -FilterScript { $_.State -eq 'Enabled' } | ForEach-Object -Process { $_.'Privilege Name' })) -join ', '))
$folder = Join-Path -Path $env:TEMP -ChildPath ('probe-{0}' -f [guid]::NewGuid().ToString('N'))
$null = New-Item -ItemType Directory -Path $folder
$fqdn = try { [Net.Dns]::GetHostEntry($env:COMPUTERNAME).HostName } catch { $env:COMPUTERNAME }
function Resolve-Sid {
param ([string] $Name)
try { (New-Object -TypeName 'Security.Principal.NTAccount' -ArgumentList $Name).Translate([Security.Principal.SecurityIdentifier]).Value } catch { '' }
}
$computer = Get-CimInstance -ClassName Win32_ComputerSystem
Write-Probe ('computer {0} domain joined={1} domain={2}' -f $env:COMPUTERNAME, $computer.PartOfDomain, $computer.Domain)
$accounts = [ordered]@{ 'self' = ''; 'Everyone' = 'S-1-1-0'; 'local Administrator' = (Resolve-Sid -Name ('{0}\Administrator' -f $env:COMPUTERNAME)) }
if ($computer.PartOfDomain) {
$accounts['domain Administrator'] = Resolve-Sid -Name ('{0}\Administrator' -f $computer.Domain)
$accounts['Domain Users'] = Resolve-Sid -Name ('{0}\Domain Users' -f $computer.Domain)
}
$servers = [ordered]@{ 'no -ServerName' = $null; 'localhost' = 'localhost'; 'empty name' = ''; 'computer name' = $env:COMPUTERNAME; 'fqdn' = $fqdn }
foreach ($name in @($OtherServer -split ',' | Where-Object -FilterScript { $_ })) { $servers["other computer $name"] = $name }
$cases = foreach ($accountName in $accounts.Keys) {
if ($accountName -ne 'self' -and -not $accounts[$accountName]) { continue }
foreach ($serverName in $servers.Keys) {
$arguments = @{}
if ($accounts[$accountName]) { $arguments.Account = $accounts[$accountName] }
if ($null -ne $servers[$serverName]) { $arguments.ServerName = $servers[$serverName] }
@{ Name = ('{0}, {1}' -f $accountName, $serverName); Arguments = $arguments }
}
}
foreach ($case in $cases) {
$arguments = $case.Arguments
$errorList = $null
$warningList = $null
try {
$result = @(Get-NTFSEffectiveAccess -Path $folder @arguments -ErrorVariable errorList -WarningVariable warningList -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
}
catch {
$result = @()
$errorList = @($_)
}
$access = if ($result.Count -gt 0) { ('{0}' -f $result[0].AccessRights) } else { 'none' }
$warnings = @($warningList | ForEach-Object -Process { ('{0}' -f $_.Message) -replace '\s+', ' ' } | ForEach-Object -Process { if ($_.Length -gt 60) { $_.Substring(0, 60) } else { $_ } })
Write-Probe ('CASE {0}: results={1} access={2} errors={3} warnings={4}' -f $case.Name, $result.Count, $access, @($errorList).Count, $warnings.Count)
foreach ($record in @($errorList)) {
$inner = $record.Exception
while ($inner.InnerException) { $inner = $inner.InnerException }
$native = if ($inner -is [ComponentModel.Win32Exception]) { $inner.NativeErrorCode } else { '' }
$frames = (('{0}' -f $inner.StackTrace) -split "`r?`n" | Select-Object -First 1 | ForEach-Object -Process { $_.Trim() -replace '^at ', '' -replace '\(.*$', '' }) -join ' <- '
Write-Probe (' ERROR id={0} type={1} native={2} message={3} frames={4}' -f $record.FullyQualifiedErrorId, $inner.GetType().Name, $native, $inner.Message, $frames)
}
}
Remove-Item -LiteralPath $folder -Recurse -Force -ErrorAction SilentlyContinue
Write-Probe 'DONE'
}
catch {
Write-Probe ('FAILED: {0}' -f $_)
}