Sunday 1 September 2019

The Art of Becoming TrustedInstaller - Task Scheduler Edition

2 years ago I wrote a post running a process in the TrustedInstaller group. It was pretty well received, and as others pointed out there's many way of doing the same thing. However in my travels I came across a new way I've not seen documented before, though I'm sure someone will point out where I've missed documentation. As with the previous post, this does require admin privileges, it's not a privilege escalation. Also I tested the behavior I'm documented on Windows 10 1903. Your mileage may vary on different versions of Windows.

It revolves around the Task Scheduler (obvious by the title I guess), specifically calling the IRegisteredTask::RunEx method exposed by the Task Scheduler COM API. The prototype of RunEx is as follows:

HRESULT RunEx(
  VARIANT      params,
  LONG         flags,
  LONG         sessionID,
  BSTR         user,
  IRunningTask **ppRunningTask
);

The thing we're going to use is the user parameter, which is documented as "The user for which the task runs." Cheers Microsoft! Through a bit of trial and error, and some reverse engineering it's clear the user parameter can take three types of string values:

  1. A normal user account. This can be the name or a SID. The user must be logged on at the time of starting the task as far as I can tell.
  2. The standard system accounts, i.e. SYSTEM, LocalService or NetworkService.
  3. A service account!
Number 3 is the one we're interested in here, it allows you to specify an installed service account, such as TrustedInstaller and the task will run as SYSTEM with the service SID included. Let's try it out.

The advantage of using the user parameter is the task can be registered to run as a normal user, and we'll change it at run time to be more sneaky. In theory you could directly register the task to run as TrustedInstaller, but then it'd be more obvious if anyone went looking. First we need to create a scheduled task, run the following script in PowerShell to create a simple task which will run notepad.

$a = New-ScheduledTaskAction -Execute notepad.exe
Register-ScheduledTask -TaskName 'TestTask' -Action $a

Now we need to call RunEx. While PowerShell has a Start-ScheduledTask cmdlet neither it, or the schtasks.exe /Run command allows you to specify the user parameter (aside, the /U parameter for schtask does not do what you might think). Instead as the COM API is scriptable we can just run some PowerShell again and use the COM API directly.

$svc = New-Object -ComObject 'Schedule.Service'
$svc.Connect()

$user = 'NT SERVICE\TrustedInstaller'
$folder = $svc.GetFolder('\')
$task = $folder.GetTask('TestTask')
$task.RunEx($null, 0, 0, $user)

After executing this script you should find a copy of notepad running as SYSTEM with with the TrustedInstaller group in the access token.


Enjoy responsibly.