作者:REInject@73lab@青藤实验室
原文链接:https://mp.weixin.qq.com/s/ktGug1VbSpmzh9CEGKbbdw
恶意软件或攻击者通常使用计划任务作为他们的持久化机制。从风险发现的角度考虑,理解计划任务的运行和创建方式以及和计划任务相关联的进程是有必要的。此外,本文还分享了一种未公开的计划任务隐藏方式。
现在,让我们开始了解计划任务的一切。
MSDN 里对计划任务的细节信息描述得很详细了,包括使用的API或工作机制。所以不再重复阐述,只引用一些必要的东西:
计划任务调度器会根据任务的定义在指定的时间触发任务,它包含以下组件:
更多信息可以在 MSDN 找到。
命令行创建计划任务包括 at.exe 和 schtasks.exe。
使用 at 命令创建计划任务的方式:
at 11:11 /every:Sunday,Monday,Tuesday "malware.exe"
以上命令将创建一个计划任务,在每周日、周一、周二的 11:11 执行 malware.exe。
也可以通过 \\ComputerName
在指定计算机上运行,更多的参数信息参考 MSDN。
下面这些信息可能会帮助排查 at.exe 生成的计划任务:
%SystemRoot%\System32\at.exe
%SystemRoot%\Tasks
,关注 At[x].job 文件,x 代表任务的 ID%SystemRoot%\System32\Tasks
应用程序和服务日志/Microsoft/Windows/TaskScheduler/Operational
事件日志at.exe 在 windows8 开始就弃用了,之后的系统都是使用 schtasks.exe 创建计划任务,该命令参数信息如下:
SCHTASKS /Create [/S system [/U username [/P [password]]]] [/RU username [/RP password]] /SC schedule [/MO modifier] [/D day] [/M months] [/I idletime] /TN taskname /TR taskrun [/ST starttime] [/RI interval] [ {/ET endtime | /DU duration} [/K] [/XML xmlfile] [/V1]] [/SD startdate] [/ED enddate] [/IT | /NP] [/Z] [/F] [/HRESULT] [/?]
schtasks 比 at 更加强大,提供了很多自定义任务时需要的参数。在对计划任务排查时,我们可能更关注的是执行的任务内容,和它相关的参数是 TR
。一般情况创建恶意计划任务的命令大概是这样子:
"c:\Windows\System32\schtasks.exe" /Create /SC ONCE /TN KglN9I99 /TR "cmd /c \"start /min C:\ProgramData\KglN9I99.bat\"" /ST 20:21
这个命令中使用了 /TN
指定任务名称为 KglN9I99,/TR
参数指定运行的恶意命令,/ST
指定了运行时间,/SC
指定运行周期,还可以通过 /ED
参数指定任务终止日期等。
更多关于 schtasks.exe 的用法,参考 MSDN
下面这些信息可能会帮助排查 schtasks.exe 生成的计划任务:
%SystemRoot%\System32\schtasks.exe
/TR
参数的值,可执行文件或命令是否是恶意的%SystemRoot%\System32\Tasks
应用程序和服务日志/Microsoft/Windows/TaskScheduler/Operational
事件日志一旦任务创建,将会自动在目录 %SystemRoot%\System32\Tasks
生成一个关于该任务的描述性 XML 文件,包含了所有的任务信息。
win+r 启动 taskschd.msc
mmc 程序启动后会直接提权到管理员权限,所以普通用户可以创建高权限的任务:
选中 Task Scheduler Library ,右键 Create Task...,在弹出界面,逐个配置即可:
需要注意的是,通过 taskschd.msc
创建的任务会直接从托管 Task Scheduler 服务的 svchost.exe 进程派生。
代码最终都是和 c:\windows\system32\taskschd.dll
提供的 COM 服务交互,它的 GUID 是 0F87369F-A4E5-4CFC-BD3E-73E6154572DD
。
注册表路径:
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0f87369f-a4e5-4cfc-bd3e-73e6154572dd}
该 COM 组件不支持 Elevation,无法自动提权。
下面是2种创建计划任务的代码示例。
c# 实现的话,为了方便,引用 TaskScheduler 项目进行计划任务的创建:
using System; using System.Security.Principal; using System.Security.AccessControl; using Microsoft.Win32.TaskScheduler; using System.Text.RegularExpressions; namespace SchtaskHidden { class Program { static void Main(string[] args) { //TaskCollection tt = TaskService.Instance.RootFolder.GetTasks(new Regex("test")); //foreach(Task ti in tt) //{ // Console.WriteLine(ti.Name); //} //System.Environment.Exit(0); TaskDefinition td = TaskService.Instance.NewTask(); td.RegistrationInfo.Description = "do something"; //td.Principal.RunLevel = TaskRunLevel.Highest; //td.Principal.LogonType = TaskLogonType.ServiceAccount; //td.Principal.UserId = "SYSTEM"; TimeTrigger dt = new TimeTrigger(); dt.StartBoundary = DateTime.Now; dt.Repetition.Interval = TimeSpan.FromMinutes(1); td.Triggers.Add(dt); td.Actions.Add("cmd.exe", "/c \"calc.exe\"", null); Task t = TaskService.Instance.RootFolder.RegisterTaskDefinition(path: "testxxx", definition: td, TaskCreation.CreateOrUpdate, null, null, 0); Console.WriteLine("success!!"); //TaskSecurity ts = new TaskSecurity(t); //ts.RemoveAccessRuleAll(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.ServiceSid, null), TaskRights.Read | TaskRights.Write | TaskRights.ReadAttributes, AccessControlType.Allow)); //t.SetAccessControl(ts); //Console.WriteLine("success!!"); } } }
$TaskDescr = "test task" $Author = "thin0" $TaskName = "test" $TaskStartTime = [datetime]::Now $TaskCommand = "cmd.exe" $TaskArg = "/c calc.exe" $UserAcct = "$env:userdomain\$env:username" # $UserAcct = "SYSTEM" $ScheduleObject = new-object -ComObject("Schedule.Service") # connect to the local machine. $ScheduleObject.Connect("localhost") $rootFolder = $ScheduleObject.GetFolder("\") $TaskDefinition = $ScheduleObject.NewTask(0) $TaskDefinition.RegistrationInfo.Description = "$TaskDescr" $TaskDefinition.RegistrationInfo.Author = "$Author" #$TaskDefinition.Principal.RunLevel = 1 $TaskDefinition.Settings.Enabled = $true $TaskDefinition.Settings.AllowDemandStart = $true $TaskDefinition.Settings.DisallowStartIfOnBatteries = $false $TaskDefinition.Settings.ExecutionTimeLimit = "PT0S" # See Note Below $triggers = $TaskDefinition.Triggers $trigger = $triggers.Create(1) # Creates a "time-based" trigger, 8: system startup $trigger.StartBoundary = $TaskStartTime.ToString("yyyy-MM-dd'T'HH:mm:ss") $trigger.Repetition.Interval = 1 $trigger.Enabled = $true $Action = $TaskDefinition.Actions.Create(0) $action.Path = "$TaskCommand" $action.Arguments = "$TaskArg" $rootFolder.RegisterTaskDefinition($TaskName,$TaskDefinition,6,$UserAcct,$null,3)
powershell 可以不用这么麻烦,有自带的 cmdlet:
$taskname = "test" $cmd = "cmd.exe" $cmdargs = "/c calc.exe" $username = "$env:username" #$username = "SYSTEM" $taskdescription = "test task" $action = New-ScheduledTaskAction -Execute $cmd -Argument $cmdargs $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -minutes 1) $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 0) -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $taskname -Description $taskdescription -Settings $settings -User $username -RunLevel 1 # 1 for highest, 0 for low
删除命令:Unregister-ScheduledTask -TaskName test -Confirm:$false
运行计划任务的相关服务是:Task Scheduler。该服务使用 svchost.exe 的 netsvcs 组进行托管。
通过进程树看到 svchost.exe 进程的命令行为
svchost.exe -k netsvcs -p -s Schedule
在 Windows 10.1703以下可能看不到 -s Schedule
参数。
在旧系统中计划任务的进程派生顺序是这样子的: svchost.exe -> taskeng.exe -> [SpecialTaskProcess]
例如:
taskeng.exe 的进程参数语法如下:
taskeng.exe {GUID} [User SID]:[Domain]\[User Name]:[Options]
某些情况下,可能只有 {GUID} 一个参数,Options 参数可能会标识一些其他信息,例如权限信息,如果一个任务运行在高权限模式下,Options 的内容会是:Interactive:Highest[1]
所以在旧的系统中可以查看进程树排查恶意进程,例如找到 taskeng.exe 的进程树,它的父进程为svchost.exe -k netsvcs
。
子进程就是运行中的任务对应的进程,通过排查子进程确定恶意进程。
win7之后,计划任务托管程序从 taskeng.exe 慢慢迁移到 svchost.exe,这期间计划任务进程派生可能有两种顺序:
从 Windows 10.1511 版本开始,不再有 taskeng.exe 了,新版本系统中找不到该程序,任务进程直接运行在托管 Task Scheduler 服务的 svchost.exe 进程下:
所以高版本系统中,我们可以通过
排查进程的子进程来确定恶意或可疑程序。
在上面的图里,可以很明显注意到
下还有一个名为 taskhostw.exe 的进程。
在 Windows 7 上该进程名为:taskhost.exe。
在 Windows 8 上该进程名为:taskhostex.exe。
该进程的作用同 dllhost.exe 和 svchost.exe 相似,起到一个 DLL 托管的作用。
通过搜索 %SystemRoot%\System32\Tasks
文件夹,我们能发现一些任务对应的动作不是 Exec,而是 ComHandler。
我们找到一些能起到对比效果的任务 XML,隐藏了一些不必要的字段。
这是我们利用 schtasks.exe 命令创建的任务 XML:
<?xml version="1.0" encoding="UTF-16"?> <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> <Actions Context="Author"> <Exec> <Command>"C:\Program Files (x86)\IObit\Advanced SystemCare\ASC.exe"</Command> <Arguments>/SkipUac</Arguments> </Exec> </Actions> </Task>
这个是系统 .NET Framework 的计划任务 XML 内容,如果安装了 .NET 框架可以在
%SystemRoot%\System32\Tasks\Microsoft\Windows\.NET Framework
目录下找到:
<?xml version="1.0" encoding="UTF-16"?> <Task version="1.6" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> <Actions Context="Author"> <ComHandler> <ClassId>{84F0FAE1-C27B-4F6F-807B-28CF6F96287D}</ClassId> <Data><![CDATA[/RuntimeWide]]></Data> </ComHandler> </Actions> </Task>
手动触发 .NET Framework 任务,该任务调用 ngentasklauncher.dll ,通过 ProcExp.exe 监控进程观察到的进程树:
可以注意到 taskhostw.exe 的参数 /RuntimeWide
和 xml 中 \<Data\>
标签指定的一样。
在观察 taskhostw.exe 进程树的过程中,发现下面的命令行参数:
taskhosw.exe Install $(Arg0)
这个东西在 MSDN 中做了介绍:
由此可知,$(Arg0)
这个参数是在通过 IRegisteredTask::Run[Ex]
接口运行任务时动态指定的。在一些 Windows 默认的任务中常见:
\Microsoft\Windows\Workplace Join\Automatic-Device-Join 任务:
<?xml version="1.0" encoding="UTF-16"?> <Task xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> <Actions Context="LocalSystem"> <Exec> <Command>%SystemRoot%\System32\dsregcmd.exe</Command> <Arguments>$(Arg0) $(Arg1) $(Arg2)</Arguments> </Exec> </Actions> </Task>
\Microsoft\Windows\Maps\MapsToastTask 任务:
<?xml version="1.0" encoding="UTF-16"?> <Task xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> <Actions Context="Users"> <ComHandler> <ClassId>{9885AEF2-BD9F-41E0-B15E-B3141395E803}</ClassId> <Data><![CDATA[$(Arg0);$(Arg1);$(Arg2);$(Arg3);$(Arg4);$(Arg5);$(Arg6);$(Arg7)]]></Data> </ComHandler> </Actions> </Task>
查阅 MSDN 可知,该参数可以通过以下 API 进行传递:
在一次应急排查中,只能从计划任务日志中看到恶意计划任务在周期性的执行,但却无法通过 taskschd.msc 或 schtasks 查询到恶意任务,并且通过排查 %SystemRoot%\System32\Tasks
目录后仍无法找到它。
在测试中发现,创建计划任务 test 后,无论是手动修改任务 xml 文件,还是删除任务 xml 文件,都无法影响该任务的运行。于是对注册表项进行监控,发现在创建任务后,下面的注册表项发生变化:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Schedule
下面是从 winreg-kb 项目中得到该注册表对应的信息:
在 XP 时,计划任务注册表路径为
HKEY_LOCAL_MACHINE\Software\Microsoft\SchedulingAgent
Win7 以后发生变化,变成
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Schedule
子项有:
名称 | 描述 |
---|---|
Aliases | 存储AtServiceAccount,默认NT AUTHORITY\System |
CompatibilityAdapter | |
Configuration | |
CredWom | |
Handlers | |
Handshake | |
TaskCache | 存储任务项信息 |
任务项信息除了在磁盘中的 %SystemRoot%\System32\Tasks
下之外,还在下面的注册表项中存在:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache
Schedule\TaskCache :
名称 | 描述 |
---|---|
Boot | |
Logon | |
Plain | |
Plain | |
Tree |
TaskCache\Tree 子项以任务名称命名,每个任务下的 Value 结构如下:
名称 | 类型 | 描述 |
---|---|---|
Id | REG_SZ | {GUID},任务对应的guid编号 |
Index | REG_DWORD | 一般任务为3,其他值未知 |
SD | REG_BINARY | 该任务项的安全描述信息,二进制值,结构未知 |
每个 Schedule\TaskCache\Tasks\%GUID% 对应一个任务,有这些 Value :
名称 | 类型 | 描述 |
---|---|---|
Actions | REG_BINARY | 二进制值,动作信息,中间包含 UNICODE 形式 COMMAND 信息 |
Date | REG_SZ | 任务创建日期? |
Description | REG_SZ | 任务描述 |
DynamicInfo | REG_BINARY | Win7 以下 28 位,Win8 以上 32 位 |
Hash | REG_BINARY | SHA-256 or CRC32, 疑似对应 xml 文件 Hash |
Path | REG_SZ | 在 TaskCache\Tree 中的任务路径 |
Schema | REG_DWORD | |
Triggers | REG_BINARY | 二进制,触发器信息 |
URI | REG_SZ | 任务路径 |
如果是 at 命令创建的计划任务,对应的注册表位置在
Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\At1
计划任务的 SD 配置在注册表中的位置:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\{TaskName}\SD
为人类不可读的二进制格式:
在一篇关于隐藏windows服务的文章中,了解到通过修改对象的安全描述信息可以达到隐藏的目的。触类旁通一下,计划任务的隐藏应该也是可以通过改变安全描述信息(SD)实现。
Windows 自带的工具 schtasks.exe 并不支持 sd 的设置,需要通过 API 实现。通过查阅 MSDN 并未发现对于 TASK 的 SDDL 该怎么写,相关 API 列表: - IRegistrationInfo::put_SecurityDescriptor - ITaskFolder::CreateFolder - ITaskFolder::CreateFolder - ITaskFolder::RegisterTaskDefinition - ITaskFolder::SetSecurityDescriptor - IRegisteredTask::SetSecurityDescriptor
通过这些 API 可以实现设置 TASK 的 SD 信息。我尝试使用项目 TaskScheduler 进行任务的创建,它用起来会更方便一些:
using System; using System.Security.Principal; using System.Security.AccessControl; using Microsoft.Win32.TaskScheduler; namespace SchTaskOpt { class Program { static void Main(string[] args) { TaskDefinition td = TaskService.Instance.NewTask(); td.RegistrationInfo.Description = "do something"; td.Principal.RunLevel = TaskRunLevel.Highest; td.Principal.LogonType = TaskLogonType.ServiceAccount; td.Principal.UserId = "SYSTEM"; td.RegistrationInfo.SecurityDescriptorSddlForm = @"D:P(D;;DCLCWPDTSD;;;IU)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCLCSWRPWPDTLOCRRC;;;SY)"; DailyTrigger dt = new DailyTrigger(); dt.StartBoundary = DateTime.Now; dt.DaysInterval = 1; dt.Repetition.Interval = TimeSpan.FromMinutes(1); //td.Triggers.Add(dt); td.Actions.Add("notepad", null, null); Task t = TaskService.Instance.RootFolder.RegisterTaskDefinition(path:"test2", definition:td, TaskCreation.CreateOrUpdate, null, null, TaskLogonType.ServiceAccount); Console.WriteLine("success!!"); //TaskSecurity ts = new TaskSecurity(t); //ts.AddAccessRule(new TaskAccessRule(new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), TaskRights.Read | TaskRights.Write | TaskRights.ReadAttributes, AccessControlType.Deny)); //t.SetAccessControl(ts); Console.WriteLine("success!!"); } } }
但不幸的是报错了,无论是通过管理员运行还是 SYSTEM,都无法另其正常运作:
PS C:\source\SchTaskOpt\bin\> .\SchTaskOpt.exe 未经处理的异常: System.Runtime.InteropServices.COMException: 异常来自 HRESULT:0xD0000061 在 Microsoft.Win32.TaskScheduler.V2Interop.ITaskFolder.RegisterTaskDefinition(String Path, ITaskDefinition pDefinition, Int32 flags, Object UserId, Object password, TaskLogonType LogonType, Object sddl) 在 Microsoft.Win32.TaskScheduler.TaskFolder.RegisterTaskDefinition(String path, TaskDefinition definition, TaskCreation createType, String userId, String password, TaskLogonType logonType, String sddl)
这个疑问先保留一下吧,需要清楚知道注册表中每个任务自动生成的 SD 格式才有头绪,这份类型结构清点也许会排上用场。
臆想一下,如果知道注册表里的所有结构形式及其意义,就可以手动通过添加注册表的方式进行创建计划任务,实际对该注册表项了解的并不是很清楚,所以只能做一些计划任务隐藏删除排查的工作。
我对计划任务隐藏的手法进行深入探究,经过不断测试,发现两种隐藏任务的方式。
如果想要隐藏一个计划任务,可以通过修改 Schedule\TaskCache\Tree 中对应任务的 Index 值,一般情况下都是 3,步骤如下:
psexec64 -i -s cmd.exe
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree
下对应任务的 Index 值为 0
%SystemRoot%\System32\Tasks
下任务对应的 XML 文件优点: - 利用 taskschd.msc、schtasks 甚至系统API查询出的所有任务中,都看不到该任务
缺点:
- 并非完全隐藏,如果知道该任务的名字,可以通过 schtasks /query /tn {TaskName}
查到
- 无论是低权的任务还是高权,都需要 SYSTEM 权限(在win10测试,低版本好像没这个要求,待测试)
PS C:\> schtasks.exe /create /tn test /tr "calc.exe" /sc minute /mo 1 /ru "administrator" 成功: 成功创建计划任务 "test"。 PS C:\> schtasks.exe /query /tn test 文件夹: \ 任务名 下次运行时间 模式 ======================================== ====================== =============== test 2021/1/11 14:54:00 就绪 PS C:\> schtasks.exe /query|findstr test test 2021/1/11 14:55:00 ?? PS C:\> Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\test" -Name "Index" -Value 0 PS C:\> schtasks.exe /query|findstr test PS C:\> schtasks.exe /delete /tn test /f 成功: 计划的任务 "test" 被成功删除。 PS C:\>
Index 的含义未知,这个暂时不做探讨。
按道理,是可以通过配置任务 SD 实现。在一次测试过程中,偶然删除了注册表中的 SD 项,发现无论什么方式都查不到任务信息,达到完全隐藏的目的:
psexec64 -i -s cmd.exe
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\{TaskName}\SD
%SystemRoot%\System32\Tasks
下任务对应的 XML 文件优点: - 无论何种方式(除了注册表),都查不到该任务,较为彻底
缺点: - 无论是低权的任务还是高权,都需要 SYSTEM 权限(在win10测试,低版本好像没这个要求,待测试)
下面是测试用的 powershell 代码(不通用,里面涉及的二进制内容需要先dump一个正常创建的任务):
$taskname = "test" $uuid = "{3EC79FBB-0533-4356-89B3-8CE2003F1CD8}" $cmd = "calc.exe" New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\" -Name $uuid New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$uuid\" -Name "Path" -Value "\$taskname" -Type String -Force New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$uuid\" -Name "URI" -Value "\$taskname" -Type String -Force New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$uuid\" -Name "Schema" -Value 0x00010002 -Type DWORD -Force $triggerstring = "FwAAAAAAAAABBwEAAAAIAAAI/NDz5dYBAAcBAAAACAD//////////zghQUNISEhI38PL80hISEgOAAAASEhISEEAdQB0AGgAbwByAAAASEgAAAAASEhISABISEhISEhIAEhISEhISEgBAAAASEhISBwAAABISEhIAQUAAAAAAAUVAAAAEXy5KUkCH0AHyc8n6AMAAEhISEgsAAAASEhISFQARQBDAEgATABJAFUAMQAwADUANwBcAHQAZQBjAGgAbABpAHUAAAAAAAAASEhISCwAAABISEhIWAIAABAOAACA9AMA/////wcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABISEhI3d0AAAAAAAABBwEAAAAIAAAI/NDz5dYBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAAAP////8AAAAAAAAAAAAAAAAAAcVLAQAAAAAAAAB2oQAAAAAAAEhISEg=" $triggerbytes = [System.Convert]::FromBase64String($triggerstring) $actionbytes = [byte[]](0x03,0x00,0x0c,0x00,0x00,0x00,0x41,0x00,0x75,0x00,0x74,0x00,0x68,0x00,0x6f,0x00,0x72,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00) + [System.Text.Encoding]::Unicode.GetBytes($cmd) + [byte[]](0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00) New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$uuid\" -Name "Triggers" -Value $triggerbytes -Type binary -Force New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$uuid\" -Name "Actions" -Value $actionbytes -Type binary -Force New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree" -Name $taskname -Force New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$taskname" -Name "Id" -Value "{3EC79FBB-0533-4356-89B3-8CE2003F1CD8}" -Type string -Force New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$taskname" -Name "Index" -Value 0x3 -Type DWORD -Force #Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$taskname" -Force #Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$uuid" -Force
更通用一些的测试用例:
PS C:\> schtasks.exe /create /tn test /tr "calc.exe" /sc minute /mo 1 /ru "administrator" 成功: 成功创建计划任务 "test"。 PS C:\> schtasks.exe /query /tn test 文件夹: \ 任务名 下次运行时间 模式 ======================================== ====================== =============== test 2021/1/11 14:56:00 就绪 PS C:\> schtasks.exe /query|findstr test test 2021/1/11 14:56:00 ?? PS C:\> Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\test" -Name "SD" PS C:\> schtasks.exe /query /tn test 错误: 系统找不到指定的文件。 PS C:\> schtasks.exe /query|findstr test PS C:\>
这种方式创建的计划任务,删除要相对麻烦一些:
$taskname = "test" $uuid = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$taskname" -Name "Id").Id Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$uuid" Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\$taskname" sc.exe stop schedule sc.exe start schedule
经过进程监控,发现在计划任务信息查询过程中的流程如下:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\SD
,查的到继续,查不到则终止查询HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\{TaskName}
中的 Id、Index、SD等值HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\{TaskId}
中的任务详细信息正常任务查询:
删除 SD 后的任务查询:
可见,因为找不到任务的 SD 信息,无法确定用户是否有权限查看该任务信息,导致系统直接判定无权限查看:
https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr
https://docs.microsoft.com/en-us/windows/application-management/svchost-service-refactoring
https://attack.mitre.org/techniques/T1053/002/
https://attack.mitre.org/techniques/T1053/005/
https://github.com/libyal/winreg-kb/blob/master/documentation/Task%20Scheduler%20Keys.asciidoc
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/
https://github.com/dahall/TaskScheduler
https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1464/