Loading...
 

Reading NTFS and Share Permissions with VBScript and .Net




Reading NTFS and Share security with VbScript

NTFS (File System) and Share security can be enumerated using the Win32_LogicalFileSecuritySetting and Win32_LogicalShareSecuritySetting WMI classes. This post demonstrates how to use each class to read the security descriptors.

In each case the WMI class contains a GetSecurityDescriptor method used to retrieve the Security Descriptor (as a Win32_SecurityDescriptor).

The references section at the bottom of this article include details of sources for all constants. A little PowerShell creeps in to show how the numeric values were retrieved and checked.

DACLs and SACLs

A DACL or Discretionary Access Control List is the most heavily used, it contains Access Control Entries that define who can, and who cannot, access a resource or object. These are seen when viewing the Security tab of an object.

An SACL or System Access Control List defines which actions will be audited when accessing an a resource or object. Seen by accessing the Audit tab through Security and Advanced.

NTFS security vs Share security

Both the NTFS and the Share security descriptor can be read in exactly the same way. However, there are important differences between them.

NTFS security descriptor

  • Supports inheritance on descriptor and on individual Access Control Entries
  • Can hold a Discretionary Access Control List and an System Access Control List
Share security descriptor
  • Has no owner or Primary Group
  • Control flags will be set (and usually limited) to SelfRelative and DiscretionaryACLPresent
  • Access Control Entry (ACE) Flags will not be set
  • Can hold a Discretionary Access Control List only
  • Supports a sub-set of Access Masks (rights) on each ACE

Getting the security descriptor

NTFS

1 Dim strComputer : strComputer = "." 
2 ' Connect to WMI on strComputer  
3 Dim objWMI : Set objWMI = GetObject("winmgmts://" & strComputer & "/root/cimv2")  
4 ' Get an instance of LogicalFileSecuritySetting for C:\  
5 Dim objSecuritySettings : Set objSecuritySettings = _  
6   objWMI.Get("Win32_LogicalFileSecuritySetting='C:\'")  
7 Dim intReturnValue : Dim objSD  
8 ' Request the Security Descriptor as objSD  
9 intReturnValue = objSecuritySettings.GetSecurityDescriptor objSD

Share

1 Dim strComputer : strComputer = "." 
2 ' Connect to WMI on strComputer  
3 Dim objWMI : Set objWMI = GetObject("winmgmts://" & strComputer & "/root/cimv2")  
4 ' Get an instance of LogicalShareSecuritySetting for ShareName  
5 Dim objSecuritySettings : Set objSecuritySettings = _  
6   objWMI.Get("Win32_LogicalShareSecuritySetting='ShareName'")  
7 Dim intReturnValue : Dim objSD  
8 ' Request the Security Descriptor as objSD  
9 intReturnValue = objSecuritySettings.GetSecurityDescriptor objSD

Error handling

The GetSecurityDescriptor method of each WMI class (Win32_LogicalFileSecuritySetting and Win32_LogicalShareSecuritySetting)
has a return value to allow errors to be handled. The return codes are represented by the constants below.
1 Const SUCCESS = 0  
2 Const ACCESS_DENIED = 2  
3 Const UNKNOWN_FAILURE = 8  
4 Const PRIVILEGE_MISSING = 9  
5 Const INVALID_PARAMETER = 21

Values in the security descriptor

The security descriptor contains three fields in addition to the DACL and SACL which are described later.

Control flags

Each security descriptor has a ControlFlags field which dictates how the descriptor behaves. This includes settings such as DACLProtected which indicates that the DACL cannot inherit values from a parent.

The script loads the values above into a Scripting.Dictionary object to simplify enumeration.
01 Dim objControlFlags : Set objControlFlags = CreateObject("Scripting.Dictionary")  
02 objControlFlags.Add 32768, "SelfRelative" 
03 objControlFlags.Add 16384, "RMControlValid" 
04 objControlFlags.Add 8192, "SystemAclProtected" 
05 objControlFlags.Add 4096, "DiscretionaryAclProtected" 
06 objControlFlags.Add 2048, "SystemAclAutoInherited" 
07 objControlFlags.Add 1024, "DiscretionaryAclAutoInherited" 
08 objControlFlags.Add 512, "SystemAclAutoInheritRequired" 
09 objControlFlags.Add 256, "DiscretionaryAclAutoInheritRequired" 
10 objControlFlags.Add 32, "SystemAclDefaulted" 
11 objControlFlags.Add 16, "SystemAclPresent" 
12 objControlFlags.Add 8, "DiscretionaryAclDefaulted" 
13 objControlFlags.Add 4, "DiscretionaryAclPresent" 
14 objControlFlags.Add 2, "GroupDefaulted" 
15 objControlFlags.Add 1, "OwnerDefaulted"

Note that they are intentionally entered in order from highest to lowest. A For Each loop will start with the highest value (because it was added first), then compare it to the ControlFlags value. In bitwise comparison, if the integer value can be there, then it must be there, that would all go wrong unless it starts with the highest possible value.

The values and names were taken from the .NET Framework, they can be displayed in PowerShell with the following command.
1 [Enum]::GetValues([System.Security.AccessControl.ControlFlags]) | `  
2   Select-Object @{ n='Name';e={[String]$_} }, @{ n='Value';e={$_.value__} }

That logic is applied within the code as follows.
01 ' Read the value of Control Flags from the descriptor  
02 dblControlFlags = objSD.ControlFlags  
03 WScript.Echo "Control Flags:" 
04 ' For each possible flag  
05 For Each dblFlag in objControlFlags  
06   ' If it is possible for the flag to be there, then it must be there  
07   ' Something like bitwise AND  
08   If dblControlFlags >= dblFlag Then 
09     ' Echo the flag, indicating it is present  
10     WScript.Echo "  " & objControlFlags(dblFlag)  
11     ' Remove this value from the control flag value,  
12     ' already found this flag  
13     dblControlFlags = dblControlFlags - dblFlag  
14   End If 
15 Next

Primary group

The Primary Group is not very interesting unless Services for Unix is in use. In many cases it will be blank.

If set, it can be enumerated as a Win32_Trustee, an object containing a Domain, Username, SID array, SID length and SIDString.

Owner

As with the Primary Group, the owner is stored as a Win32_Trustee object. It can be read from the descriptor as follows.
1 WScript.Echo "Domain: " & objSD.Owner.Domain  
2 WScript.Echo "Username: " & objSD.Owner.Name  
3 WScript.Echo "SID: " & objSD.Owner.SIDString

Access control entries in the DACL and SACL

Both the DACL and SACL, if present, consist of one or more Access Control Entries (ACE). The ACE, like the security descriptor, breaks down into a number of different fields.

Access Mask

The access mask defines which rights should be used by the Access Control Entry. In the case of System ACL it defines which actions should be audited.

A number of the right names are omitted from the list below as they carry the same numeric value (mask the same bit), the meaning only changes in the context the right is applied.
01 Dim objAccessRights : Set objAccessRights = CreateObject("Scripting.Dictionary")  
02 objAccessRights.Add 2032127, "FullControl" 
03 objAccessRights.Add 1048576, "Synchronize" 
04 objAccessRights.Add 524288, "TakeOwnership" 
05 objAccessRights.Add 262144, "ChangePermissions" 
06 objAccessRights.Add 197055, "Modify" 
07 objAccessRights.Add 131241, "ReadAndExecute" 
08 objAccessRights.Add 131209, "Read" 
09 objAccessRights.Add 131072, "ReadPermissions" 
10 objAccessRights.Add 65536, "Delete" 
11 objAccessRights.Add 278, "Write" 
12 objAccessRights.Add 256, "WriteAttributes" 
13 objAccessRights.Add 128, "ReadAttributes" 
14 objAccessRights.Add 64, "DeleteSubdirectoriesAndFiles" 
15 objAccessRights.Add 32, "ExecuteFile" 
16 objAccessRights.Add 16, "WriteExtendedAttributes" 
17 objAccessRights.Add 8, "ReadExtendedAttributes" 
18 objAccessRights.Add 4, "AppendData" 
19 objAccessRights.Add 2, "CreateFiles" 
20 objAccessRights.Add 1, "ReadData"

Note that a number of the rights in the list are composites of simpler rights, including Read, FullControl, Write and Modify.

The list can be retrieved, using PowerShell again, with the following command:
1 [Enum]::GetValues([System.Security.AccessControl.FileSystemRights]) | `  
2   Select-Object @{n='Name';e={[String]$_} }, @{n='Value';e={$_.value__} }

Holding the flags in this fashion allows the Access Mask to be converted to a friendly form in the same way as the Control Flags were displayed.
01 ' For each access control entry in the discretionary access control list  
02 For Each objACE in objSD.DACL  
03   WScript.Echo "Access Mask:" 
04   ' Read the value of the access mask from the Access Control Entry  
05   dblAccessMask = objACE.AccessMask  
06   ' For each possible Right  
07   For Each dblAccess in objAccessRights  
08     ' If the right can be there then it must...  
09     If dblAccessMask >= dblAccess Then 
10       ' Echo the right name  
11       WScript.Echo "  " & objAccessRights(dblAccess)  
12       ' Remove the value from the access mask,  
13       ' already found this right.  
14       dblAccessMask = dblAccessMask - dblAccess  
15     End If 
16   Next 
17 Next

Flags

The flags on an Access Control Entry defines whether the entry applies to Leaf or Container objects, how it behaves when inheritance is calculated, and whether or not the ACE is inherited or explicit.
1 Dim objAceFlags : Set objAceFlags = CreateObject("Scripting.Dictionary")  
2 objAceFlags.Add 128, "FailedAccess" 
3 objAceFlags.Add 64, "SuccessfulAccess" 
4 objAceFlags.Add 16, "Inherited" 
5 objAceFlags.Add 8, "InheritOnly" 
6 objAceFlags.Add 4, "NoPropagateInherit" 
7 objAceFlags.Add 2, "ContainerInherit" 
8 objAceFlags.Add 1, "ObjectInherit"

Heading back to PowerShell again we can find the the names and values for these.
1 [Enum]::GetValues([System.Security.AccessControl.AceFlags]) | `  
2   Select-Object @{n='Name';e={[String]$_} }, @{n='Value';e={$_.value__} }

A few are intentionally left out as they are composites of values more interesting to display separately.

Enumeration of the Access Control Entry flags is performed as follows.
01 ' For each access control entry in the discretionary access control list  
02 For Each objACE in objSD.DACL  
03   WScript.Echo "ACE Flags:"
04     ' Read the value of ACE Flags from the Access Control Entry  
05   dblAceFlags = objAce.AceFlags  
06   ' For each possible flag  
07   For Each dblFlag in objAceFlags  
08     ' If the flag can be there then it must...  
09     If dblAceFlags >= dblFlag Then 
10       ' Echo the name of the flag  
11       WScript.Echo "  " & objAceFlags(dblFlag)  
12       ' Found it, remove it to note that it has been found.  
13       dblAceFlags = dblAceFlags - dblFlag  
14     End If 
15   Next 
16 Next

Trustee

The value for the trustee (Win32_Trustee), the security principal (user, group, computer, etc) the right applies to is enumerated in the same way as the Owner above.
1 For Each objACE in objDACL  
2   WScript.Echo "Domain: " & objACE.Trustee.Domain  
3   WScript.Echo "User: " & objACE.Trustee.Name  
4   WScript.Echo "SID: " & objACE.Trustee.SIDString  
5 Next

Type

Finally, the ACEType consists of three values which define whether the ACE permits access, denies access or is an audit control.
1 Dim objAceTypes : Set objAceTypes = CreateObject("Scripting.Dictionary")  
2 objAceTypes.Add 0, "Allow" 
3 objAceTypes.Add 1, "Deny"
4 objAceTypes.Add 2, "Audit"

Once again, we can discover all of the possible values for AceFlags using PowerShell.
1 [Enum]::GetValues([System.Security.AccessControl.AceType]) | `  
2   Select-Object @{n='Name';e={[String]$_} }, @{n='Value';e={$_.value__} }

However, this time the only values that are used in the script are 0 (Allow), 1 (Deny), and 2 (Audit).
1 ' For each access control entry in the discretionary access control list  
2 For Each objACE in objDACL  
3   ' Echo the type: Allow, Deny or Audit.  
4   WScript.Echo "ACE Type: " & objAceTypes(objAce.AceType)  
5 Next

Listing permissions for all Shares

Time to put all of that together. This sample lists the NTFS and Share permissions for each share configured on strComputer.
001 Option Explicit 
002    
003 ' WMI Constants  
004    
005 Const WBEM_RETURN_IMMEDIATELY = &h10  
006 Const WBEM_FORWARD_ONLY = &h20  
007    
008 ' Constants and storage arrays for security settings  
009    
010 ' GetSecurityDescriptor Return values  
011    
012 Dim objReturnCodes : Set objReturnCodes = CreateObject("Scripting.Dictionary")  
013 Const SUCCESS = 0  
014 Const ACCESS_DENIED = 2  
015 Const UNKNOWN_FAILURE = 8  
016 Const PRIVILEGE_MISSING = 9  
017 Const INVALID_PARAMETER = 21  
018    
019 ' Security Descriptor Control Flags  
020    
021 Dim objControlFlags : Set objControlFlags = CreateObject("Scripting.Dictionary")  
022 objControlFlags.Add 32768, "SelfRelative" 
023 objControlFlags.Add 16384, "RMControlValid" 
024 objControlFlags.Add 8192, "SystemAclProtected" 
025 objControlFlags.Add 4096, "DiscretionaryAclProtected" 
026 objControlFlags.Add 2048, "SystemAclAutoInherited" 
027 objControlFlags.Add 1024, "DiscretionaryAclAutoInherited" 
028 objControlFlags.Add 512, "SystemAclAutoInheritRequired" 
029 objControlFlags.Add 256, "DiscretionaryAclAutoInheritRequired" 
030 objControlFlags.Add 32, "SystemAclDefaulted" 
031 objControlFlags.Add 16, "SystemAclPresent" 
032 objControlFlags.Add 8, "DiscretionaryAclDefaulted" 
033 objControlFlags.Add 4, "DiscretionaryAclPresent" 
034 objControlFlags.Add 2, "GroupDefaulted" 
035 objControlFlags.Add 1, "OwnerDefaulted" 
036    
037 ' ACE Access Right  
038    
039 Dim objAccessRights : Set objAccessRights = CreateObject("Scripting.Dictionary")  
040 objAccessRights.Add 2032127, "FullControl" 
041 objAccessRights.Add 1048576, "Synchronize" 
042 objAccessRights.Add 524288, "TakeOwnership" 
043 objAccessRights.Add 262144, "ChangePermissions" 
044 objAccessRights.Add 197055, "Modify" 
045 objAccessRights.Add 131241, "ReadAndExecute" 
046 objAccessRights.Add 131209, "Read" 
047 objAccessRights.Add 131072, "ReadPermissions" 
048 objAccessRights.Add 65536, "Delete" 
049 objAccessRights.Add 278, "Write" 
050 objAccessRights.Add 256, "WriteAttributes" 
051 objAccessRights.Add 128, "ReadAttributes" 
052 objAccessRights.Add 64, "DeleteSubdirectoriesAndFiles" 
053 objAccessRights.Add 32, "ExecuteFile" 
054 objAccessRights.Add 16, "WriteExtendedAttributes" 
055 objAccessRights.Add 8, "ReadExtendedAttributes" 
056 objAccessRights.Add 4, "AppendData" 
057 objAccessRights.Add 2, "CreateFiles" 
058 objAccessRights.Add 1, "ReadData" 
059    
060 ' ACE Types  
061    
062 Dim objAceTypes : Set objAceTypes = CreateObject("Scripting.Dictionary")  
063 objAceTypes.Add 0, "Allow" 
064 objAceTypes.Add 1, "Deny" 
065 objAceTypes.Add 2, "Audit" 
066    
067 ' ACE Flags  
068    
069 Dim objAceFlags : Set objAceFlags = CreateObject("Scripting.Dictionary")  
070 objAceFlags.Add 128, "FailedAccess" 
071 objAceFlags.Add 64, "SuccessfulAccess" 
072 objAceFlags.Add 16, "Inherited" 
073 objAceFlags.Add 8, "InheritOnly" 
074 objAceFlags.Add 4, "NoPropagateInherit" 
075 objAceFlags.Add 2, "ContainerInherit" 
076 objAceFlags.Add 1, "ObjectInherit" 
077    
078 Sub ReadNTFSSecurity(objWMI, strPath)  
079   WScript.Echo "  Displaying NTFS Security" 
080    
081   Dim objSecuritySettings : Set objSecuritySettings = _  
082     objWMI.Get("Win32_LogicalFileSecuritySetting='" & strPath & "'")  
083   Dim objSD : objSecuritySettings.GetSecurityDescriptor objSD  
084    
085   Dim strDomain : strDomain = objSD.Owner.Domain  
086   If strDomain <> "" Then strDomain = strDomain & "\"  
087   WScript.Echo "  Owner: " & strDomain & objSD.Owner.Name  
088   WScript.Echo "  Owner SID: " & objSD.Owner.SIDString  
089    
090   WScript.Echo "  Basic Control Flags Value: " & objSD.ControlFlags  
091   WScript.Echo "  Control Flags:" 
092    
093   DisplayValues objSD.ControlFlags, objControlFlags  
094    
095   WScript.Echo  
096    
097   Dim objACE  
098    
099   ' Display the DACL  
100    
101   WScript.Echo "  Discretionary Access Control List:" 
102   For Each objACE in objSD.DACL  
103     DisplayACE objACE  
104   Next 
105    
106   ' Display the SACL (if there is one)  
107    
108   If Not IsNull(objSD.SACL) Then 
109     WScript.Echo "  System Access Control List:" 
110     For Each objACE in objSD.SACL  
111       DisplayACE objACE  
112     Next 
113   End If 
114 End Sub 
115    
116 Sub ReadShareSecurity(objWMI, strName)  
117   WScript.Echo "  Displaying Share Security" 
118    
119   Dim objSecuritySettings : Set objSecuritySettings = _  
120     objWMI.Get("Win32_LogicalShareSecuritySetting='" & strName & "'")  
121    
122   Dim objSD : objSecuritySettings.GetSecurityDescriptor objSD  
123    
124   WScript.Echo "  Basic Control Flags Value: " & objSD.ControlFlags  
125   WScript.Echo "  Control Flags:" 
126    
127   DisplayValues objSD.ControlFlags, objControlFlags  
128    
129   WScript.Echo  
130    
131   Dim objACE  
132    
133   ' Display the DACL  
134    
135   WScript.Echo "  Discretionary Access Control List:" 
136   For Each objACE in objSD.DACL  
137     DisplayACE objACE  
138   Next 
139 End Sub 
140    
141 Sub DisplayValues(dblValues, objSecurityEnumeration)  
142    
143   Dim dblValue  
144   For Each dblValue in objSecurityEnumeration  
145     If dblValues >= dblValue Then 
146       WScript.Echo "      " & objSecurityEnumeration(dblValue)  
147       dblValues = dblValues - dblValue  
148     End If 
149   Next 
150 End Sub 
151    
152 Sub DisplayACE(objACE)  
153    
154   Dim strDomain : strDomain = objAce.Trustee.Domain  
155   If strDomain <> "" Then strDomain = strDomain & "\"  
156   WScript.Echo "    Trustee: " & UCase(strDomain & objAce.Trustee.Name)  
157   WScript.Echo "    SID: " & objAce.Trustee.SIDString  
158    
159   WScript.Echo "    Basic Access Mask Value: " & objACE.AccessMask  
160    
161   WScript.Echo "    Access Rights: " 
162   DisplayValues objACE.AccessMask, objAccessRights  
163    
164   WScript.Echo "    Type: " & objAceTypes(objACE.AceType)  
165    
166   WScript.Echo "    Basic ACE Flags Value: " & objACE.AceFlags  
167    
168   WScript.Echo "    ACE Flags: " 
169   DisplayValues objACE.AceFlags, objAceFlags  
170   WScript.Echo  
171 End Sub 
172    
173 '  
174 ' Main Code  
175 '  
176    
177 ' The system to execute this script against  
178 Dim strComputer : strComputer = "." 
179    
180 ' Connect to WMI  
181 Dim objWMI : Set objWMI = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2")  
182    
183 ' Return all of the shares (Type = 0 means File Shares only, exclude  
184 ' are Administrative, Printer, etc)  
185 Dim colItems : Set colItems = _  
186   objWMI.ExecQuery("SELECT * FROM Win32_Share WHERE Type='0'", "WQL", _  
187   WBEM_RETURN_IMMEDIATELY + WBEM_FORWARD_ONLY)  
188    
189 Dim objItem  
190 For Each objItem in colItems  
191   WScript.Echo  
192   WScript.Echo "Security for " & objItem.Path & _  
193     " (Shared as " & objItem.Name & ")" 
194    
195   ReadNTFSSecurity objWMI, objItem.Path  
196   ReadShareSecurity objWMI, objItem.Name  
197 Next


Show php error messages