Looking for unprotected directories (and files)

When checking if a system is secure, you often want to validate if there are no locations that are writeable for standard users (non-admins); for example, on a website, you want to make sure that all application files and directories are read-only (unless you have a specific directory in which you would allow users to upload files). Even on content management systems where users can store their own files (such as SharePoint), user-documents are stored in the database and never on the file system, so no need there to grant users any kind of write access on the server.

What can go wrong if users do have write access to application files?

Well, by default, they won't be able to upload anything, so it might seem not too harmful. But what if for some reasons you open up a file-share (e.g. to allow server-administrators to upload new files)? Or somebody enables WebDav authoring access on a website? Enforcing least privilege access is often a defense-in-depth measure that will limit the damage if somebody makes a configuration mistake later on.

So, how can we check the access rights on the file-system and identify those directories (and files) where users have write-access on?

One tool that might help you is AccessEnum from SysInternals: "This simple yet powerful security tool shows you who has what access to directories, files and Registry keys on your systems. Use it to find holes in your permissions."

If you're just interested in finding to which directories and files, standard users have write-access, you can use the script below. It will enumerate all directories (if you want to include files, remove the where-clause ?{$_.PSIScontainer -eq $true}) and for each one, check if standard users (identified as "Builtin\Users", "Domain Users", "NT AUTHORITY\Authenticated Users" and "everyone" in the code-sample) have some kind of write-access to the directory. Write-access also includes the right to create or delete a file in a directory, change a file, take ownership, modify permissions, etc.

The script can probably be further optimized for performance (scanning all directories in c:\windows takes less than 2 minutes on my W7 machine), but it does the job.

What does the script do? In short:

  • Check ACL of the given root-directory, including inherited ACEs
  • Get a listing of all subdirectories
  • Check the ACL for each of the subdirectories, excluding inherited ACEs (since these were checked at the root)

(Check also my first post on AppLocker: this script might help you identifying writeable directories under C:\Windows and c:\program files, so you can add them as exceptions)

Function CheckACLStrength([string]$FromDir) {

 #enumerate all groups you want to check (add your own groups) $Grps= @("BUILTIN`\Users","DomainUsers","NTAUTHORITY`\AuthenticatedUsers","Everyone")

 $ListViolations = @() #Check the ACL of the root-directory, including inherited ACEs $acl=(Get-Acl $FromDir) if(CheckACLViol $acl $Grps $True) {$ListViolations += $FromDir} #enumerate all subdirectories and check ACLs (excluding inherited ACEs) $allDirs = Get-ChildItem –path $FromDir -recurse | ?{$_.PSIScontainer –eq $true} foreach($DirOrFile in ($allDirs)){ $acl=(Get-Acl $DirOrFile.FullName) if(CheckACLViol $acl $Grps $false){ $ListViolations += $DirOrFile.FullName } }

 Return $ListViolations}

Function CheckACLViol($acl, [String[]] $Groups, [boolean] $InclInherited){

 #create an array of all unwanted access types (valid for both files & directories) $InvalidAccessTypes=([system.Security.AccessControl.FileSystemRights]::AppendData,` [system.Security.AccessControl.FileSystemRights]::ChangePermissions,` [system.Security.AccessControl.FileSystemRights]::CreateDirectories,` [system.Security.AccessControl.FileSystemRights]::CreateFiles,` [system.Security.AccessControl.FileSystemRights]::Delete,` [system.Security.AccessControl.FileSystemRights]::DeleteSubdirectoriesAndFiles,` [system.Security.AccessControl.FileSystemRights]::FullControl,` [system.Security.AccessControl.FileSystemRights]::Modify,` [system.Security.AccessControl.FileSystemRights]::TakeOwnership,` [system.Security.AccessControl.FileSystemRights]::WriteData)

 # check if any of the ACE matches any of the unwanted access types for the list of groups $count=0 $AceAllow=[System.Security.AccessControl.AccessControlType]::Allow $AceDeny=[System.Security.AccessControl.AccessControlType]::Deny foreach($ace in $acl.access){ if ($ace.AccessControlType –eq $AceAllow –and (($InclInherited –and $ace.IsInherited) –or –not $ace.IsInherited)){ $tmpmtch=$Groups –contains $ace.IdentityReference.toString() if($tmpmtch){ $isok=$true $InvalidAccessTypes | foreach{ $isok = $isok –and –not ($ace.FileSystemRights –match $_)} If (-not $isok) {$count++} } } }

 if($count-gt0) {1} else {0}}

CheckACLStrength "c:\Windows"