NanoCore: Scheduled Task Privilege Escalation — Detection
Run once with admin rights, NanoCore registers a scheduled task configured to run at the highest available privilege. From then on it can regain admin at will — and across reboots — by simply running that task, with no further prompt.
MITRE ATT&CK: T1053.005
Technique write-up: NanoCore_PrivEscalation
Detection Indicators
| Indicator | Value |
|---|---|
| Registration | schtasks.exe /create /f /tn "NTFS Manager" /xml <temp file> |
| Task name | masquerades as a Windows component — NTFS Manager here, randomised in the wild |
| Task XML | <RunLevel>HighestAvailable</RunLevel>, <LogonType>InteractiveToken</LogonType> |
| Task XML | <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>, MultipleInstancesPolicy Parallel, no <Triggers> |
| Action | %APPDATA%\<GUID>\NTFS Manager\ntfsmgr.exe |
| Re-elevation | schtasks.exe /run /tn "NTFS Manager" |
A task with HighestAvailable, no triggers, and an action under %APPDATA% is the shape
to hunt. No triggers means it exists purely to be run on demand — the opposite of what a
scheduled task is for.
Log Sources
- Microsoft Defender XDR (
DeviceProcessEvents) - Sysmon EID 1 (process create)
- Windows Security 4688 with command line inclusion
- Microsoft-Windows-TaskScheduler/Operational EID 106, 140, 200
- Windows Security 4698 (scheduled task created), 4702 (updated)
Detection Engineering with Defender XDR
Task registered from an XML file, and the later on-demand run.
// Registration and re-elevation, in one query
DeviceProcessEvents
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has_any ("/create", "/run")
| extend stage = case(ProcessCommandLine has "/create", "register",
ProcessCommandLine has "/run", "elevate", "other")
| where ProcessCommandLine has "/xml" or stage == "elevate"
| project Timestamp, DeviceName, AccountName, stage, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessFolderPath, ProcessIntegrityLevel
| order by Timestamp desc
Detection Engineering with Sysmon
The task action path is the durable half — the binary lives under AppData.
WindowsEvent //(sysmon)
| where EventID == 1
| extend CommandLine = tostring(EventData.CommandLine), Image = tostring(EventData.Image)
| where Image endswith "\schtasks.exe"
| where CommandLine has "/create" and CommandLine has "/xml"
| project TimeGenerated, Computer, Image, CommandLine,
ParentImage = tostring(EventData.ParentImage)
Detection Engineering with SIGMA
Full rule: sigma_nanocore_schtask_privesc.yml
detection:
selection_img:
Image|endswith: '\schtasks.exe'
selection_create:
CommandLine|contains|all:
- '/create'
- '/xml'
condition: all of selection_*
Notes and tuning
Registering a task from an XML file is the narrowing condition. Most legitimate task
creation on endpoints goes through the GUI or through /tn plus /tr inline, not /xml
pointing at a file in %TEMP%.
The stronger, tool-agnostic hunt is on the task definition itself: Security 4698 carries the
full XML, so alert on HighestAvailable combined with an action path under AppData and an
empty <Triggers> element. That survives the attacker dropping schtasks.exe for the
Task Scheduler COM API.
Disclaimer
For educational purposes only!!!