Summary: Microsoft Scripting Guy, Ed Wilson talks about using Windows PowerShell to find errant access control lists on files inside a folder.
Microsoft Scripting Guy, Ed Wilson, is here. I have a long flight ahead of me today. The Scripting Wife and I are crossing the Atlantic Ocean to Charlotte, North Carolina in the United States as we return from our excursion to Madrid Spain for TechEd Europe 2013. I love transatlantic flights (actually, I love transpacific flights even more) because they provide me with a decent amount of uninterrupted time to play, write, and think.
I put on my noise canceling headphones, plug them into my Zune HD, fire up some old-fashioned blues, and get to work. I do not pay for the wireless access on the plane, because then I have the same distractions as at home. The really cool thing about Windows PowerShell is that it is completely self-discoverable, and with the extensive Help files and discovery cmdlets, I have all the tools I need to keep me busy for hours (and that is why I pack a spare battery).
There was a comment last week on the Hey, Scripting Guy! Blog that made me think about the task of finding security changes in a file inside a folder. So I created two folders, filled them with files, and then modified the security settings on one of the files. “How hard would that be to detect,” I wondered…
Create two folders
As I have mentioned previously in this blog, often when I am experimenting, playing, and exploring, I have to write a bit of Windows PowerShell script to set up a test environment. This is what I needed to do here. So, first I created two folders. One folder is named Afolder, and the other is named Bfolder. In this script, % is the alias for Foreach-Object and md is the MakeDirectory function:
"Afolder","Bfolder" | %{md $_}
When I run the command, folder information objects return. This is shown here:
PS C:\> "Afolder","Bfolder" | %{md $_}
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 6/17/2013 12:45 PM Afolder
d---- 6/17/2013 12:45 PM Bfolder
Fill the folders with test files
With the folders created, it is a simple matter to fill them with files. I whip up this one-liner:
1..10 | % {New-Item -ItemType file -Value "$_ text" -Name "$_.txt" -Path C:\afolder}
But as they say, a script is never perfect, and I decide that I do not like the having to run the same command twice. Therefore, I delete the folders and the files, and I decide to do it all at once. It takes only a few minutes before I come up with the following quick script to create the two folders and 10 files.
"C:\Afolder","C:\Bfolder" |
% {
md $_
sl $_
1..10 |
% {New-Item -ItemType file -Value "$_ text" -path "$_.txt"}}
This script without aliases looks like this:
"C:\Afolder","C:\Bfolder" |
ForEach-Object {
mkdir $_
Set-Location $_
1..10 |
ForEach-Object {New-Item -ItemType file -Value "$_ text" -path "$_.txt"}}
Look for changed ACLs
I go into the C:\Bfolder folder and change the permissions on the first file. I add authenticated users and users who have full control. This change is shown here:
Use Get-ACL on each folder
The first thing I do is use the Get-Acl cmdlet to retrieve the ACL for each file in the folder. To do this, I use a wildcard character when I supply the path to the Get-Acl cmdlet. The result is a collection of FileSecurity objects. The two commands I use (one for each folder) are shown here. To make processing later easier, I store the resultant objects in variables.
$aacl = Get-Acl C:\Afolder\*
$bacl = get-acl C:\Bfolder\*
Compare the ACLs from each folder
The ideal tool to use when comparing the ACLs from each folder is the Compare-Object cmdlet. This cmdlet excels at comparing a reference object with a difference object. Don’t get too caught up as to which one is reference and which one is difference. As shown here, the important thing is the direction that the arrows point:
So, the comparison works. It tells us that there is something different in the Bfolder and that it is a change from what is in the Afolder. Note that I choose to compare on the Access property. Any property from a FileSecurity object is usable here. Property members of a FileSecurity object are shown in the image that follows.
Making sense of the comparison
The problem with the output produced earlier is that although it does tell me that something has changed, the output does not tell me which file has changed or what the actual changes are. To do this, I need to allow the original object to pass through. I use the PassThruparameter to do this. Following are the command and its output:
PS C:\> Compare-Object -ReferenceObject $aacl -DifferenceObject $bacl -Property access -PassThru
Directory: C:\Bfolder
Path Owner Access
---- ----- ------
1.txt IAMMRED\ed NT AUTHORITY\Authenticat...
Directory: C:\Afolder
Path Owner Access
---- ----- ------
1.txt IAMMRED\ed BUILTIN\Administrators A...
Store the passed-through object for analysis
I decide to store the resultant object in a variable for analysis. This technique is shown here:
PS C:\> $comp = Compare-Object -ReferenceObject $aacl -DifferenceObject $bacl -Property access -PassThru
PS C:\> $comp[0]
Directory: C:\Bfolder
Path Owner Access
---- ----- ------
1.txt IAMMRED\ed NT AUTHORITY\Authenticat...
PS C:\> $comp[1]
Directory: C:\Afolder
Path Owner Access
---- ----- ------
1.txt IAMMRED\ed BUILTIN\Administrators A...
Now I can tell that it is 1.txt in the two folders that has changed. So I decide to look at the Access property of each file. To do this, all I need to do is to index the $comp variable and look at the Access property. I also decide that I want to see only the ACLs that are not inherited. This is shown here for the 1.txt file in the C:\Bfolder directory. I can easily see that the Authenticated Users and the BUILTIN \Users have FullControl to the 1.txt file in the C:\Bfolder.
PS C:\> $comp[0].Access | ? {! $_.isinherited}
FileSystemRights : FullControl
AccessControlType : Allow
IdentityReference : NT AUTHORITY\Authenticated Users
IsInherited : False
InheritanceFlags : None
PropagationFlags : None
FileSystemRights : FullControl
AccessControlType : Allow
IdentityReference : BUILTIN\Users
IsInherited : False
InheritanceFlags : None
PropagationFlags : None
When I check the 1.txt file in the C:\Afolder directory, nothing returns. This tells me that all ACLs are inherited; and therefore, they have not been modified.
PS C:\> $comp[1].Access | ? {! $_.isinherited}
That is about it for today. The attendants are coming down the aisle to offer coffee or tea. Luckily, I brought my own tea bags, so if I can score a cup of hot water, I will be fine. I bought a box of nice biscuits while in Madrid, so I am set.
Join me tomorrow when we will have some more cool Windows PowerShell stuff.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy