Skip to content

Instantly share code, notes, and snippets.

@hinchley
Created September 12, 2016 09:35
Show Gist options
  • Save hinchley/ade9528e5ce986e9a8131489ad852789 to your computer and use it in GitHub Desktop.
Save hinchley/ade9528e5ce986e9a8131489ad852789 to your computer and use it in GitHub Desktop.
A PowerShell script for identifying user-writable folders. Usage is discussed in the following article: http://hinchley.net/2016/06/13/an-approach-for-managing-microsoft-applocker-policies/
# Paths that we've already excluded via AppLocker.
$exclusions = @()
# Paths to process.
$paths = @(
"C:\Windows"
)
# Setup log.
$log = "$PSScriptRoot\UserWritableLocations.log"
$FSR = [System.Security.AccessControl.FileSystemRights]
# Unfortunately the FileSystemRights enum doesn't contain all the values from the Win32 API. Urgh.
$GenericRights = @{
GENERIC_READ = [int]0x80000000;
GENERIC_WRITE = [int]0x40000000;
GENERIC_EXECUTE = [int]0x20000000;
GENERIC_ALL = [int]0x10000000;
FILTER_GENERIC = [int]0x0FFFFFFF;
}
# ... so we need to map them ourselves.
$MappedGenericRights = @{
FILE_GENERIC_READ = $FSR::ReadAttributes -bor $FSR::ReadData -bor $FSR::ReadExtendedAttributes -bor $FSR::ReadPermissions -bor $FSR::Synchronize
FILE_GENERIC_WRITE = $FSR::AppendData -bor $FSR::WriteAttributes -bor $FSR::WriteData -bor $FSR::WriteExtendedAttributes -bor $FSR::ReadPermissions -bor $FSR::Synchronize
FILE_GENERIC_EXECUTE = $FSR::ExecuteFile -bor $FSR::ReadPermissions -bor $FSR::ReadAttributes -bor $FSR::Synchronize
FILE_GENERIC_ALL = $FSR::FullControl
}
Function Map-GenericRightsToFileSystemRights([System.Security.AccessControl.FileSystemRights]$Rights) {
$MappedRights = New-Object -TypeName $FSR
if ($Rights -band $GenericRights.GENERIC_EXECUTE) {
$MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_EXECUTE
}
if ($Rights -band $GenericRights.GENERIC_READ) {
$MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_READ
}
if ($Rights -band $GenericRights.GENERIC_WRITE) {
$MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_WRITE
}
if ($Rights -band $GenericRights.GENERIC_ALL) {
$MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_ALL
}
return (($Rights -band $GenericRights.FILTER_GENERIC) -bor $MappedRights) -as $FSR
}
# These are the rights from the FileSystemRights enum we care about.
$WriteRights = @('WriteData', 'CreateFiles', 'CreateDirectories', 'WriteExtendedAttributes', 'WriteAttributes', 'Write', 'Modify', 'FullControl')
# Helper function to match against a list of patterns.
function notlike($string, $patterns) {
foreach ($pattern in $patterns) { if ($string -like $pattern) { return $false } }
return $true
}
# The hard work...
function scan($path, $log) {
$cache = @()
gci $path -recurse -exclude $exclusions -force -ea silentlycontinue |
? {($_.psiscontainer) -and (notlike $_.fullname $exclusions)} | %{
trap { continue }
$directory = $_.fullname
(get-acl $directory -ea silentlycontinue).access |
? {$_.isinherited -eq $false} |
? {$_.identityreference -match ".*USERS|EVERYONE"} | %{
(map-genericrightstofilesystemrights $_.filesystemrights).tostring().split(",") | %{
if ($writerights -contains $_.trim()) {
if ($cache -notcontains $directory) { $cache += $directory }
}
}
}
}
return $cache
}
# Start scanning.
$paths | %{ scan $_ $log } | out-file $log
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment