Windows: Permit a limited user to run a schedule task defined by an Administrator

Hi,

This post is developed by the trial and error principle 😉 . Here you can find some background.

On Windows a User with just “normal” user rights cannot see and execute Tasks created from User with Administrator permissions. There are dozed of sites that describes that the permissions of an task can be changed by setting the filesystems rights in C:\Windows\System32\Tasks. But this won’t work since some early Versions of Windows 10 (IMHO 1607).

Ok, let’s start. Let use assume you have a Task MyTask in Folder MyAdminTasks.

Then you have to adjust 2 Security Descriptors.

The 1st is necessary to see, modify or delete the task. It is located in the Registry Path

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\MyAdminTasks\MyTask\SD

The 2nd is the SecurityDescriptor Value in the Tasks Key. The Task ID can be determined under the Tree Key

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\MyAdminTasks\MyTask\Id

And the task itself is located in den TaskCache\Tasks key.

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

The SecurityDescriptor value controls who can execute a Task. If the SecurityDescriptor value not exists only Administrators and the User who created the Task can execute it.

For example the builtin Task SilentCleanup

[Reflection.Assembly]::LoadWithPartialName("System.Security.Principal")
$sExampleTask="Microsoft\Windows\DiskCleanUp\SilentCleanup"
$sTaskPath=$sExampleTask
$oACL=[System.Security.AccessControl.RawSecurityDescriptor]::new((Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$sTaskPath").SD,0)
foreach($oACLEntry in $oACL.DiscretionaryAcl)
{
    $oSID = New-Object System.Security.Principal.SecurityIdentifier($oACLEntry.SecurityIdentifier) 
    write-host "User" (($oSID.Translate([System.Security.Principal.NTAccount])).Value) $oACLEntry.AccessMask
}
    User Administrators AccessMask: 2032127
    User SYSTEM AccessMask: 2032127
    User Authenticated User AccessMask: 1179817
    User Local Service AccessMask: 1179817
    User Administrators AccessMask: 2032031
    User SYSTEM AccessMask: 2032031
    User Authenticated User AccessMask: 1179785
    User Lokaler Dienst AccessMask: 1179785
    User Network Service AccessMask: 1179785
    User Administrators AccessMask: 2032127
$sTaskUID=(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$sTaskPath").Id
$sTaskACL=(Get-ItemProperty ("HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$sTaskUID")).SecurityDescriptor
# $oDACL=ConvertFrom-SddlString $sTaskACL
$oDACL=[System.Security.AccessControl.RawSecurityDescriptor]::new($sTaskACL)
foreach($oACLEntry in $oDACL.DiscretionaryAcl)
{
    $oSID = New-Object System.Security.Principal.SecurityIdentifier($oACLEntry.SecurityIdentifier) 
    write-host "User" (($oSID.Translate([System.Security.Principal.NTAccount])).Value) "AccessMask:" $oACLEntry.AccessMask
}
User Administrators AccessMask: 2032127
User SYSTEM AccessMask: 2032127
User Authenticated User AccessMask: 1179817


Typically 3 Access Masks are found on a tasks which could controlled from Authenticated users: 2032127 -> 0x1f01ff (System , Administrators), 1179817-> 0x1200a9 (SYSTEM, Authenticated User, Local Service, Network Service) and 1179785 is the readonly access mask.

This simple function converts the access mask to a bitmask.

function Int32BitMask([int32]$Value)
{
	for($i=31;$i -ge 0;$i--){write-host -NoNewLine ([string]::Format(" {0:d2}",$i))}
	write-host ""
	for($i=31;$i -ge 0;$i--){if($Value -band ([math]::Pow(2,$i))){write-host -NoNewLine ([string]::Format("  {0:d1}",1))}else{write-host -NoNewLine ([string]::Format("  {0:d1}",0))}}
	write-host ""
}	

This is the bitmask for read only user.

PS D:\> $AccessMask=1179785 
PS D:\> Int32BitMask $AccessMask
 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
  0  0  0  0  0  0  0  0  0  0  0  1  0  0  1  0  0  0  0  0  0  0  0  0  1  0  0  0  1  0  0  1

This is the bitmask for standard (Authenticated) user. Bit 0-7 are the object type specific access mask and Bit 5 controls if a user could run a task

PS D:\> $AccessMask=1179817
PS D:\> Int32BitMask $AccessMask
 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
  0  0  0  0  0  0  0  0  0  0  0  1  0  0  1  0  0  0  0  0  0  0  0  0  1  0  1  0  1  0  0  1

According to MSDN the Standard Access Mask Bit 17 and 20, READ_CONTROL| SYNCHRONIZE

While System and Administrators have all rights SYNCHRONIZE|WRITE_OWNER|WRITE_DAC|READ_CONTROL|DELETE

PS D:\> $AccessMask=2032127 
PS D:\> Int32BitMask $AccessMask
 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
  0  0  0  0  0  0  0  0  0  0  0  1  1  1  1  1  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1  1

So the easiest way to permitted non administrative Users, means Authenticated Users, to execute tasks created by an Adminstrators is to copy both security descriptor registry values from an well defined Task to your Task.

Note you have to do this with the SYSTEM Account. Adminstrators does not have the permissions to change the registry under HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule.

Here are the steps to open a powershell as User SYSTEM. Get psexec from Microsoft Sysinternals. Open a command shell elevated as Administrator. Then start powershell as SYSTEM

C:\> psexec -s -h powershell.exe
PsExec v2.2 - Execute processes remotely
Copyright (C) 2001-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

Windows PowerShell
Copyright (C) Microsoft Corporation. 
PS C:\> whoami
system

The code 🙂 The Taskpathes TemplateTask and MyTask are relative to HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree registry key

PS D:\> $TemplateTask="Microsoft\Windows\DiskCleanUp\SilentCleanup"
PS D:\> $MyTask="MyAdminTasks\MyTask"
PS D:\> $sTaskUID=(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$TemplateTask").Id
PS D:\> $sTaskDefACL=(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$TemplateTask").SD
PS D:\> $sTaskACL=(Get-ItemProperty ("HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$sTaskUID")).SecurityDescriptor
PS D:\> $sMyTaskUID=(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$MyTask").Id
PS D:\> New-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$MyTask" -Name "SD" -Value $sTaskDefACL -Type Binary -Force 
PS D:\> New-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$sMyTaskUID" -Name "SecurityDescriptor" -Value $sTaskACL -Type String -Force
PS D:\> 

The hard way is to manipulate the Security Descriptor by adding ace (System.Security.AccessControl.CommonAce) entries.

Determine the SID of the Account/Group you want to add, Accessmask 1179817 means Read & run permissions on the task MyAdminTasks\MyTask. In this example the User MyUser

PS D:\> # Local user for example
PS D:\> $sTaskPath="MyAdminTasks\MyTask"
PS D:\> $UserToAdd="MyUser"
PS D:\> $SID=(get-localuser $UserToAdd).SID.Value
PS D:\> # $SID="S-1-5-21-103132666-1872472456-3852854213-672354"
PS D:\> $AccessMask=1179817
PS D:\> $oNewACE=new-object System.Security.AccessControl.CommonAce([System.Security.AccessControl.AceFlags]::None,`
       [System.Security.AccessControl.AceQualifier]::AccessAllowed,`
       $AccessMask,`
       (new-object System.Security.Principal.SecurityIdentifier($SID)),`
       $false,`
       ([byte[]]@()))
PS D:\> $oMyTaskACL=[System.Security.AccessControl.RawSecurityDescriptor]::new((Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$sTaskPath").SD,0)
PS D:\> $sMyTaskUID=(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$sTaskPath").Id
PS D:\> $oMyTaskACL.DiscretionaryAcl.InsertACE($oMyTaskACL.DiscretionaryAcl.Count,$oNewACE)
PS D:\> $aACLBytes=([wmiclass]"Win32_SecurityDescriptorHelper").SDDLToBinarySD($oMyTaskACL.GetSddlForm(15)).BinarySD
PS D:\> New-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$sTaskPath" -Name "SD" -Value $aACLBytes -Type Binary -Force
PS D:\> New-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$sMyTaskUID" -Name "SecurityDescriptor" -Value $oMyTaskACL.GetSddlForm(15) -Type String -Force

Ok thats is, comments are welcome :-).

Update 21.02.2020 Added a Script to edit the ACE entries. Does currently not take care about duplicate ACE Entries!

Michael

Permit a limited user to run a schedule task defined by an Administrator
Set-RunPermissionsOnScheduleTask.ps1
Version: 1

Permit a limited user to run a schedule task defined by an Administrator

Author:Michael
Category:Powershell Scripts
Date:February 21, 2020
4.9 KiB
915 Downloads
Details...

5 thoughts on “Windows: Permit a limited user to run a schedule task defined by an Administrator”

  1. There is a simpler way to achive this using PowerShell.

    $scheduler = New-Object -ComObject “Schedule.Service”
    $scheduler.Connect()
    $task = $scheduler.GetFolder(“”).GetTask(“”)
    $sec = $task.GetSecurityDescriptor(0xF)
    $sec = $sec + ‘(A;;GRGX;;;AU)’
    $task.SetSecurityDescriptor($sec, 0)

    This will add read and execute permissions for the group of authenticated users to the specified task. If you recheck the permissions you set you will see that the access rights “GRGX” are translated to 1179817 (0x1200a9).

    1. Works great on Win7, but on Win7 I get this error. Any idea?

      PS C:\Windows\system32> $sec = $task.GetSecurityDescriptor(15)
      Exception calling “GetSecurityDescriptor” with “1” argument(s): “A required privilege is not held by the client. (Exception from HRESULT: 0x80070522)”
      At line:1 char:35
      + $sec = $task.GetSecurityDescriptor <<<< (15)
      + CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
      + FullyQualifiedErrorId : ComMethodTargetInvocation

  2. Hello Grubi,

    trying to do this on Windows Server Machines getting this error:

    This security ID may not be assigned as the owner of this object. (Exception from HRESULT: 0x8007051)

    Any idea? I welcome any help.

Leave a Reply Cancel reply