Windows Unprivileged - Scheduled Tasks JEA
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!
As partly explained in the post on Scheduled Tasks, setting permissions on scheduled tasks is not trivial.
So what is more obvious than avoiding those permissions using Just Enough Admin again?
The immediate problem is that allowing non-privileged users to create and modify scheduled tasks using a virtual administrator account will allow those users to create scheduled tasks running as LocalSystem that will then do whatever those users want, perhaps just add them to the Administrators group.
Any JEA configuration for scheduled tasks would have to disallow creation or modification of scheduled tasks that use privileged accounts. Perhaps it is sufficient to stop creation of scheduled tasks that run as a system account (LocalSystem, LocalService, or NetworkService). Scheduled tasks to be modified can then simply be recreated, then being subject to the same restriction.
This requires two cmdlets to be available:
- Get-ScheduledTask to get any scheduled task as a PowerShell object (so that it can then be modified in PowerShell)
- Register-RestrictedScheduledTask as a wrapper for Register-ScheduledTask that makes the -Password parameter a required parameter (to disallow creation of scheduled tasks that use accounts without passwords)
As a bonus, another cmdlet can annex and permission a scheduled task for the calling user, to allow manipulation in the Task Scheduler GUI (taskschd.msc) as a non-privileged user.
-
Claim-ScheduledTask changes the owner of a scheduled task to the calling user and adds the user to the scheduled task’s ACL with Full Access before any other entry (to preempt deny entries as well, also, it’s easier).
-
Get-ScheduledTaskPermissions shows the permissions of a scheduled task. Set-ScheduledTaskPermissions sets the permissions.
And as a further bonus, the ability simply to enable or disable any scheduled task is included.
- Enable-ScheduledTask and Disable-ScheduledTask
Get ScheduledTasks.pssc and ScheduledTasts.psrc from GitHub, register ScheduledTasks.pssc, and create the associated JEA group:
PS C:\Program Files\WindowsPowerShell\Modules\JEA> New-LocalGroup JEA_ScheduledTasks
Name Description
---- -----------
JEA_ScheduledTasks
PS C:\Program Files\WindowsPowerShell\Modules\JEA> Add-LocalGroupMember JEA_ScheduledTasks benoit
PS C:\Program Files\WindowsPowerShell\Modules\JEA> Register-PSSessionConfiguration ScheduledTasks -Path .\ScheduledTasks.pssc
WARNING: Register-PSSessionConfiguration may need to restart the WinRM service if a configuration using this name has recently been unregistered, certain system data structures may still be cached. In that case, a restart of WinRM may be required.
All WinRM sessions connected to Windows PowerShell session configurations, such as Microsoft.PowerShell and session configurations that are created with the Register-PSSessionConfiguration cmdlet, are disconnected.
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Plugin
Type Keys Name
---- ---- ----
Container {Name=ScheduledTasks} ScheduledTasks
WARNING: Set-PSSessionConfiguration may need to restart the WinRM service if a configuration using this name has recently been unregistered, certain system data structures may still be cached. In that case, a restart of WinRM may be required.
All WinRM sessions connected to Windows PowerShell session configurations, such as Microsoft.PowerShell and session configurations that are created with the Register-PSSessionConfiguration cmdlet, are disconnected.
WARNING: Register-PSSessionConfiguration may need to restart the WinRM service if a configuration using this name has recently been unregistered, certain system data structures may still be cached. In that case, a restart of WinRM may be required.
All WinRM sessions connected to Windows PowerShell session configurations, such as Microsoft.PowerShell and session configurations that are created with the Register-PSSessionConfiguration cmdlet, are disconnected.
PS C:\Program Files\WindowsPowerShell\Modules\JEA>Note that this configuration gives access to two cmdlets Get-ScheduledTask and Get-Help. The latter is included to allow showing help for the two exposed functions.
It also gives access to three functions Register-RestrictedScheduledTask, Get-ScheduledTaskPermissions, and Claim-ScheduledTask. (The latter cmdlet might need a cooler name.)
Register-RestrictedScheduledTask is simple: it just calls Register-ScheduledTask but will insist that the -Password parameter exists and that something is given as argument for it. (See Parameters and Arguments. It’s never quite clear to me.) Since system accounts don’t take a password, the user cannot register a scheduled task that runs as a system account. (The user can still register a scheduled task that runs as another privileged account, like a member of the Administrators group, but if the user knows such an account, then why does he bother using this JEA configuration?)
Get-ScheduledTaskPermissions uses a COM (Component Object Model) API to read scheduled task permissions and gets an SDDL (Security Descriptor Definition Language) representation of the scheduled task’s security descriptor which it will then produly display. Set-ScheduledTaskPermissions uses the same API to set scheduled task permissions.
Claim-ScheduledTask is a bit pseudo-clever. It also uses the COM API to modify scheduled task permissions . It gets an SDDL representation of the scheduled task’s security descriptor and modifies it by a) replacing the owner and b) inserting an entry at the beginning of the access control list giving the new owner full access. Then it writes the SDDL back into the scheduled task COM object.
Note that you understood correctly: This does allow the user to annex any scheduled task he wants. (Modify the cmdlet code if you want to prevent this.) But he cannot change what it does and and keep it running under an account of which he does not know the password. So, yes, the user can cause damage with this cmdlet. Deploy with care!
Make sure your user is in both the Remote Management Users and JEA_ScheduledTasks groups and:
PS C:\> whoami
achaemenes\benoit
PS C:\> whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ==================================== ========
SeShutdownPrivilege Shut down the system Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeUndockPrivilege Remove computer from docking station Disabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
SeTimeZonePrivilege Change the time zone Disabled
PS C:\> $scheduledtasks=New-PSSession -ConfigurationName ScheduledTasks
PS C:\> $action=New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c cls"
PS C:\> $task=New-ScheduledTask -Action $action -Settings (New-ScheduledTaskSettingsSet)
PS C:\> Invoke-Command $scheduledtasks {param($task); Register-RestrictedScheduledTask -TaskName BenoitTask -User benoit -InputObject $task} -ArgumentList $task
cmdlet Register-RestrictedScheduledTask at command pipeline position 1
Supply values for the following parameters:
Password: BenoitsSecretPassword
TaskPath TaskName State PSComputerName
-------- -------- ----- --------------
\ BenoitTask Ready localhost
PS C:\> Invoke-Command $scheduledtasks {Get-ScheduledTask -TaskName BenoitTask}
TaskPath TaskName State PSComputerName
-------- -------- ----- --------------
\ BenoitTask Ready localhost
PS C:\> Invoke-Command $scheduledtasks {Get-ScheduledTaskPermissions -TaskName BenoitTask}
O:S-1-5-94-3G:S-1-5-94-3D:(A;ID;0x1f019f;;;BA)(A;ID;0x1f019f;;;SY)(A;ID;FA;;;S-1-5-94-3)(A;;FR;;;S-1-5-21-2059049455-1877585131-3415813230-1007)
PS C:\> Invoke-Command $scheduledtasks {Claim-ScheduledTask -TaskName BenoitTask}
Writing security descriptor [O:S-1-5-21-2059049455-1877585131-3415813230-1007G:S-1-5-94-3D:(A;;FA;;;S-1-5-21-2059049455-1877585131-3415813230-1007)(A;ID;0x1f019f;;;BA)(A;ID;0x1f019f;;;SY)(A;ID;FA;;;S-1-5-94-3)(A;;FR;;;S-1-5-21-2059049455-1877585131-3415813230-1007)] into scheduled task [BenoitTask]. Press ctrl+c to cancel.:
PS C:\> Invoke-Command $scheduledtasks {Get-ScheduledTaskPermissions -TaskName BenoitTask}
O:S-1-5-21-2059049455-1877585131-3415813230-1007G:S-1-5-94-3D:AI(A;;FA;;;S-1-5-21-2059049455-1877585131-3415813230-1007)(A;;FR;;;S-1-5-21-2059049455-1877585131-3415813230-1007)(A;ID;0x1f019f;;;BA)(A;ID;0x1f019f;;;SY)(A;ID;FA;;;S-1-5-94-3)
PS C:\>Observe how user benoit can register a scheduled task. With the the -Force parameter set, it will also overwrite an existing scheduled task.
To modify a task, benoit can Get-ScheduledTask it, modify the task object and then Register-RestrictedScheduledTask -Force it:
PS C:\> $task1=Invoke-Command $scheduledtasks {Get-ScheduledTask -TaskName BenoitTask}
PS C:\> $task1
TaskPath TaskName State PSComputerName
-------- -------- ----- --------------
\ BenoitTask Ready localhost
PS C:\> $task1.Actions
Id :
Arguments : /c cls
Execute : cmd.exe
WorkingDirectory :
PSComputerName :
PS C:\> $action=New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c echo foo"
PS C:\> $task1.Actions=$action
TaskPath TaskName State PSComputerName
-------- -------- ----- --------------
\ BenoitTask Ready localhost
PS C:\> $task1.Actions
Id :
Arguments : /c echo foo
Execute : cmd.exe
WorkingDirectory :
PSComputerName :
PS C:\> Invoke-Command $scheduledtasks {param($task); Register-RestrictedScheduledTask -TaskName BenoitTask -User benoit -Password BenoitsSecretPassword -InputObject $task -Force} -ArgumentList $task
TaskPath TaskName State PSComputerName
-------- -------- ----- --------------
\ BenoitTask Ready localhost
PS C:\>The task now does a “cmd.exe /c echo foo” instead of the original “cmd.exe /c cls”. (But it is running as the account given to it when registering it after modification.)
And if user benoit needs to have access to this (or any other) scheduled task in the Task Scheduler GUI (or with PowerShell but without JEA) Claim-ScheduledTask annexes the scheduled task to him and grants him access.
Note again 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 is wrong or dangerous or will lead to disaster or confusion. I am not taking responsibility here for anything.
Happy new year, everyone!
P.S.: In case you ever wondered. The “Run with highest privileges” checkbox in Task Scheduler does nothing to non-interactive scheduled tasks (batch jobs). What it does do is affect interactive scheduled tasks. It only becomes relevant when “Run only when user is logged on” is selected (instead of “Run whether user is logged on or not”). It acts as a preemptive OK to a UAC prompt that might otherwise prevent the interactive process from running elevated. I don’t know where this is properly documented.
Next: Windows Sudo