Creating SMB shares on a file server is one thing, but unfortunately, every Windows client or server has the ability to create local shares. These shares can appear anywhere across your domain, often created by users or applications without IT's knowledge, potentially exposing sensitive data to unauthorized access.
If you need to audit these shares and confirm there are no security concerns, then you'll need PowerShell to do a comprehensive assessment of your domain. Manual checking simply isn't feasible when you have hundreds or thousands of computers.
Understanding Share Permissions vs ACL
When it comes to shares, there are two permission layers:
- Share permissions - Applied at the share level
- Security permissions (ACL) - Applied at the NTFS folder level
Share permissions apply before security permissions, meaning if you set "Read" on the share permission and "Write" on the ACL, users will only be able to obtain read access because the share has restricted it.
This is why it's recommended to set your share permissions to "Everyone Full Control" or, if you wish to be a little bit more secure, "Authenticated Users Full Control". The difference is that "Everyone" includes unauthenticated users, while "Authenticated Users" only includes people authenticated by your domain controller, making it a little bit more secure against unauthorized and unmanaged devices.
The Security Risk
What is particularly concerning is when on the ACL of a share (or anywhere in Windows), someone blindly applies "Everyone Full Control". This causes several problems because it means any user - authenticated or not - can technically delete, modify, or access files from that folder without proper authorization, especially if that folder is shared via an unsecured share.
The Solution: PowerShell Audit Scripts
I've developed two PowerShell scripts to address this security concern. The first targets individual servers for testing, and the second scans your entire domain.
Script 1: Individual Server Audit
The first script targets a server individually with the -ServerName parameter. This is to make sure the script works and does what it should do - giving you a report on all the shares listed on that server, and then another report where it detects "Everyone" having Full Control on the ACL.
Key Components Explained
Administrative Share Exclusions
I've built in automatic exclusion of administrative shares that are part of Windows by default:
# Define exclusions for administrative shares
$exclusions = @('C$', 'D$', 'E$', 'ADMIN$', 'IPC$', 'print$', 'NETLOGON', 'SYSVOL')
# Add any custom exclusions here
$customExclusions = @()
$exclusions += $customExclusions
This ensures I'm not reporting on built-in administrative shares that are meant to be there. The $customExclusions array allows you to add your own shares to skip, such as backup software shares or monitoring tool shares.
Remote Connection Management
The script establishes a CIM session for remote connectivity:
try {
# Test connectivity to the server first
if (-not (Test-Connection -ComputerName $ServerName -Count 1 -Quiet)) {
throw "Cannot connect to server $ServerName"
}
# Get all SMB shares from the remote server
$shares = Get-SmbShare -CimSession $ServerName -ErrorAction Stop
}
catch {
Write-Host "✗ ERROR: $_" -ForegroundColor Red
}
This approach uses CIM sessions which are more firewall-friendly than traditional PowerShell remoting and work over standard Windows management ports.
Permission Analysis Logic
The core permission checking logic examines each ACL entry:
# Get ACL for the share path
$acl = Get-Acl -Path $uncPath -ErrorAction Stop
# Process each access rule
foreach ($accessRule in $acl.Access) {
$identityName = $accessRule.IdentityReference.Value
# Create detailed share data object for reporting
$shareData = [PSCustomObject]@{
ServerName = $ServerName
ShareName = $share.Name
SharePath = $share.Path
UNCPath = $uncPath
Description = $share.Description
Identity = $identityName
Rights = $accessRule.FileSystemRights
AccessControlType = $accessRule.AccessControlType
IsInherited = $accessRule.IsInherited
InheritanceFlags = $accessRule.InheritanceFlags
PropagationFlags = $accessRule.PropagationFlags
}
}
This captures comprehensive permission details including inheritance flags, which help identify whether permissions were set explicitly or inherited from parent folders.
Dangerous Permission Detection
The script looks for multiple variations of "Everyone" groups and different representations of Full Control:
# Check for Everyone with Full Control
if ($identityName -match "Everyone|BUILTIN\\Users|NT AUTHORITY\\Authenticated Users") {
# Check multiple representations of Full Control
if ($accessRule.FileSystemRights -eq "FullControl" -or
$accessRule.FileSystemRights -eq "-1" -or
$accessRule.FileSystemRights -eq "268435456" -or
$accessRule.FileSystemRights -match "FullControl") {
Write-Host "⚠ WARNING: Full Control detected!" -ForegroundColor Red
# Create detailed risk entry
$everyoneData = [PSCustomObject]@{
ServerName = $ServerName
ShareName = $share.Name
SharePath = $share.Path
UNCPath = $uncPath
Identity = $identityName
Rights = $accessRule.FileSystemRights
RiskLevel = "HIGH"
DiscoveredTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
}
}
The multiple checks for Full Control are necessary because Windows can represent these permissions in different numeric formats depending on how they were set.
Sample Output - Normal Server
Here's what you'd see when scanning a normal server with five shares:
Console Output:
========================================
SMB Share Permission Audit
Target Server: FILESERVER01
Started: 2024-01-15 10:23:45
========================================
Testing connection to FILESERVER01...
Retrieving SMB shares from FILESERVER01...
Found 8 total shares
Skipping administrative share: ADMIN$
Skipping administrative share: C$
Skipping administrative share: IPC$
Processing share: Finance
Path: E:\Shares\Finance
Total ACL entries: 4
Unique identities: 3
Processing share: HR_Documents
Path: E:\Shares\HR
Total ACL entries: 3
Unique identities: 2
Processing share: Public
Path: E:\Shares\Public
Total ACL entries: 5
Unique identities: 4
Processing share: Marketing
Path: E:\Shares\Marketing
Total ACL entries: 6
Unique identities: 4
Processing share: IT_Tools
Path: E:\Shares\IT
Total ACL entries: 2
Unique identities: 2
========================================
Generating Reports...
========================================
✓ All shares report saved to: AllShares_FILESERVER01_20240115_102345.csv
Total permission entries: 20
✓ No shares found with Everyone Full Control (Good!)
========================================
Audit Summary
========================================
Server: FILESERVER01
Total shares scanned: 8
Non-admin shares: 5
Risky shares found: 0
Completed: 2024-01-15 10:24:12
Sample Output - Server with Security Risk
Now, here's what happens when one of those shares has "Everyone Full Control" on the ACL:
Console Output:
Processing share: Public
Path: E:\Shares\Public
Found: Everyone
Rights: FullControl
⚠ WARNING: Full Control detected!
Found: BUILTIN\Users
Rights: FullControl
⚠ WARNING: Full Control detected!
Total ACL entries: 5
Unique identities: 4
========================================
Generating Reports...
========================================
✓ All shares report saved to: AllShares_FILESERVER01_20240115_102345.csv
Total permission entries: 22
⚠ SECURITY ALERT: Found 2 shares with Everyone Full Control!
Report saved to: EveryoneFullControl_FILESERVER01_20240115_102345.csv
Affected shares:
- Public (Everyone)
- Public (BUILTIN\Users)
If you don't get the additional "EveryoneFullControl" exception file, you have no shares that assign Everyone Full Control - which is the desired state.
Script 2: Domain-Wide Audit
The second script connects to your domain and looks up all computers, which you can filter with "Workstations" or "Servers", and will try to query the shares remotely. I've built a timeout parameter into this to avoid excessively long-running scripts.
Advanced Features Explained
Active Directory Computer Discovery
The script uses LDAP filters to efficiently query Active Directory:
if ($ComputerType -eq "Servers") {
# Get all servers (Windows Server OS)
$computers = Get-ADComputer -Filter {
OperatingSystem -like "*Server*" -and
Enabled -eq $true
} -Properties Name, OperatingSystem, DNSHostName |
Select-Object Name, OperatingSystem, DNSHostName
}
else {
# Get all workstations (non-Server OS)
$computers = Get-ADComputer -Filter {
OperatingSystem -notlike "*Server*" -and
Enabled -eq $true
} -Properties Name, OperatingSystem, DNSHostName |
Select-Object Name, OperatingSystem, DNSHostName
}
The filter for Enabled -eq $true ensures I'm not wasting time trying to connect to disabled computer accounts.
Robust Timeout Mechanism
The timeout mechanism using PowerShell jobs prevents the script from hanging:
# Create a job to get SMB shares with timeout
$job = Start-Job -ScriptBlock {
param($compName)
try {
$session = New-CimSession -ComputerName $compName -ErrorAction Stop
$shares = Get-SmbShare -CimSession $session -ErrorAction Stop
Remove-CimSession $session -ErrorAction SilentlyContinue
return $shares
}
catch {
return $null
}
} -ArgumentList $computerName
# Wait for job with timeout
Write-Host "Attempting to retrieve SMB shares (timeout: $TimeoutSeconds seconds)..." -NoNewline
$completed = Wait-Job -Job $job -Timeout $TimeoutSeconds
if ($completed) {
$shares = Receive-Job -Job $job
Remove-Job -Job $job -Force
# Process shares
}
else {
# Timeout occurred - kill the job and move on
Stop-Job -Job $job -ErrorAction SilentlyContinue
Remove-Job -Job $job -Force -ErrorAction SilentlyContinue
Write-Host " Timeout!" -ForegroundColor Red
}
This ensures the script never hangs indefinitely on an unresponsive computer. The job runs in the background and is forcefully terminated if it doesn't complete within the timeout period.
Connectivity Testing with Flexibility
I've included optional ping testing that can be bypassed:
# Test connectivity (unless skipped)
if (-not $SkipPing) {
Write-Host "Testing connectivity..." -NoNewline
if (Test-Connection -ComputerName $computerName -Count 1 -Quiet) {
Write-Host " Ping successful" -ForegroundColor Green
$isReachable = $true
}
else {
Write-Host " Ping failed" -ForegroundColor Yellow
# Try anyway since ping might be blocked
$isReachable = $true
}
}
This is important because many organizations block ICMP (ping) traffic, but SMB might still work.
Nested Timeout for ACL Retrieval
Even ACL retrieval has its own timeout to prevent hanging on slow network shares:
# Create a job for ACL retrieval with timeout
$aclJob = Start-Job -ScriptBlock {
param($path)
try {
return Get-Acl -Path $path -ErrorAction Stop
}
catch {
return $null
}
} -ArgumentList $uncPath
$aclCompleted = Wait-Job -Job $aclJob -Timeout $TimeoutSeconds
if ($aclCompleted) {
$acl = Receive-Job -Job $aclJob
Remove-Job -Job $aclJob -Force
# Process ACL entries
}
Progress Tracking
For large domains, I've added progress indicators:
# Progress indicator
if ($processedComputers % 10 -eq 0) {
$percentComplete = [math]::Round(($processedComputers / $computers.Count) * 100, 1)
Write-Host "`nProgress: $percentComplete% complete ($processedComputers/$($computers.Count))" -ForegroundColor Magenta
}
This gives you feedback every 10 computers so you know the script is still running.
Comprehensive Error Tracking
The script tracks unreachable computers with detailed status:
$unreachableComputers += [PSCustomObject]@{
ComputerName = $computerName
OperatingSystem = $computer.OperatingSystem
Status = "Connection timeout ($TimeoutSeconds seconds)"
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
This helps identify patterns - maybe all Windows 10 machines are failing, or all computers in a specific subnet.
Memory Management
The cleanup section ensures no orphaned jobs remain:
finally {
# Clean up any remaining jobs
Get-Job | Where-Object {$_.State -eq 'Running'} | Stop-Job -ErrorAction SilentlyContinue
Get-Job | Remove-Job -Force -ErrorAction SilentlyContinue
# Clean up CIM sessions
Get-CimSession | Where-Object {$_.ComputerName -eq $ServerName} | Remove-CimSession -ErrorAction SilentlyContinue
}
This prevents memory leaks during long-running scans of large domains.
Important Considerations
If you're going to run the domain-wide script, it must be able to connect to the computer to retrieve the data. This requires:
- SMB service to be running and accessible (TCP port 445)
- Appropriate firewall rules allowing WMI/CIM traffic
- Administrative credentials on target computers
- PowerShell remoting capabilities or WinRM configured
However, if SMB is not available to that server, there's no risk because users won't be able to access the shares to start with - so a failure to connect is actually a good indicator that the shares aren't exposed.
Running the Scripts
For individual server testing:
# Basic usage
.\Audit-SmbSharePermissions.ps1 -ServerName "FILESERVER01"
# If you need to test from a specific domain controller
.\Audit-SmbSharePermissions.ps1 -ServerName "DC01.domain.local"
For domain-wide scanning:
# Scan all servers with default 5-second timeout
.\Audit-SmbSharePermissions-AD.ps1 -ComputerType "Servers"
# Scan workstations with extended timeout
.\Audit-SmbSharePermissions-AD.ps1 -ComputerType "Workstations" -TimeoutSeconds 10
# Skip ping tests for environments that block ICMP
.\Audit-SmbSharePermissions-AD.ps1 -ComputerType "Servers" -SkipPing
# Combine parameters for challenging networks
.\Audit-SmbSharePermissions-AD.ps1 -ComputerType "Servers" -TimeoutSeconds 15 -SkipPing
Understanding the Output Files
The scripts generate timestamped CSV files that can be opened in Excel for analysis:
AllShares_[Type]_[Timestamp].csv contains:
- ComputerName, OperatingSystem
- ShareName, SharePath, UNCPath
- Every single ACL entry with full permission details
- Inheritance and propagation flags
- Scan timestamp
EveryoneFullControl_[Type]_[Timestamp].csv contains:
- Only the dangerous permission entries
- Risk level assessment
- When it was discovered
- Complete context for remediation
UnreachableComputers_[Type]_[Timestamp].csv contains:
- Failed connection attempts
- Reason for failure (timeout, ping failed, etc.)
- Operating system information
- Timestamps for pattern analysis
Conclusion
Regular auditing of SMB shares across your domain is essential for maintaining security. These scripts provide a systematic approach to identifying potentially dangerous permission configurations before they can be exploited. The ability to scan individual servers for testing, then scale up to domain-wide audits, gives you the flexibility to validate your approach before running it across your entire infrastructure.
By identifying shares with "Everyone Full Control" permissions, you can proactively address security risks that might otherwise go unnoticed until it's too late. Remember, the absence of an "EveryoneFullControl" CSV file is a good thing - it means your shares are configured more securely.
The time invested in running these audits regularly will pay dividends in preventing data breaches and maintaining compliance with security policies. I recommend scheduling these scans monthly or quarterly, depending on your environment's rate of change. Consider integrating these scripts into your security monitoring dashboard or SIEM system for continuous oversight of share security across your domain.