Updated 7/2/2016 – yeah, I haven’t needed to parse the shitty output from this tool in 7 years. I never accounted for “special access” permissions not including the account with the access.
Updated 12/28/2018 – link to the code didn’t work so I’ve fixed that. Also updated if you receive a specific error message.
CACLS.exe is a great builtin Windows utility that allows you to list the permissions on a file or folder. This command has been used in an audit to get the permissions of the folders on an agency file server that served the “private” shares to each Domain user. The findings we would be looking for when examining the results are improper access to the “private” shares by other Domain users.
For CACLS options and how to interpret the results see this site.
The commands that I run are as follows:
Directories and Files in the folder your run CACLS
c:\>for /f "delims=" %a in ('dir /b') do @cacls "%a" >> savefile.txt
All directories, recursive, from the folder your run CACLS
c:\>for /f "delims=" %a in ('dir /b /S /A:D') do @cacls "%a" >> savefile.txt
Once results are obtained they need to be parsed so they can be analyzed. I have written a perl script to add the correct folder name to each permission. This is so they can be sorted by permission in your spreadsheet application of choice.
Save the code and run it as follows (also download here):
#!/usr/bin/perl
$numArgs = $#ARGV +1;
if($numArgs < 2){
print "Invalid Number of Arguments\n";
print "caclsparse.pl <filename> <foldername>\n";
print "The foldername is the root folder you ran CACLS.exe from.\n\n";
print "foldername example: \"C\\:\\\\Documents and Settings\\\\jedge\"\n";
print "Folder names with spaces need to be encapsulated in quotes.\n";
print "You need to escape the backslash twice.\n";
print "You need to escape the colon with a backslash as well.\n";
exit;
}
#open the file
$infile = "$ARGV[0]";
open(DAT, $infile) || die("Something did not work. You can email me at james.edge(at)jedge.com\n");
#save file contents into an array
@raw_data=<DAT>;
close(DAT);
open (OUTPUT, '>cacls_parse_output.csv');
#Cycle through the entire array
for($count=0;$count<=$#raw_data;$count++){
#pull folder name, split it, and print it
#the first record in each grouping is the only record with the folder name
if(@raw_data[$count] =~ /($ARGV[1])/){
chomp(@raw_data[$count]);
$x = 0;
while(substr(@raw_data[$count+1],$x,1) eq " "){
$x++;
}
$folder = substr(@raw_data[$count],0,$x-1);
$permissions = substr(@raw_data[$count],$x,length(@raw_data[$count]) - $x);
print OUTPUT "\"$folder\",\"$permissions\"\n";
#cycle through the permissions listed below the folder name
for($c=$count+1;$c<=$#raw_data;$c++){
$permissions = substr(@raw_data[$c],$x,length(@raw_data[$c]) - $x);
chomp($permissions);chomp($permissions);
#print until you get to the next folder item
if(@raw_data[$c] =~ /($ARGV[1])/){last;}
#another problem is "special access" permissions
#cycle through including the folder AND the account with access
if($permissions =~ /special access/){
$c++;
while(@raw_data[$c] =~ /. /){
$special_permissions = substr(@raw_data[$c],$x,length(@raw_data[$c]) - $x);
$special_permissions =~ s/\s+//g;
print OUTPUT "\"$folder\",\"$permissions $special_permissions\"\n";
$c++;
}
} else { print OUTPUT "\"$folder\",\"$permissions\"\n";} #not "special access" so just print to file
}
}
}
close(OUTPUT);
$perl caclsparse.pl savefile.txt "C\\:\\Documents and Settings\\jedge"
NOTE: I run it from Linux but ActivePerl for Windows will work as well. Installing perl is outside the scope of this posting.
Open parseresults.csv in Excel/OO Calcs/Gnumeric and begin analyzing the results!
I’ve noticed that if you use double-quotes you will get the following error (cacls run on Windows 10 and output parsed on Kali 2017-04-04).
Unmatched ( in regex; marked by <-- HERE in m/( <-- HERE C:\Users\us315622\)/ at caclsparse.pl line 28.
You need to use single-quotes.$perl caclsparse.pl savefile.txt 'C\\:\\Users\\edge'
Nice work. I have a little issue with the parsed results. The very first folder encountered is parse correctly, but when it gets to the next directory, it places the prior directory name in Column A, the new directory in Column B and the file permission in Column C. From there it is correct, withe current Directory in the A column and permissions in B column.
Is there a way to test the first argument on each pass? I am not any good at scripting or I would try to figure it out.
Thanks,
Susan