Public/Get-SharePermissionReport.ps1
|
function Get-SharePermissionReport { <# .SYNOPSIS Audits SMB share-level permissions for compliance issues. .DESCRIPTION Examines the share-level permissions (not NTFS ACLs) on SMB shares using Get-SmbShare and Get-SmbShareAccess. Share permissions are a separate security layer that is often overlooked. Common findings include Everyone with Full Control, shares with no explicit deny rules, and overly broad access. Note: Share permissions and NTFS permissions are evaluated together by Windows. The most restrictive of the two applies. However, auditors expect both layers to follow least-privilege. .PARAMETER ComputerName One or more computer names to audit. Defaults to localhost. .PARAMETER ExcludeDefault When specified, excludes default administrative shares (ADMIN$, C$, D$, IPC$, etc.). .EXAMPLE Get-SharePermissionReport .EXAMPLE Get-SharePermissionReport -ComputerName 'FileServer01', 'FileServer02' -ExcludeDefault #> [CmdletBinding()] param( [Parameter(Position = 0)] [ValidateNotNullOrEmpty()] [string[]]$ComputerName = @('localhost'), [Parameter()] [switch]$ExcludeDefault ) begin { # Patterns for default administrative shares $defaultSharePatterns = @( '*$' # All admin/hidden shares end with $ ) function Test-DefaultShare { param([string]$ShareName) foreach ($pattern in $defaultSharePatterns) { if ($ShareName -like $pattern) { return $true } } return $false } $results = [System.Collections.Generic.List[PSObject]]::new() } process { foreach ($computer in $ComputerName) { Write-Verbose "Scanning shares on: $computer" # Get all SMB shares $smbParams = @{ ErrorAction = 'Stop' } if ($computer -ne 'localhost' -and $computer -ne $env:COMPUTERNAME -and $computer -ne '.') { $smbParams['CimSession'] = New-CimSession -ComputerName $computer -ErrorAction Stop } try { $shares = Get-SmbShare @smbParams } catch { Write-Warning "Cannot enumerate shares on $computer : $_" continue } foreach ($share in $shares) { # Skip default shares if requested if ($ExcludeDefault -and (Test-DefaultShare -ShareName $share.Name)) { Write-Verbose "Skipping default share: $($share.Name)" continue } # Get share-level access rules try { $accessRules = Get-SmbShareAccess -Name $share.Name @smbParams } catch { Write-Warning "Cannot read access on share $($share.Name): $_" continue } $hasExplicitDeny = $false foreach ($rule in $accessRules) { if ($rule.AccessControlType -eq 'Deny') { $hasExplicitDeny = $true } } foreach ($rule in $accessRules) { # Determine findings $findings = [System.Collections.Generic.List[string]]::new() # Everyone with Full Control if ($rule.AccountName -match '(^Everyone$|\\Everyone$)' -and $rule.AccessRight -eq 'Full') { $findings.Add('EVERYONE FULL CONTROL') } # Everyone with any access if ($rule.AccountName -match '(^Everyone$|\\Everyone$)' -and $rule.AccessRight -ne 'Full') { $findings.Add('EVERYONE ACCESS') } # Full Control for non-admin accounts if ($rule.AccessRight -eq 'Full' -and $rule.AccountName -notmatch '(^Everyone$|\\Everyone$)' -and $rule.AccountName -notmatch '(BUILTIN\\Administrators|NT AUTHORITY\\SYSTEM)') { $findings.Add('FULL CONTROL GRANTED') } # No explicit deny on the share if (-not $hasExplicitDeny -and $rule.AccessControlType -eq 'Allow') { $findings.Add('NO EXPLICIT DENY ON SHARE') } $finding = if ($findings.Count -gt 0) { $findings -join '; ' } else { 'OK' } $result = [PSCustomObject]@{ ComputerName = $computer ShareName = $share.Name SharePath = $share.Path AccountName = $rule.AccountName AccessRight = $rule.AccessRight AccessControlType = $rule.AccessControlType Finding = $finding } $results.Add($result) } } # Clean up CIM session if we created one if ($smbParams.ContainsKey('CimSession')) { Remove-CimSession -CimSession $smbParams['CimSession'] -ErrorAction SilentlyContinue } } } end { $results } } |