How to Find Files with Specific Permissions in Batch Script
Identifying files with specific, often insecure, permissions is a critical task for system administrators and security auditors. You might need to find all files where the "Everyone" group has write access, or identify which executable files a specific low-privilege user can modify.
Searching for these permissions through a standard Windows Explorer interface is nearly impossible at scale. In this guide, we will use Batch scripting and the icacls command to create powerful search tools that filter files based on their Access Control Lists (ACLs).
Leveraging ICACLS for Searching
The icacls command is primarily a tool for setting and viewing permissions, but when combined with Batch's string processing capabilities, it becomes an effective search engine.
The Strategy
We will use icacls to list permissions recursively and then use the findstr command to filter for specific permission strings (like (F) for Full Control or (W) for Write).
Finding Files with "Full Control" for a Specific User
If you suspect a user has been granted too much power over a sensitive directory, you can run a script to find every instance where they have Full Control.
@echo off
setlocal
set "searchDir=D:\Projects"
set "targetUser=jdoe"
echo Searching for files where '%targetUser%' has Full Control in %searchDir%...
echo ----------------------------------------------------------------------
:: icacls output format: filepath DOMAIN\user:(OI)(CI)(F)
:: We search for the username followed by permissions containing (F)
:: The /C flag tells icacls to continue on errors
:: The /T flag processes files in subdirectories
icacls "%searchDir%" /T /C | findstr /i "%targetUser%.*(F)"
echo.
echo Search complete.
pause
Understanding the Pattern:
In icacls output, permissions appear like DOMAIN\user:(OI)(CI)(F). The findstr pattern %targetUser%.*(F) matches lines containing the username followed eventually by (F) (Full Control). We removed the /Q switch because it suppresses output, the opposite of what we want when searching. We also removed \* from the path since icacls with /T already recurses through all contents. The /R flag was removed because findstr regex does not support * the same way as standard regex, using /i with a literal search is more reliable here.
Finding Insecure "Everyone" Permissions
One of the big security risks in any organization is the "Everyone" group having Write or Modify access to a folder. This script helps you audit your file shares for this vulnerability.
@echo off
setlocal
set "sharePath=F:\CompanyShared"
set "logFile=Insecure_Shares_Found.txt"
echo Auditing shares for 'Everyone' Write/Modify access...
:: icacls output shows permissions like: Everyone:(OI)(CI)(M) or Everyone:(W) or Everyone:(F)
:: We search for lines containing "Everyone" AND any of (W), (M), or (F)
:: Using multiple /C: searches with /i for case-insensitive matching
(
icacls "%sharePath%" /T /C | findstr /i "Everyone" | findstr /i /C:"(W)" /C:"(M)" /C:"(F)"
) > "%logFile%"
:: Check if the log file has content (not empty)
for %%A in ("%logFile%") do set "fileSize=%%~zA"
if defined fileSize if %fileSize% gtr 0 (
echo.
echo WARNING: Insecure permissions detected!
echo See %logFile% for the list of files.
) else (
echo.
echo No insecure 'Everyone' permissions were found.
del "%logFile%" 2>nul
)
pause
Finding Files with "Deny" Rules
Explicit "Deny" rules are often used to patch security holes but can also be the cause of mysterious "Access Denied" errors. This script finds every file that has an explicit Deny setting.
@echo off
setlocal
set "target=C:\Data"
echo Finding explicit DENY permissions in %target%...
:: In icacls output, deny entries are marked with '(DENY)'
:: /T recurses into subdirectories, /C continues on errors
icacls "%target%" /T /C | findstr /i /C:"(DENY)"
pause
Searching by permissions involves reading the security descriptor of every single file. On large file servers with millions of files, these scripts can take significant time and consume CPU. It is best to run these during off-peak hours.
Advanced Filtering: Using the ICACLS /FINDSID Switch
Modern versions of icacls actually have a built-in search feature designed specifically for this task: the /findsid switch. This is much faster than the manual findstr method because the search is performed directly by the icacls binary.
Example: Find all files with ACL entries referencing a specific user or group
If you know the Security Identifier (SID) or simply the username, you can use:
:: This finds every file in the tree that mentions the target user/group
:: /findsid lists files where the named SID appears in the ACL
:: /T recurses subdirectories, /C continues past errors
icacls "C:\Docs" /findsid "DOMAIN\GroupName" /T /C
Note that /findsid only outputs file paths: it does not show the actual permission entries. To find only instances where they have a specific level of control (e.g., Modify), retrieve the full ACLs for the matched files:
@echo off
setlocal
set "searchPath=C:\Docs"
set "targetSID=DOMAIN\GroupName"
:: First pass: find files where the target appears in the ACL
:: Second pass: get full ACLs and filter for (M) Modify permission
for /f "tokens=*" %%F in ('icacls "%searchPath%" /findsid "%targetSID%" /T /C 2^>nul') do (
icacls "%%F" 2>nul | findstr /i "%targetSID%" | findstr /i /C:"(M)"
)
Best Practices
- Elevation is Required: Always right-click your batch file and select Run as Administrator. If the script doesn't have permission to read the ACL of a file, it will skip it, leading to a "false negative" in your audit.
- Log Your Results: When searching across many directories, always redirect the output to a text file (
>> results.txt) so you can analyze the data later without the console buffer cutting off the history. - Understand
icaclsOutput Format: Permission entries include inheritance flags in separate parentheses, for example,DOMAIN\user:(OI)(CI)(RX). When writingfindstrpatterns, account for these extra tokens rather than assuming permissions immediately follow the username. - Avoid
/QWhen Searching: The/Q(quiet) switch suppresses file names in some operations. When your goal is to find and list files, omit/Qto ensure full output. - Don't Append
\*with/T: When usingicacls path /T, the/Tflag already recurses into all contents. Adding\*to the path can cause unexpected behavior or duplicate processing.
Summary of Fixes Applied
| Issue | Original | Fixed |
|---|---|---|
| Suppressed output | /Q flag hid results | Removed /Q from search scripts |
| Redundant wildcard | "%path%\*" /T | "%path%" /T because /T handles recursion |
| Broken regex | findstr /R "user:[^ ]*F" | findstr /i "user.*(F)" accounts for inheritance flags |
| Unreliable error check | ERRORLEVEL after pipe + redirect | File size check on output log |
Misused /findsid | Piped path-only output to findstr /C:"(M)" | Two-pass approach: find files, then query their ACLs |
Conclusion
By combining the recursive power of icacls with the filtering logic of findstr, you can transform your Batch scripts into highly effective security tools. Whether you are finding insecure "Everyone" shares or hunting down specific "Full Control" assignments, these automated search techniques save hours of manual clicking and provide a reliable way to secure your infrastructure.undefined