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
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).
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
Not to discount your original post Michael, but GRUBI…much easier.
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.