Windows Server 2016 中的计划任务,由非管理员用户运行

n0r*_*lub 8 permissions scheduled-task user-permissions windows-server-2016 administrative-privileges

在较早的 Windows Server 版本(2016 年之前)中,可以通过执行以下步骤授予非管理员用户运行计划任务的权限:

  1. 定时任务:在系统下运行,执行脚本
  2. 授予用户对 C:\Windows\System32\Tasks\ 下特定任务的读取和执行权限

现在在服务器 2016 中,这不再起作用。你知道怎么做吗?

谢谢

相关帖子,没有得到回答,也没有帮助:允许非管理员用户在 Windows Server 2016 中运行计划任务

unN*_*med 1

在深入研究这个主题一段时间并尝试了一些建议之后,我想出了这个脚本,它需要来自 Microsoft 的 PsTools/SysInternals 的 PsExec 工具:

$task = "Test-Script"

$secdesc = new-object system.management.ManagementClass Win32_SecurityDescriptorHelper 
$regkeys = Get-childitem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree"
foreach ($key in $regkeys) {
    if ($key.PSChildName -eq $task) {
        
        
        $t = Get-ItemProperty $($key.name).replace("HKEY_LOCAL_MACHINE","HKLM:")
        $sddl = $secdesc.BinarySDToSDDL( $t.SD ) 
        $newSDDL = $sddl['SDDL'] +  '(A;ID;0x1301bf;;;AU)'
        $binSDDL = $secdesc.SDDLToBinarySD( $newSDDL )
        [string]$binSDDLStr =  $([System.BitConverter]::ToString($binSDDL['BinarySD'])).replace('-','') 
        
        "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, $binSDDLStr | out-file -Encoding ascii $PWD\temp_setreg.bat
        start-sleep 2
        .\PsExec.exe -accepteula -nobanner -s "$PWD\temp_setreg.bat"
        break
    }
}
Run Code Online (Sandbox Code Playgroud)

背景:显然,从 Windows 10(以及相应的 Windows Server 2016+)开始,安全设置不再从任务路径 (C:\Windows\system32\Tasks) 使用,而是存储在注册表中:

我找到了一个使用注册表值的脚本。它创建一个以系统身份运行的任务,以便您能够编辑权限(因为管理员也只有只读权限)。
我编辑了此脚本以缩短它并使用 PsExec 以 SYSTEM 用户身份编辑注册表权限。
放置PsExec.exe在与脚本相同的目录中,编辑第一行以保存任务的名称。从该目录运行脚本。

来自脚本作者:表示经过许可(A;ID;0x1301bf;;;AU)添加。 您可以使用 Windows 资源管理器的安全选项卡创建自己的权限条目,并从命令行以 SDDL 格式读取它:如果不是您当前的目录, 则替换为路径或文件。Authenticated Usersread and execute
Cacls . /S
.

这是原始脚本(存档):UnlockScheduledTask.ps1

<#

.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.'
        }
    }      
 }
Run Code Online (Sandbox Code Playgroud)