Note that absolutely none of this is authoritative or directly based on relevant documentation. It’s mostly what I found and figured out and guessed and (in some cases) made up. Some of it may be wrong or dangerous or lead to disaster or confusion. I am not taking responsibility here for anything. Read and act on it at your own peril!

The Windows Task Scheduler is a horrible tool.

Not only does it often appear to be highly unreliable (it is not, it’s just ridiculously capricious, as in, a small misconfiguration will cause maximum damage), but it is also pretty much designed to be configured for permissions (hence small misconfigurations happen a lot more often than they should).

The first three things you have to know about Windows Task Scheduler permissions are these:

  1. Scheduled task permissions are stored as byte arrays in the system registry, in a key and subkeys that are not writable even to Administrators (let alone the scheduled task owners).
  2. Scheduled task permissions do not inherit, that is a scheduled task in a scheduled task folder does not inherit the permissions of the scheduled task folder and the scheduled task folder does not inherit permissions from its parent folder either.
  3. The permission getting and setting functionality of the scheduled task cmdlets does not work. And that’s it.

Note that in Windows any object created by a user will then be owned by the user. An owner can always change the permissions on the object. That is what the “discretionary” in “discretionary access control” means. But for scheduled tasks this is not true. While a scheduled task created by user benoit will be “owned” by user benoit, user benoit cannot change permissions on the scheduled task because of point #1 above.

Also note that in Windows any object created by a member of the Administrators group will be owned by the Administrators group. But even a member of the Administrators group cannot change scheduled task permissions. If a member of the Administrators group created a scheduled task, no restricted user will be able to see the task.

Task permissions are stored beneath this registry key:

HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\

PS HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\> dir


    Hive: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree


Name                           Property
----                           --------
Microsoft                      SD : {1, 0, 20, 156...}
MicrosoftEdgeUpdateTaskMachine SD    : {1, 0, 4, 128...}
Core{AF97B5B7-628F-4A2A-A93E-C Id    : {2ACF5743-7CF9-4E2E-8AD5-40FCDDCFA72A}
37F4C6FBFFA}                   Index : 2
MicrosoftEdgeUpdateTaskMachine SD    : {1, 0, 4, 128...}
UA{0675DC18-57C0-41F8-BB75-FD8 Id    : {25E74989-7221-4208-A380-26330BE002C9}
2D1690C93}                     Index : 3
TestFolder                     SD : {1, 0, 4, 140...}

PS HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\>

Permissions for each task or task folder are stored in the Property “SD” of each corresponding key as byte arrays.

Using the WMI helper class Win32_SecurityDescriptorHelper these byte arrays can be translated into security descriptor objects or security descriptor definition language strings.

PS HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\> $sd=(Get-ItemProperty TestFolder).SD
PS HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\> $helper=[wmiclass]"Win32_SecurityDescriptorHelper"
PS HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\> $sddl=$helper.BinarySDToSDDL($sd).SDDL
PS HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\> $sddl
O:BAG:S-1-5-21-344341352-2539047333-2300305637-513D:AI(A;CI;FA;;;BA)(A;OIIO;0x1f019f;;;BA)(A;CI;FA;;;SY)(A;OIIO;0x1f019f;;;SY)(A;CI;FW;;;AU)(A;CI;FW;;;NS)(A;CI;FW;;;LS)(A;OICIIO;FA;;;CO)
PS HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\>

The sddl string shows is the following:

O:BA The owner is this task (actually a folder) is BUILTIN\Administrators
G:S-1-5-21-344341352-2539047333-2300305637-513 This is the group owner of the task, it does not concern us
D:AI This is the start of the discretionary acccess control list for the folder, which is allegedly “AUTO_INHERITED”
(A;CI;FA;;;BA) This is an “Allow” access control element, which gives FA full access to BA BUILTIN\Administrators
(A;OIIO;0x1f019f;;;BA) More rights for BA
(A;CI;FA;;;SY) Full access for SY, the system account
(A;OIIO;0x1f019f;;;SY) More rights for system account
(A;CI;FW;;;AU) FW file write (generic write) for AU Authenticated Users
(A;CI;FW;;;NS) Same for NS Network Service
(A;CI;FW;;;LS) Same for LS Local Service
(A;OICIIO;FA;;;CO) And full access for CO Creator Owner (the owner of the object)

In TuskForce this can be seen better:

image

You can download TuskForce as part of the Elephant permissions editor here: introduction

In order to change any of those permissions you need to be either running as LocalSystem or allow yourself (or better a group you are in) full access to the respective registry key hierarchy.

You can then use this PowerShell script to grant full access to a folder or task to a specific user or group: scheduled tasks permissions script

I recommend creating a scheduled task running as LocalSystem that will regularly run this script against certain task folders to reset the permissions.

PS C:\> (Get-ScheduledTask "SchedTaskPermissions TestFolder").Actions

Id               :
Arguments        : C:\SchedTaskPermissions.ps1 TestFolder benoit -Recurse -NoConfirm
Execute          : powershell.exe
WorkingDirectory : C:\
PSComputerName   :

PS C:\>

You can set it up in the task scheduler GUI. Just make sure it runs as LocalSystem and create one for each task folder that must be accessible by a specific group or user.

Note that you probably want folder names to be more specific and you want a group for each folder rather than user benoit. Also don’t keep the permissions script on C:\.

Such a scheduled task will keep tasks in specific folders accessible to specific groups. You can scheduled the task to run daily our hourly and/or make it available to all users to run when they need to fix permissions on their scheduled tasks.


Annex: How to create a scheduled task using PowerShell

PS C:\> $action=New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c cls"
PS C:\> $task=New-ScheduledTask -Action $action -Settings (New-ScheduledTaskSettingsSet)
PS C:\> Register-ScheduledTask -TaskName "BenoitTestTask" -User "benoit" -Password "Password1" -InputObject $task

TaskPath                                       TaskName                          State
--------                                       --------                          -----
\                                              BenoitTestTask                    Ready


PS C:\> Get-ScheduledTask BenoitTestTask

TaskPath                                       TaskName                          State
--------                                       --------                          -----
\                                              BenoitTestTask                    Ready


PS C:\> Start-ScheduledTask BenoitTestTask
PS C:\> Unregister-ScheduledTask BenoitTestTask

Confirm
Are you sure you want to perform this action?
Performing operation 'Delete' on Target '\BenoitTestTask'.
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
PS C:\>

To create a scheduled task that runs as LocalSystem, do the following registration (as an administrator):

Register-ScheduledTask -TaskName "BenoitTestTask" -User "System" -InputObject $task

Next: Sudo