3

In previous versions of Windows we could allow a user to execute a scheduled task by granting Read+Execute permissions to the file in WINDOWS\SYSTEM32\Tasks.

This no longer works in Windows 2016. I tried granting Full access to the MyTask file for Everyone. But still I get:

schtasks.exe /Run /TN "MyTask"
ERROR: Access is denied.

Has anyone figured out how to make this work in Windows 2016?

EDIT: I granted SeBatchLogonRight to the account with ntrights but it made no difference.

richb
  • 141
  • 1
  • 5
  • **Log on as a batch job** set for the user in Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment ? – yagmoth555 Oct 21 '16 at 01:41
  • @yagmoth555 Yes the user account is listed in Log on as a batch job – richb Oct 21 '16 at 01:43

3 Answers3

1

There is a powershell script in this thread that will grant Authenticated users read and execute permissions to a task:

https://social.technet.microsoft.com/Forums/windows/en-US/6b9b7ac3-41cd-419e-ac25-c15c45766c8e/scheduled-task-that-any-user-can-run?forum=win10itprogeneral

  • 2
    It would be better to show the key idea of the answer here, so your answer will still be useful when the linked content goes away. – Andrew Schulman May 17 '21 at 17:58
0

Magic Code still requires PSExec to elevate permissions to the “System Account” because the registry keys cannot be modified otherwise:

.\psexec.exe -s -i powershell.exe

Then we need to get the Account Sid of the applicable object

get-aduser USERNAME | select sid   #Plenty of other ways to accomplish this

# Then we Get SDDL from existing task, to ensure that we maintain the proper owner and creator ID’s
$PathToTask = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\TASKYOUWISHTOALLOWTOBETRIGGERED"
$SDBin =  ( (get-itemProperty $PathToTask).SD )
# the $existingSDDL is the string we will actually be modifying (which is technically of the parent object that we are going to apply the new value to)
$existingSDDL = ([wmiclass]"Win32_SecurityDescriptorHelper").BinarySDToSDDL($SDBin).SDDL
# The result returned below is the interpretation of it in English
$secEnglish = ConvertFrom-SddlString ([wmiclass]"Win32_SecurityDescriptorHelper").BinarySDToSDDL($SDBin).SDDL
$sec.DiscretionaryAcl

# The $existingSDDL will look something akin to the $p1 value
$p1 = 'O:BAG:DUD:(A;ID;0x1f019f;;;BA)(A;ID;0x1f019f;;;SY)(A;ID;FA;;;BA)(A;;FR;;;S-1-5-21-0000000000-111111111-2222222222-3333)'
# We then need to add the appropriate DACL for the account/SID we want to grant permissions to and the FA (File Full Access) permission to the string
$p2 = 'O:BAG:DUD:(A;ID;0x1f019f;;;BA)(A;ID;0x1f019f;;;SY)(A;ID;FA;;;BA)(A;;FR;;;S-1-5-21-0000000000-111111111-2222222222-3333)(A;;FR;;;S-1-5-21-0000000000-111111111-2222222222-9999)'

# We then need to convert the DACL into a binary Value to be applied to the SD (REG_BINARY) ****Value****, not the security of the object but the actual value of the SD Key
$p2BinVal = ([wmiclass]"Win32_SecurityDescriptorHelper").SDDLToBinarySD($p2).BinarySD

#Once we have the proper Binary Value we can update the Value of the Key Set-ItemProperty -Path $PathToTask -Name SD -Value (byte[])

#If the key gets set incorrectly to a different data type along the way you can remove it an recreate remove-itemproperty -path $PathToTask -name "SD" New-ItemProperty -Path $PathToTask -Name SD -PropertyType Binary -Value ([byte[]]$p2BinVal)

DACL/SACL Notes: Header D: = DACL S: = SACL G: = Primary Group O: = Owner DACL & SACL are combination of ACEs enclosed in () 6 Fields ACE Type (allow/deny/audit), ACE flags (inheritance and audit settings), Permissions (list of incremental permissions), ObjectType (GUID), Inherited Object Type (GUID), and Trustee (SID) https://itconnect.uw.edu/wares/msinf/other-help/understanding-sddl-syntax/

0

The link from Vladimir seems to be the solution. Since links to Microsoft always disappear eventually I've copied the complete solution here. The powershell script comes from user MotoX80 on social.technet.microsoft.com.

MotoX80 wrote:

I was investigating this a while back and was unable to get file permission changes to work as you discovered.

This time I dug a little deeper and discovered the SD registry value. See the comments in the code. I have built a Powershell script that I named UnlockScheduledTask.ps1. This worked for me on Win10Pro. Just create a task and then unlock it.

Give it a try and see if it works for you.

<#

.SYNOPSIS
This Powershell script updates the security descriptor for scheduled tasks so that any user can run the task. 

Version 1.0 of this script only displays tasks in the root folder. I want to make sure that works first. 

.DESCRIPTION
Earlier versions of Windows apparently used file permissions on C:\Windows\System32\Tasks files to manage security.
Windows now uses the SD value on tasks under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree to accomplish that. 

By default, this script will display the SDDL on all tasks. If a taskname is passed as a parameter, this script will grant Authenticated users read and execute permissions to the task. 

This script accepts 1 parameters.
-taskname   The name of a scheduled task. 

.EXAMPLE
./UnlockScheduledTask.ps1 
./UnlockScheduledTask.ps1 -taskname "My task"  

.NOTES
Author: Dave K. aka MotoX80 on the MS Technet forums. (I do not profess to be an expert in anything. I do claim to be dangerous with everything.)



.LINK
http://www.google.com

#>

param (
    [string]$taskname = ""   
 )

 'UnlockScheduledTask.ps1  Version 1.0'
 if ($taskname -eq '') {
    ''
    'No task name specified.'
    'SDDL for all tasks will be displayed.'
    ''
 } else {
    $batFile = "$env:TEMP\Set-A-Task-Free.bat"           # if you don't like my names, you can change them here. 
    $updateTaskName = 'Set-A-Task-Free'
    ''
    "SDDL for $taskname will be updated via $batfile"
    ''
 }
 $wmisdh = new-object system.management.ManagementClass Win32_SecurityDescriptorHelper 
 $subkeys = Get-childitem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree"
 foreach ($key in $subkeys) {
    if ($taskname -eq '') {              # if blank, show SDDL for all tasks 
        ''
        $key.PSChildName
        $task = Get-ItemProperty $($key.name).replace("HKEY_LOCAL_MACHINE","HKLM:")
        $sddl = $wmisdh.BinarySDToSDDL( $task.SD ) 
        $sddl['SDDL']        
    
    } else {
        if ($key.PSChildName -eq $taskname) {
            ""
            $key.PSChildName
            $task = Get-ItemProperty $($key.name).replace("HKEY_LOCAL_MACHINE","HKLM:")
            $sddl = $wmisdh.BinarySDToSDDL( $task.SD ) 
            $sddl['SDDL']
            ''
            'New SDDL'
            $newSD = $sddl['SDDL'] +  '(A;ID;0x1301bf;;;AU)'          # add authenticated users read and execute
            $newSD                                                    # Note: cacls /s will display the SDDL for a file. 
            $newBin = $wmisdh.SDDLToBinarySD( $newsd )
            [string]$newBinStr =  $([System.BitConverter]::ToString($newBin['BinarySD'])).replace('-','') 
            
            # Administrators only have read permissions to the registry vlaue that needs to be updated.
            # We will create a bat file with a reg.exe command to set the new SD.
            # The bat file will be invoked by a scheduled task that runs as the system account.
            # The bat file can also be reused if the task is deployed to other machines. 
            ''
            "reg add ""HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\{0}"" /f /v SD /t REG_BINARY /d {1}" -f $key.PSChildName, $newBinStr
            "reg add ""HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\{0}"" /f /v SD /t REG_BINARY /d {1}" -f $key.PSChildName, $newBinStr  | out-file -Encoding ascii $batfile  
            ''

            SCHTASKS /Create /f /tn "$updateTaskName" /sc onstart  /tr "cmd.exe /c $batfile" /ru system 
            SCHTASKS /run /tn "$updateTaskName"
            $count = 0
            while ($count -lt 5) {
                start-sleep 5
                $count++
                $(Get-ScheduledTask -TaskName $updateTaskName).State
                if ($(Get-ScheduledTask -TaskName $updateTaskName).State -eq 'Ready') {
                    $count = 99            # it's ok to procees
                }
            }
            if ($count -ne 99) {
                "Error! The $updateTaskName task is still running. "
                'It should have ended by now.'
                'Please investigate.'
                return
            }
            SCHTASKS /delete /f /tn "$updateTaskName"
            ''
            'Security has been updated. Test it.'
        }
    }      
 }
richb
  • 141
  • 1
  • 5