- Access control lists
- Permission inheritance
- Mandatory access control or integrity levels
- icacls syntax
- Getting help
- Displaying the ACL
- Setting permissions
- Advanced permissions
- Inheriting permissions
- Removing permissions
- Denying permissions
- Resetting the ACL
- Setting ownership
- Exporting and importing an ACL
- Determining user rights
- Managing Windows integrity control
The most common task for an admin is to modify the permissions of various objects. The file explorer's Security tab works fine for adjusting a few permissions, but changing a lot of permissions using the file explorer is monotonous and eventually becomes tedious if you happen to do it on a regular basis. What if you could use a built-in command line tool to do that job for you? The icacls utility is built into Windows to help you.
In this article, you will learn how to manage file and folder permissions with the help of icacls. Before diving into the icacls command directly, you should be aware of certain things related to permissions and security in Windows.
Access control lists ^
In computer security, ACL stands for "access control list." An ACL is essentially a list of permission rules associated with an object or resource. Each permission rule in an ACL is known as an access control entry (ACE), which controls access to an object by a specified trustee, such as a person, group, or session. These types of access control lists are called discretionary access control lists (DACLs). In a DACL, permissions are generally set by the administrator or owner of the object. The NTFS permissions in Windows are an example of a DACL.
Windows supports the following types of permissions in a DACL:
- Basic permissions
- Full access (F)
- Modify access (M)
- Read and execute access (RX)
- Read-only access (R)
- Write-only access (W)
- Advanced permissions
- Delete (D)
- Read control (RC)
- Write DAC (WDAC)
- Write owner (WO)
- Synchronize (S)
- Access system security (AS)
- Maximum allowed (MA)
- Generic read (GR)
- Generic write (GW)
- Generic execute (GE)
- Generic all (GA)
- Read data/list directory (RD)
- Write data/add file (WD)
- Append data/add subdirectory (AD)
- Read extended attributes (REA)
- Write extended attributes (WEA)
- Execute/traverse (X)
- Delete child (DC)
- Read attributes (RA)
- Write attributes (WA)
The letters in parentheses indicate the short notation you will use with the icacls command when setting a particular permission. For example, to grant test.user a write permission on file1.txt, you will use icalcs as shown below:
icacls file1.txt /grant test.user:W
Don't worry about the command if you don't understand it yet; I just wanted to show what the letters in parentheses really mean at this point. To grant full access, you would just write test.user:F instead of test.user:W.
Since you will see the terms ACL and ACE a lot throughout this guide, the following image will help you clearly understand and distinguish them:
Permission inheritance ^
Permissions can either be explicitly defined on an object or can be inherited from a parent container. Windows supports the following types of inherited permissions:
- Inherit (I)—The ACE is inherited from the parent directory.
- Object Inherit (OI)—The objects in the current directory inherit the specified ACE; applicable only to directories.
- Container Inherit (CI)—The subdirectories in the current parent directory inherit the specified ACE; applicable only to directories.
- Inherit Only (IO)—The ACE is inherited from the parent directory but does not apply to the object itself; applicable to directories only.
- Not Propagate (NP)—The ACE is inherited by directories and objects from the parent directory but does not propagate to nested subdirectories; applicable to directories only.
Again, the letters in parentheses indicate the short notation you will use with the icacls command when setting permissions with inheritance. You can see that most inheritance attributes apply only to directories. You will learn more about permission types and how inheritance works later in this guide.
Mandatory access control or integrity levels ^
In mandatory access control (MAC), permissions are defined by policy-based fixed rules and generally cannot be overridden by users. Starting with Windows Vista and Server 2008, Microsoft introduced mandatory integrity control (MIC)—a form of MAC—to add an integrity level (IL) for most objects in Windows. It is also referred to as Windows integrity control (WIC) or Windows integrity level (WIL), but we will call it IL throughout this guide. The integrity level is used to determine the level of trustworthiness or protection of an object (or process) from the perspective of Windows. There are six integrity levels in Windows:
- Untrusted—The lowest level of trustworthiness. The processes that are anonymously logged on are automatically allocated an untrusted IL by Windows.
- Low—The processes that directly interact with the Internet are allocated a low IL by default. Such processes have very limited access to files and directories.
- Medium—The processes started by standard and non-admin users are allocated an IL of medium by default. This is the default and implicit IL in Windows. The objects lacking an IL are by default treated as medium by Windows.
- High—The high IL is allocated to the processes running with an elevated security token (processes launched using the Run as Administrator option).
- System—The system IL is allocated to the core operating system processes and services.
- Trusted installer—The trusted installer IL denotes the highest level of trustworthiness.
In a nutshell, you could say that MIC and IL are more restrictive defense mechanisms used by Windows that override the NTFS permissions (DACL) and evaluate the object's access before the DACL does. Therefore, a process with a lower IL cannot write to an object with a higher IL, even if there are full NTFS permissions on that object. Windows uses the concept of ILs to protect the core files and processes, so even if you've got full control on a core system file, you will still get an Access is denied error when you delete that file.
To view the IL of a process in Windows, you can use the Process Explorer tool from Sysinternals. The following screenshot shows that most core Windows processes are running with System integrity, the user processes are running with Medium integrity, and the processes launched with elevated tokens (e.g., powershell and procexp64) are running with High integrity.
The icacls command is primarily used to manage DACLs in Windows, but it can also be used to manage ILs with certain limitations.
The terms MAC, WIC, WIL, IL, MIL, etc., used throughout this guide, essentially mean the same thing. Later in this guide, we will see how to use icacls to view and modify the ILs. Now let's get started.
icacls syntax ^
The following syntax shows how to use icacls with a file object:
icacls <filename> [/grant[:r] <sid>:<perm>[...]] [/deny <sid>:<perm>[...]] [/remove[:g|:d]] <sid>[...]] [/t] [/c] [/l] [/q] [/setintegritylevel <Level>:<policy>[...]]
The following syntax shows how to use icacls with a directory object:
icacls <directory> [/substitute <sidold> <sidnew> [...]] [/restore <aclfile> [/c] [/l] [/q]]
Don't worry if the syntax looks a little complicated. It will eventually become clear as we progress through this guide.
Getting help ^
First, let's take a look at the Help section. To view the help, just run the icacls command without any parameters, as shown below:
The help section displays all the parameters supported by the icacls command along with a few examples. I will try to cover as much as possible with the help of examples.
Displaying the ACL ^
To display the current ACL for an object, run the icacls command with the name of the object (file or directory). The following command shows the ACL for a directory object:
where RnD is a directory in my C: drive.
When you run the icacls command on a file object, the output is slightly different:
You can see that the ACL of the directory contains values such as (OI) or (CI), but you cannot see these in the file ACL. Instead, you will see an (I), which means the ACE is inherited from its parent container (the RnD directory, in this case). In the Access Control Lists section, we mentioned that (OI), (CI), (IO), and (NP) are inheritance rights and are applicable only to directories (a.k.a. containers).
Let me briefly explain the ACL output returned by this command.
- The first part of the output, (NT AUTHORITY\SYSTEM), is the username. This can be a user, a group, or a special identity, such as Everyone, Authenticated Users, or Network Service.
- The second part, (OI), indicates that the other (new or existing) objects in this container will inherit this ACE. You will see this only in the case of a directory.
- The third part, (CI), indicates that other containers (or directories) in this parent container will inherit this ACE. Again, you will see this only in the case of a directory.
- The last part, (F), indicates that the user specified in the first part has Full
I hope it has now started making a little sense to you. This will become clearer in the upcoming sections. Let's keep going.
Setting permissions ^
In the last example, we saw that the directory name RnD was accessible to SYSTEM, Administrators, and Users only. Anyone else who tries to access this directory will be denied access, since implicit deny is the default behavior of an ACL. If you want to add the special identity Everyone to this ACL and then grant them a Read permission recursively, you can use the icacls command, as shown below:
icacls RnD /grant everyone:R /t /c
- /grant parameter—Adds a new ACE in the ACL, granting read access to the special identity Everyone. You can also combine multiple rights. For example, to grant read and write permission, use Everyone:RW.
- /t parameter—Specifies a recursive operation, which means the permissions will be updated on all the files and subdirectories in the specified directory (RnD, in our case).
- /c parameter—Specifies a continued operation despite any errors. This option is particularly useful during bulk permission changes using a script when you don't want your script to stop executing even if there are errors.
- /q parameter—Suppress success messages. By default, the command displays all the success messages for each operation.
Note that using special identities, such as Everyone, Authenticated Users, Network Service, etc., with the icacls command only works if the system language is set to English. If you're working on a non-English system, use the SID format to specify such special identities. So, on a non-English system, the above command needs to be used as shown below:
icacls RnD /grant *S-1-1-0:R /t /c
The SID should be prefixed with an asterisk (*); S-1-1-0 is the well-known SID for the Everyone identity. To know the well-known SIDs for all special identities, see this article.
Now that we've run the above command, let's take a look at the ACL of the RnD directory.
icacls RnD /t
where the /t parameter is used to recursively list the ACLs of all the child objects.
The Everyone identity is now added to every file and subdirectory inside the RnD parent directory because of the /t parameter.
Advanced permissions ^
To grant or deny advanced permissions, the syntax of the icacls command is slightly different. For instance, if you want to give the Auditors group the ability to write NTFS permissions, you need to give that group the Write DAC (WDAC) permission. To do that, use the following command:
icacls RnD /grant:r Auditors:(WDAC) /t /c
Notice that the advanced permissions need to be enclosed in parentheses. You can specify the multiple permissions in a comma-separated string in parentheses. For example, to specify the Read Extended Attributes (REA) permission along with (WDAC), write it as follows:
Inheriting permissions ^
With the previous command, we assigned the special identity Everyone a Read permission recursively to all the child objects in our RnD directory. If we take a closer look at the ACL of the dir1 subdirectory, which is inside the RnD directory, we can see that the ACL shows Everyone with just an (R), indicating the expected read permission. But if we create a new subdirectory, dir2, and then view its ACL, we can see that there is no ACE for the Everyone identity.
This happened because we had not yet set the RnD parent directory with inheritable permissions. The /t option is only useful for setting permissions on objects that already exist. But what about objects such as files or directories that will be created in the future? The permissions for such objects will be handled by inheritance. By default, when an ACE is set with the OI permission, it is applied to the files in the directory but not to the subdirectories. In the same way, the ACE set with the CI permission is applied to the subdirectories, but not to the files. Therefore, to obtain a combined result, we need to use both the OI and CI permissions together. Take a look at the following command:
icacls RnD /grant:r Everyone:(OI)(CI)W /t
The /grant:r parameter causes the Read only permission for Everyone in the existing ACE to be replaced with Write. If you do not add :r with the /grant parameter, a new ACE will be added instead of replacing the existing one. Now let's create another subdirectory, dir3, inside the RnD parent directory and view its ACL.
Notice that the new directory, dir3, inherited the ACE from the RnD parent directory. This is how inheritance works.
Removing permissions ^
To remove a permission from a user (or group), you just have to remove the corresponding ACE from the object's ACL. Don't forget to disable the inheritance from that object beforehand (if the target is a directory). For instance, to remove the Everyone identity from the dir3 directory, we will use the icacls command, as shown below:
icacls RnD\dir3 /inheritance:d /t /c icacls RnD\dir3 /remove:g Everyone /t /c
- In the first command, the /inheritance:d parameter disables the inheritance on the directory and copies the ACEs. To directly disable the inheritance without copying the ACEs, and then remove the inherited ACEs, you could use /inheritance:r; however, this operation is a bit risky. You might get yourself locked out, as this could remove your own access. To reenable the inheritance, use the /inheritance:e
- In the second command, the /remove:g parameter removes the grant permissions from the Everyone To remove the deny permissions, use the /remove:d parameter.
Denying permissions ^
Normally, there is no need to define a deny permission explicitly, since implicit deny is there by default. Every experienced admin will suggest that you avoid the explicit deny since it could cause unexpected results. For example, a user is a member of two groups, and you add both groups to the ACL of a directory. One group has the grant ACE, and the other has a deny ACE; guess what will happen? The deny ACE will win, and the user will be denied access. This could give you a lot of headaches if you manage a lot of groups. The best approach is to define the grant ACEs for whatever groups you want, and the remaining users and groups will be denied access implicitly.
However, if you still want to define a deny permission explicitly, icacls allows you to do that, too. For example, to deny Full Control to the Developers group on the HR directory containing the important records of all the employees, use the following command:
icacls D:\FileShare\HR /deny Developers:(OI)(CI)F /t /c
Note that explicitly denying permission overrides any permission explicitly granted to the same user or group.
To remove the deny permission, use the following command:
icacls D:\FileShare\HR /remove:d Developers /t /c
Notice the use of the /remove:d parameter in this command. It will not work if you use the /remove:g parameter since we are removing the deny permission here. To remove a grant permission, use the /remove:g parameter.
Resetting the ACL ^
There are situations in which you might want to reset the permissions to default. For example, a junior admin messed up the permissions on a program's directory, which broke its functionality, or a malware attack corrupted the ACL of an important directory. In such cases, you could use icacls with the /reset parameter to reset the permissions to the default. The following command shows how to reset permissions:
icacls RnD /reset /t /c
The /reset parameter is equivalent to the Replace all child permission entries with inheritable permission from this object option in the GUI.
Setting ownership ^
You can use the icacls command to set ownership on directories and files. The following command sets the owner Surender on the RnD directory recursively:
icacls RnD /setowner Domain\Surender /t /c /q
- /setowner parameter—Makes the specified user or group the new owner. You can also specify the username in UPN format (i.e., firstname.lastname@example.org).
- /t switch—Perform the set ownership operation recursively.
- /c switch—Indicates a continued operation, even if errors occur.
- /q switch—Perform the operation quietly and suppress the success messages.
Unfortunately, the icacls command does not offer any way to view the owner of an object, but you can use the dir /q command as shown in the screenshot below.
You can see that the owner is now recursively changed on the RnD directory and all its child objects.
Exporting and importing an ACL ^
One of the coolest features of the icacls command is its ability to export the ACL of an object to a file and then use that backup file to import the ACL back to restore the permissions. This feature is loved by most admins, since it makes the monotonous task of setting permissions very easy. Whenever you have to do a bulk permission change on huge directories, it is recommended to back up the existing permissions with the help of the icacls command so that if something goes wrong, you can restore the permissions.
To export the ACL, use the icacls command with the /save parameter as shown below:
icacls RnD /save rnd_acl_backup /t
This command will save the ACL of the RnD directory to the rnd_acl_backup file in the current working directory, as shown in the following screenshot.
Now, I will modify some permissions on this directory and restore them using the backup file we created. Let's take a look at the directory permissions for a moment.
The screenshot shows that the test.user has a deny write permission, the Everyone identity has full control, and so on. To restore permissions from the backup file, use the following command:
icacls C:\ /restore rnd_acl_backup
You need to provide the path of the parent directory for the /restore parameter to work properly. If you try to use the command as shown below, you will get an error.
icacls C:\RnD /restore rnd_acl_backup
If you take a closer look, the error itself indicates that icacls is looking for a C:\RnD\RnD directory, which doesn't exist. So, you got an error stating, 'The system cannot find the file specified.' If you open the ACL backup file in a text editor, you will notice that there are references for the relative path to the RnD directory itself. Therefore, you need to carefully type the directory path when using the /restore parameter.
To fix this error, you just need to provide the path of the main directory where the RnD directory actually exists. Moreover, it really depends on how you backed up the ACL while using the /save parameter. Some people prefer doing it this way:
icacls RnD\* /save rnd_acl_backup_2 /t
This command will not save the ACL of the parent directory (RnD, in our case) itself. If you save the ACL backup file this way, you will notice that there is no reference to the RnD parent directory.
To restore this backup ACL file, you can use the previous command that gave you an error, like this:
icacls C:\RnD /restore rnd_acl_backup_2
Don't make changes to the ACL backup file by opening it in a text editor. While doing so might sound intriguing to some people, it could render the ACL backup files unusable, so it is never recommended.
Furthermore, the target directory where you restore the ACL does not necessarily need to be the same. With icacls, you can save the ACL of a container and then restore that ACL to a different container. The following screenshot shows how to do this.
Another important feature you get while restoring the ACL with the icacls command is the /substitute parameter. As the name suggests, you can use this parameter to replace a user (group or SID) with another user. Let's understand this with the help of an example.
Suppose you have a backup of an ACL for a really big file server share. You are going to import the permissions back using the /restore parameter. The problem is that the backup file is slightly old, and it has a grant ACE for an old admin user, John, who is no longer working in the organization. He is now replaced with a new admin user, Mike. The good news is that you can use /restore along with the /substitute parameter to replace John with the new user, Mike, on the fly while restoring the permissions using the icacls command. Since the file shares can be really big, you won't have to spend extra time replacing the outdated users after the ACL is restored. The following command shows how to do this:
icacls D:\ /restore file_share_acl /substitute John Mike /t /c /q
where file_share_acl is the ACL backup filename that is supplied by the /restore parameter and John is the old user followed by Mike, the new user supplied by the /substitute parameter. This command recursively restores the permissions and replaces the old user John with new user Mike while preserving the rights.
Determining user rights ^
There are situations when you, as an admin, might want to determine which user has what permissions. If we consider the previous example, where I restored the ACL on a file share and replaced the old user with a new user, you might want to determine whether there are any files or directories in the D: drive of the file server to which the old user, John, still has access. To do this, icacls offers a /findsid parameter. The following command shows the files and directories with the user John listed in their ACL.
icacls d:\ /findsid John /t
You can see that the user John is listed on two main directories, D:\DRV and D:\SQL, and their child objects. Once you determine that, you can go ahead and replace the user with a new one or just remove that user from the ACL using the /remove parameter, as discussed above.
Managing Windows integrity control ^
As promised earlier, it's now time to learn how to manage MAC or IL using the icacls command. While there are six ILs in Windows, the primary limitation of icacls is that it only allows you to work with the low, medium, and high ILs. It doesn't allow the use of the restricted, system, and trusted installer ILs.
Keeping this in mind, let's first understand how to view the IL for an object. The icacls command displays the IL as a Mandatory Label (or Mandatory Level). The following example shows how to view the IL of a directory:
where RnD is the name of the directory.
If you're following this guide, you probably won't see this Mandatory Label in the output. This is because when you create an object, it will get a medium IL by default and will not show up when you use the icacls command. Remember, the medium IL is default and implicit in Windows. To be able to view the Mandatory Label, you need to explicitly set the IL on the object using icacls, which we will see in a moment.
In the output of the above command, the Low Mandatory Level indicates the low IL and (NW) indicates the no write up integrity policy, which is used to restrict write access on an object coming from a lower IL process. Let's understand this with the help of an example:
I will now run an elevated command prompt, which will give my user account and cmd.exe process a high IL. Like other objects, the user's logon session also gets an IL. To see the IL of a user, just run the whoami /groups command and you will see a Mandatory Label field. The following screenshot shows the output of this command from a non-elevated command prompt:
Notice that the user account gets a medium IL (or Mandatory Label) by default. If you run the same command in an elevated command prompt, you will see a high IL.
Now, in the elevated command prompt, I will create a directory testDir and then use the icacls command to set a high IL on it:
icacls testDir /setintegritylevel h
The /setintegritylevel parameter can only accept l (for low), m (for medium), and h (for high) ILs. If you try to set the system or untrusted IL as shown in the following screenshot, you will get an error: The parameter is incorrect.
You get this error since the icacls command doesn't allow you to work with the system, untrusted, or trusted installer ILs.
Anyway, the most important thing to remember is that you cannot set the IL beyond your own user account. For example, if my user account has a low IL, I cannot set any object with a medium or high IL. When my user account has a high IL (for an elevated process), I can set an object with a high, medium, or low IL. In short, the IL that I can set is equal to or less than the IL of my own user account, as shown in the following screenshot:
Here, you can see the high mandatory level assigned to testDir.
Now, you might be wondering how this is helpful for admins. Well, if someone with a low or medium IL tries to write to the testDir directory, he will get an Access is denied error even though he's got a Full Control NTFS permission in the ACL. The following screenshot will help you better understand this:
You can see that the test.user had Full Control on the testDir we created earlier. But he still couldn't write to that directory, thanks to the high IL. Admins can use this trick to prevent standard users (or their processes) from writing to important directories or files.
However, does this prevent those users from reading the contents of the directory or file? Hmmm, this is the limitation of icacls. Just recall the NW policy that I explained earlier. It restricts the write access to an object coming from a lower IL. It doesn't restrict the read access. There are no read up (NR) and no execute up (NX) policies, too. The NR integrity policy prevents low integrity processes from reading high integrity objects. Similarly, the NX policy prevents low integrity processes from executing high integrity objects. Unfortunately, however, we cannot use icacls to set NR and NX integrity policies. The Windows processes, by default, get an NR integrity policy to prevent low integrity processes from reading their address space.
If we could somehow set the NR integrity policy on a directory or file, it would definitely prevent other users from reading the content. Unfortunately, there is no such tool built in with Windows. However, there is a third-party tool named chml, developed by Mark Minasi, back in the days of Windows Vista. If you want to give it a try, you can do so at your own risk. This free tool allows setting up the untrusted or system IL on objects, and you can even set the NR or NX integrity policies.
The following screenshot shows how to use chml to set the system IL on testDir along with the NR, NW, and NX integrity policies:
The chml tool supports an -fs (force system) switch, but it sometimes does not work as expected in the modern versions of Windows. By the way, if you are stuck in a similar situation where you cannot open or delete a directory, you can use psexec with the -s switch, as described in the How to use PsExec guide, to launch cmd with system account privileges and then use chml to set a lower IL on that directory. In this way, you will be able to delete that directory successfully.
That is all for this guide. I know I haven't covered everything related to the icacls utility in this guide, but it surely can help you get started. If you're stuck somewhere, don't forget to take a look at the help section of the command.