Advanced Windows Task Scheduler Playbook - Part.2 (上) | 高级攻防08

语言: CN / TW / HK

本文约7000字,阅读约需13分钟。

我们上一章通过对MS-TSCH进行分析理解,大致明确了微软关于计划任务程序的设计思路:以文档化XML格式作为描述、RPC协议为基础,在公开函数式RPC调用的同时,通过COM Helper实现面向对象。

编程思想与设计模式才应当是最基础的安全技术,微软在计划任务程序设计中明显体现了一个进化的思想,从面向过程进化到面向对象,这个过程就是常说的封装。

让我们继续用常见的Web角度进行类比,可以理解为:

PHP5进化至PHP7。

PHP/ASP进化至Java/.Net。

JS进化至TS/ES6。

在开发与设计层面,一些思想是统一且几乎不会改变的。

和渗透时常用的脚本或工具不同,在一个完整的系统中,无论是一套封装后的组件,或是一组完善的协议实现,其本身并没有实现之外的任何意义。

只有在使用者(或称“调用方”)根据某些业务逻辑进行调用,随之多个完整的业务功能按照相关逻辑组成一套系统,此时的组件才称得上“有意义”。

如果你认同这个观点,那么所看到的每一个渗透技巧事件追踪漏洞分析都能找到例子进行类比。毋庸置疑,每一个。

计划任务程序作为文档化组件之一,我们当然可以直接根据文档进行调用。无论是直接利用十五年前微软提供的C/C++或者VBS,或是进一步利用十四年前vs2008附带的的C# Interop,再或是利用十年前用烂的的PowerShell都能够直接产生一些红队(Redteam)、武器化(Weaponize)、渗透测试工具(Pentesting Tools)。

感谢微软提供了丰富的API为渗透测试带来方便,但回归研究者思路,我们不该忽略这一点:计划任务作为重要系统组件之一,被广泛应用于系统多个功能模块中。

所以,让我们来思考一组问题:

有哪些自带功能调用了计划任务?

这个功能可以起到什么作用?

这个功能是否进行了组件化,即可以通过某种方式进行调用?

是否存在利用或滥用的可能?

1

基础

在回答这个问题之前,让我们重温COM基础。

微软提供了非常完善的基础知识文档:

"https://docs.microsoft.com/en-us/windows/win32/com/com-fundamentals"

以及配套的示例代码,这些文档和代码的历史至少可以追溯至Windows 2000的时代。

我不想在查找资料上花太多篇幅。根据个人经验,花费两天时间,拿出挖洞找链的劲头,配合写论文找参考资料的态度,将原文从头到尾啃一遍,比看十篇技术文章都要有用的多。包括你在看的这篇。

参考文档顺便查漏补缺,我们重新回忆一下最为基础的知识点:

在设计层面,COM模型分为接口与实现。 例如计划任务示例代码中的 ITaskService

区分COM组件的唯一标识为Guid,分别为针对接口的IID(Interface IDentifier)与针对类的CLSID(CLaSs IDentifier)。

例如CLSID_TaskScheduler定义为:

"0F87369F-A4E5-4CFC-BD3E-73E6154572DD"

COM组件需要在注册表内进行注册才可进行调用。通常情况下,系统预定义组件注册于:

"HKEY_LOCAL_MACHINE\SOFTWARE\Classes"

用户组件注册于:

"HKEY_CURRENT_USER\SOFTWARE\Classes"

HKEY_CLASSES_ROOT为二者合并后的视图,在系统服务角度等同于:

"HKEY_LOCAL_MACHINE\SOFTWARE\Classes"

例如计划任务组件的注册信息注册于:

"HKEY_CLASSES_ROOT\CLSID\{0f87369f-a4e5-4cfc-bd3e-73e6154572dd}"

Windows最小的可独立运行单元是进程,最小的可复用的代码单元为类库,所以COM同样存在进程外(In-Process)与进程内(Out-Of-Process)两种实现方式。

多数情况下,进程外COM组件为一个exe,进程内COM组件为一个dll。

例如计划任务的COM对象为进程内组件,由taskschd.dll实现。

为方便COM组件调用,可以通过ProgId(Programmatic IDentifier)为CLSID指定别名。

例如计划任务组件的ProgId为Schedule.Service.1。

客户端调用CoCreateInstance、CoCreateInstanceEx、CoGetClassObject等函数时,将创建具有指定CLSID的对象实例,这个过程称为激活(Activation)。

例如微软示例代码中的:

"CoCreateInstance(CLSID_TaskScheduler,....)"

COM采用工厂模式对调用方与实现方进行解耦,包括进程内外COM组件激活、通信、转换,IUnknown::QueryInterface和IClassFactory始终贯穿其中。

例如微软示例代码中的一大堆QueryInterface。

现在,我们有了对COM的基本认知,接下来要在一个庞大、复杂的操作系统之中,跟踪一个微小的COM对象调用了。

无论多么复杂的系统,归根结底由人开发,由编译器编译。我们知道Windows的编译器为VS,语言为微软风格的C/C++,开发者为三哥。

那么来到思考时间:你是一名三哥程序猿,恒河水使你的代码和你的身体一样无比健壮。现在,你要用VS建立一个C/C++项目,里面调用计划任务做一些事。

你会怎么写?

#include <taskschd.h>

为什么?

“标准”示例如此。

很好,我们得到了第一种方式:

在所有系统组件中搜索字符串形式的:

"0F87369F-A4E5-4CFC-BD3E-73E6154572DD"

以及其二进制表现形式。

然后重新把思维切换回安全领域,暂时客串一番样本分析:你是一名应急响应工程师,陆莲花胃脑虫被你里里外外反反复复上上下下肆意玩弄得不成马形。

现在你出台到了客户内网分析一批恶意样本,已知其中某样本会创建计划任务,在没自动化沙箱的情况下怎样能把它揪出来进行后续分析?

那还用说?ProcMon开起来、某绒刀砍它。

于是我们有了第二种方式:

跟踪下述注册表的读取:

"HKEY_CLASSES_ROOT\CLSID\{0f87369f-a4e5-4cfc-bd3e-73e6154572dd}\InprocServer32"

通过日志、Hook、劫持等等方式获取调用栈。

最后,把思维切到我们最熟悉的安全开发/红蓝对抗:现在有一个框架,能随意拓展,能过宇宙杀软加计划任务,还有源码能抄。简直完美得不能再完美了。

唯一一个问题:不知道在哪调了计划任务,就看到一堆配置文件一堆设计模式。

直接扔到IDE里面搜CLSID、IID、ProgId反过去找引用啊!

我们拿到了第三种方式:

考虑到工厂与动态调用,在配置文件等静态数据中搜索:

"0F87369F-A4E5-4CFC-BD3E-73E6154572DD"

以及其二进制表现形式。

2

发现

现在,我们有三种可行方案来进行跟踪了。

思考一下三种方式的优劣:第二种动态追踪的方式能够直观的找到调用方,但一个前提是必须存在活动的调用。

计划任务功能并不是一个需要频繁调用的功能,Windows的复杂性也决定了无法手动访问每一个功能,所以不妨暂时搁置。

第一三种均可归结为静态查找,考虑到我们研究的目标基于COM,而COM绝大多数配置基于注册表,所以首先在注册表这个最大的公共配置文件内进行搜索,可以得到如图所示结果:

C:\>reg query HKEY_CLASSES_ROOT\CLSID\{A6BFEA43-501F-456F-A845-983D3AD7B8F0} /s


HKEY_CLASSES_ROOT\CLSID\{A6BFEA43-501F-456F-A845-983D3AD7B8F0}
(默认) REG_SZ Virtual Factory for MaintenanceUI
AppId REG_SZ {A6BFEA43-501F-456F-A845-983D3AD7B8F0}
LocalizedString REG_EXPAND_SZ @%SystemRoot%\System32\MaintenanceUI.dll,-1


HKEY_CLASSES_ROOT\CLSID\{A6BFEA43-501F-456F-A845-983D3AD7B8F0}\Elevation
Enabled REG_DWORD 0x1


HKEY_CLASSES_ROOT\CLSID\{A6BFEA43-501F-456F-A845-983D3AD7B8F0}\InProcServer32
(默认) REG_EXPAND_SZ %SystemRoot%\System32\shpafact.dll
ThreadingModel REG_SZ Apartment


HKEY_CLASSES_ROOT\CLSID\{A6BFEA43-501F-456F-A845-983D3AD7B8F0}\VirtualServerObjects
{0f87369f-a4e5-4cfc-bd3e-73e6154572dd} REG_SZ

我们发现了一个可疑的东西:

一个由:

"%SystemRoot%\System32\shpafact.dll"

实现的未文档化COM组件:

"A6BFEA43-501F-456F-A845-983D3AD7B8F0"

一个未文档化的自定义注册表项VirtualServerObjects,其值包含计划任务组件CLSID。

[email protected]=1,意味着可以进行UAC自动提升。

接下来要做的,就是对这个组件进行分析,找到其设计层面的意义,以及探寻是否存在利用的可能。

(未完待续)

- END -

往期推荐

Fake dnSpy - 这鸡汤里下了毒!

ADCS攻击面挖掘与利用

安全认证相关漏洞挖掘

长按下方图片即可 关注

点击下方阅读原文,加入社群,读者作者无障碍交流

读完有话想说?点击留言按钮,让上万读者听到你的声音!

「其他文章」