CVE-2020-0787-Windows本地提权漏洞分析

2020-07-01 约 339 字 预计阅读 2 分钟

声明:本文 【CVE-2020-0787-Windows本地提权漏洞分析】 由作者 剑残雪飘 于 2020-07-01 09:37:57 首发 先知社区 曾经 浏览数 182 次

感谢 剑残雪飘 的辛苦付出!

Author:剑残雪飘@深蓝攻防实验室

itm4nBackground Intelligent Transfer Service中发现了任意文件移动漏洞,这里学习一下其中的思路。

BITS

参考链接

程序员和系统管理员使用后台智能传输服务(BITS)从HTTP Web服务器和SMB文件共享下载文件或将文件上传到HTTP Web服务器和SMB文件共享。BITS将考虑传输成本以及网络使用情况,以便用户的前台工作影响尽可能小。即使重新启动后,BITS也可以处理网络中断,暂停并自动恢复传输。

这个服务公开了几个COM对象,这些对象是“控件类”的不同迭代,并且还有一个“旧版控件类”。“旧版控件类”可以获取指向IBackgroundCopyGroup的指针,有两个未记录的方法QueryNewJobInterface()SetNotificationPointer()

如果用户调用IBackgroundCopyGroup接口的CreateJob()方法,就可以获得一个指向IBackgroundCopyJob1接口的指针,如果调用QueryNewJobInterface()方法,就可以获得一个指向新IBackgroundCopyJob接口的指针。

此调用是由服务处理的without impersonation,意味着用户可以在 NT AUTHORITY\SYSTEM的context中获得一个指向IBackgroundCopyJob接口的指针

创建并将文件添加到队列后,将创建一个临时文件,服务完成文件写入后,通过MoveFileEx()重命名,漏洞点为用QueryNewJobInterface()返回的指针是without impersonation,可以获取NT AUTHORITY\SYSTEM权限。

BITS COM类如何工作?

后台智能传输服务公开了几个COM对象,可以使用OleViewDotNet列出这些对象

重点关注后台智能传输(BIT)控制类1.0旧版BIT控制类及其主要接口,分别为IBackgroundCopyManagerIBackgroundCopyMgr

“new” BIT Control Class

BIT 控制类1.0的工作方式如下:

  1. 创建BIT控制类(CLSID:4991D34B-80A1-4291-83B6-3328366B9097)的实例,需要一个指向IBackgroundCopyQMgr的指针,接口为CoCreateInstance()
  2. 然后,创建一个“作业”,并调用IBackgroundCopyManager::CreateJob()获取指向该IBackgroundCopyJob接口的指针。
  3. 然后,调用将文件添加到作业的IBackgroundCopyJob::AddFile()。这需要两个参数:URL和本地文件路径。该URL也可以是UNC路径。
  4. 最后,由于作业中创建一个SUSPENDED状态,调用IBackgroundCopyJob::Resume(),并在工作的状态TRANSFERRED时调用IBackgroundCopyJob::Complete()
CoCreateInstance(CLSID_4991D34B-80A1-4291-83B6-3328366B9097)   -> IBackgroundCopyManager*
|__ IBackgroundCopyManager::CreateJob()                        -> IBackgroundCopyJob*
    |__ IBackgroundCopyJob::AddFile(URL, LOCAL_FILE) 
    |__ IBackgroundCopyJob::Resume() 
    |__ IBackgroundCopyJob::Complete()

尽管BIT服务的运行方式为NT AUTHORITY\SYSTEM,但所有这些操作都是在模拟RPC客户端执行的因此此处无法提权。

Legacy Control Class

传统控制类的工作方式有些不同。在过程开始时需要一个额外的步骤。

  1. 创建传统BIT控制类(CLSID:69AD4AEE-51BE-439B-A92C-86AE490E8B30)的实例,需要一个指向IBackgroundCopyQMgr的指针,接口为CoCreateInstance()
  2. 然后,创建调用IBackgroundCopyQMgr::CreateGroup()的组,获取指向该IBackgroundCopyGroup接口的指针
  3. 然后,创建“作业”,并调用IBackgroundCopyGroup::CreateJob()以获取指向该IBackgroundCopyJob1接口的指针。
  4. 然后,将文件添加到“作业”中,该调用IBackgroundCopyJob1::AddFiles()FILESETINFO结构作为参数。
  5. 最后,由于作业中创建一个SUSPENDED状态,调用IBackgroundCopyJob1::Resume(),并在工作的状态TRANSFERRED时调用IBackgroundCopyJob1::Complete()
CoCreateInstance(CLSID_69AD4AEE-51BE-439B-A92C-86AE490E8B30)   -> IBackgroundCopyQMgr*
|__ IBackgroundCopyQMgr::CreateGroup()                         -> IBackgroundCopyGroup*
    |__ IBackgroundCopyGroup::CreateJob()                      -> IBackgroundCopyJob1*
        |__ IBackgroundCopyJob1::AddFiles(FILESETINFO)
        |__ IBackgroundCopyJob1::Resume()
        |__ IBackgroundCopyJob1::Complete()

跟上面一样,尽管BIT服务运行为NT AUTHORITY\SYSTEM,但所有这些操作都是在模拟RPC客户端执行的,因此在这里也不能提权。

这两个COM类及其接口的用法在MSDN有详细记录。但是,在尝试了解IBackgroundCopyGroup接口如何工作时,注意到MSDN上列出的方法与其实际的Proxy定义有一些差异

IBackgroundCopyGroup接口的文档可在此处获得。文档有13种方法

但是,当使用OleViewDotNet查看此接口的代理定义时,我们可以看到它实际上有15个方法

Proc3Proc15匹配文档中的方法,但Proc16Proc17不存在。

通过文档,我们知道相应的头文件是Qmgr.h

看到两个未记录的方法:QueryNewJobInterface()SetNotificationPointer()

未公开的方法:“ QueryNewJobInterface()”

参数结构

virtual HRESULT STDMETHODCALLTYPE QueryNewJobInterface( 
            /* [in] */ __RPC__in REFIID iid,
            /* [iid_is][out] */ __RPC__deref_out_opt IUnknown **pUnk) = 0;

打开qmgr.dll

首先,将输入GUID(接口ID)与硬编码值37668d37-507e-4160-9316-26306d150b12进行比较:如果不匹配,则该函数返回错误代码0x80004001 。否则,它将从调用函数CJob::GetJobExternal()

硬编码的GUID值(37668d37-507e-4160-9316-26306d150b12)可以在Bits.h头文件中找到为IID_IBackgroundCopyJob

任意文件移动漏洞

在进一步进行分析之前,可以根据收集到的少量信息做出猜测。

  • 未记录方法的名称QueryNewJobInterface()
  • 传统 BIT控制类的IBackgroundCopyGroup接口中为exposed 。
  • GUID“新”的IBackgroundCopyJob接口为involved。

因此,可以假设此函数的目的是从Legacy Control Class获取指向“ new” IBackgroundCopyJob接口的指针。

  1. 创建指向Legacy Control Class的实例,获取一个指向IBackgroundCopyQMgr接口的指针
  2. 创建一个新组调用IBackgroundCopyQMgr::CreateGroup(),获取一个指向IBackgroundCopyGroup接口的指针
  3. 创建一个job,调用IBackgroundCopyGroup::CreateJob(),获取一个指向IBackgroundCopyJob1接口的指针
  4. 通过IBackgroundCopyJob1::AddFiles()添加文件
  5. 调用IBackgroundCopyGroup::QueryNewJobInterface()方法并获得指向未知接口的指针,假定它是一个IBackgroundCopyJob接口
  6. 通过调用IBackgroundCopyJob接口的Resume()Complete()来恢复和完成job而不是IBackgroundCopyJob1接口

目标URL为\\127.0.0.1\C$\Windows\System32\drivers\etc\hosts

使用Procmon分析了BIT服务

首先,可以看到该服务**在目标目录中创建了一个TMP文件。

调用该Resume()函数,该服务将开始读取目标文件\\127.0.0.1\C$\Windows\System32\drivers\etc\hosts并将其内容写入TMP文件,此时依然是 impersonating the current user

最后,将TMP文件重命名为test.txt,并且调用MoveFileEx()不再是当前用户,这意味着文件移动是在NT AUTHORITY\SYSTEM的context中完成的

SetRenameInformationFile调用了win32的MoveFileEx()函数。

漏洞原理

CreateJob()方法在IBackgroundCopyGroupCOldGroupInterface类中实现

)

由于CFG的存在,看不出什么信息,这个方法调用CreateJobInternal()方法,CreateJobInternal()调用CLockedJobWritePointer::ValidateAccess

CLockedJobWritePointer::ValidateAccess调用了CJob::CheckClientAccess

CheckClientAccess()检查用户token并应用于当前线程的impersonation

这个执行流返回CreateJobInternal方法,并调用CJob::GetOldJobExternal返回IBackgroundCopyJob1类型的指针

调用流程

(CLIENT) IBackgroundCopyGroup::CreateJob()
   |
   V
(SERVER) COldGroupInterface::CreateJob()
         |__ COldGroupInterface::CreateJobInternal()
             |__ CLockedJobWritePointer::ValidateAccess()
             |   |__ CJob::CheckClientAccess() // Client impersonation
             |__ CJob::GetOldJobExternal() // IBackgroundCopyJob1* returned

了解了CreateJob()的实现原理,回到QueryNewJobInterface()方法,如果提供的GUID匹配IID_IBackgroundCopyJob,调用CJob::GetJobExternal,查询新的接口指针调用并返回给客户端。

流程

(CLIENT) IBackgroundCopyGroup::QueryNewJobInterface()
   |
   V
(SERVER) COldGroupInterface::QueryNewJobInterface()
         |__ CJob::GetJobExternal() // IBackgroundCopyJob* returned

当我们调用COldGroupInterface::QueryNewJobInterface()获取新的指针时,客户端并没有impersonated,这样就可以获取一个NT AUTHORITY\SYSTEM上下文的指针

实际上文件移动操作发生在调用IBackgroundCopyJob::Resume()之后以及IBackgroundCopyJob::Complete()之前

调用IBackgroundCopyJob::Resume()流程

(CLIENT) IBackgroundCopyJob::Resume()
   |
   V
(SERVER) CJobExternal::Resume()
         |__ CJobExternal::ResumeInternal()
             |__ ...
             |__ CJob::CheckClientAccess() // Client impersonation
             |__ CJob::Resume()
             |__ ...

调用IBackgroundCopyJob::Complete()流程

(CLIENT) IBackgroundCopyJob::Complete()
   |
   V
(SERVER) CJobExternal::Complete()
         |__ CJobExternal::CompleteInternal()
             |__ ...
             |__ CJob::CheckClientAccess() // Client impersonation
             |__ CJob::Complete()
             |__ ...

这两种情况都不能成功

当调用IBackgroundCopyGroup::QueryNewJobInterface()获取一个IBackgroundCopyJob类型的指针时,这个工作是由服务端完成的,而不是RPC客户端。此时可以利用成功。

MoveFileEx()调用流程

漏洞利用

本地创建一个job用于下载,并在tmp文件上设置Oplock,恢复执行后该服务会写入TMP文件触发Oplock,然后切换挂载点到对象目录,创建符号链接,tmp文件指向我们的文件,本地文件指向system32文件夹中的dll,最后释放Oplock,写入成功

1)准备

创建一个如下的目录

<DIR> %temp%\workspace
|__ <DIR> bait
|__ <DIR> mountpoint
|__ FakeDll.dll

mountpoint目录是从连接bait目录,切换到RPC Control对象,FakeDll.dll为了移动到受限位置,比如system32

2)创建挂载点

创建一个从%temp%\workspace\mountpoint%temp%\workspace\mountpoint的挂载点

3)创建新的job

Legacy Control Class接口创建一个新的job,参数如下

Target URL: \\127.0.0.1\C$\Windows\System32\drivers\etc\hosts
Local file: %temp%\workspace\mountpoint\test.txt

因为之前创建了链接,所以实际路径为%temp%\workspace\bait\test.txt

4)找到tmp文件设置Oplock

列出bait文件夹的内容找到类似BITAA6.tmp的临时文件,并在文件上设置设置Oplock

5)Resume并等待Oplock

恢复job时,会打开tmp文件写入触发Oplock

6)切换挂载点

切换前

TMP file   = %temp%\workspace\mountpoint\BIT1337.tmp -> %temp%\workspace\bait\BITAA6.tmp
Local file = %temp%\workspace\mountpoint\test.txt -> %temp%\workspace\bait\test.txt

切换挂载点创建符号链接

%temp%\workspace\mountpoint -> \RPC Control
Symlink #1: \RPC Control\BITAA6.tmp -> %temp%\workspace\FakeDll.dll
Symlink #2: \RPC Control\test.txt -> C:\Windows\System32\FakeDll.dll

完成此步骤后:

TMP file   = %temp%\workspace\mountpoint\BITAA6.tmp -> %temp%\workspace\FakeDll.dll
Local file = %temp%\workspace\mountpoint\test.txt -> C:\Windows\System32\FakeDll.dll

7) 释放Oplock

释放Oplock后,CreateFile将对原始TMP文件操作,并且该服务将开始写入%temp%\workspace\bait\BITAA6.tmp。之后,MoveFileEx()由于符号链接,最终将被重定向。DLL将被移至该System32文件夹

8) 提权

使用Update Session Orchestrator service加载移动到system32文件夹的dll文件WindowsCoreDeviceInfo.dll提权

参考链接

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now