内容简介:By Phillip Langlois and Edward TorkingtonIn November 2019, we published aDuring the course of this research, we discovered a number of vulnerabilities in several COM services that we reported to Microsoft. In this particular post, we will cover one of thes
By Phillip Langlois and Edward Torkington
Introduction
In November 2019, we published a blog post covering an elevation-of-privilege vulnerability we found in Windows whilst conducting research into Windows Component Object Model (COM) services.
During the course of this research, we discovered a number of vulnerabilities in several COM services that we reported to Microsoft. In this particular post, we will cover one of these vulnerabilities in detail. The motivation for doing so is twofold; firstly, at a high level the root cause of this vulnerability shares many similarities with a number of others that we discovered and is itself of interest. Secondly, Microsoft’s initial attempt to patch the vulnerability highlighted both the difficulty of successfully fixing this kind of bug and, perhaps of even more interest, some inaccuracies in Microsoft documentation that may have implications for other operating system components.
Some basic background information about COM may be found in our first post and we will not cover any COM background material here.
The Component Based Servicing Session Object
The Windows Module Installer service is enabled by default on Windows 10 and executes as the user NT AUTHORITY\SYSTEM
. A number of COM objects execute as local servers in this service, as shown by OleViewDotNet
in the screenshot below:
The following screenshot displays the launch permissions applied to the service and shows that users in the group NT AUTHORITY\INTERACTVE
can launch the COM servers it hosts:
Servers executing in the context of the Windows Modules Installer service are therefore of interest when investigating possible privilege elevation vectors and this includes the Component Based Servicing Session COM object. This object implements a number of non-standard interfaces, as shown in the screenshot below:
In particular, this object implements various iterations of the undocumented ICbsSession
interface. On Windows 10 at the time of writing, for example, this object supports the ICbsSession10
interface and OleViewDotNet
(with symbols configured) gives us the following interface definition:
Arbitrary File Disclosure (Part 1)
Without performing any in-depth analysis of the code, it was clear that some of the methods exposed by the ICbsSession10
interface were of interest. Furthermore, it seemed likely that the Initialize
method would need to be called successfully prior to calling other methods.
In general, understanding how to correctly call such a relatively complex method without documentation requires some reverse engineering of the code behind. In this case, however, we noticed that calling Initialize
with any data resulted in the service writing entries to the CBS.log
file in the %WINDIR%\Logs\CBS
folder. In particular, it was possible to iteratively determine the purpose of most of the arguments to Initialize
(for example) by calling the method, examining the latest entries in the log file (and associated activity in Process Monitor), fixing reported errors and trying again. The precise details do not add a great deal to this exposition and we will not cover them here. Ultimately, we were able to determine the following working definitions for the Initialize
and CreatePackage
methods:
HRESULT Initialize( [in] int p0, [in] wchar_t* ClientId, [in] wchar_t* BootDrive, [in] wchar_t* WindowsDir ); HRESULT CreatePackage( [in] int p0, [in] int p1, [in] wchar_t* PackageFolder, [in] wchar_t* p3, [out] IUnknown** p4 );
Analysis confirmed that the Initialize
method must be called successfully prior to executing other methods from the interface. The p0
and ClientId
parameters can be set to arbitrary values, and BootDrive
can be set to the string C:\
. The WindowsDir
parameter must be set to a folder containing certain Windows components, however, and the parent folder of the WindowsDir
folder must also contain a suitable Users
folder. In practice, it is possible to construct a suitable folder structure containing the required files from a base installation of Windows 10. Note, however, that it is not possible to specify the actual Windows
folder, since this results in a sharing violation when the service attempts to open certain registry hives.
Most of the parameters to CreatePackage
do not matter for the purposes of the vulnerability we will shortly describe; setting p0
to the same value as used for the Initialize
call, p1
to 1
and p3
to an arbitrary string works fine ( p4
is an output parameter that we also do not care about). When CreatePackage
is called in this way after a successful call to Initialize
, the service attempts to copy the file update.mum
from the specified PackageFolder
folder to a temporary folder in the CbsTemp
folder located in the folder specified by WindowsDir
in the Initialize
call. This copy was performed by the service without impersonating the caller.
As described previously, a low-privilege user is able to create the folder structure required by the Initialize
method. Such a user is also able to use the CreateSymLink
tool to create a folder containing a symbolic link called update.mum
that points to any file on the local file system. When such a folder is passed to the CreatePackage
method, the target of the symbolic link is copied and this copy is then readable by the user, leading to a simple arbitrary file disclosure vulnerability.
This vulnerability was reported to Microsoft in August 2019 and assigned CVE-2019-1381. A patch was released in November 2019.
Arbitrary File Disclosure (Part 2)
Process Monitor showed that prior to the patch, the file copy operation was performed in a TIWorker.exe
process by the CopyManifestFilesAndVerifyContent
method implemented in the module CbsCore.dll
. After applying the patch and attempting the exploit, we observed additional entries in the CBS.log
file similar to those shown below:
2019-11-15 10:51:41, Info CBS Build: 18362.1.x86fre.19h1_release.190318-1202 2019-11-15 10:51:41, Info CBS Virtual Edition: Microsoft-Windows-EducationEdition will be mapped to: Microsoft-Windows-ProfessionalEdition 2019-11-15 10:51:41, Info CBS Session: 2104_24958890 initialized by client string1, external staging directory: (null), external registry directory: (null) <strong>2019-11-15 10:51:41, Info CBS Symlink found, not copying file. [HRESULT = 0x80070002 - ERROR_FILE_NOT_FOUND]</strong> 2019-11-15 10:51:41, Info CBS Failed to copy catalog:\\?\C:\users\lowpriv\CBSSTEST\update.cat to private session store: \\?\C:\users\lowpriv\CBSSLPEdata\windows\CbsTemp\2104_24958890\ [HRESULT = 0x80070002 - ERROR_FILE_NOT_FOUND] 2019-11-15 10:51:41, Info CBS Failed to copy manifest: \\?\C:\users\lowpriv\CBSSTEST\update.mum to private store and verify the content. [HRESULT = 0x80070002 - ERROR_FILE_NOT_FOUND] 2019-11-15 10:51:41, Info CBS Failed to initialize internal package [HRESULT = 0x80070002 - ERROR_FILE_NOT_FOUND] 2019-11-15 10:51:41, Error CBS Failed to create internal package [HRESULT = 0x80070002 - ERROR_FILE_NOT_FOUND] 2019-11-15 10:51:41, Info CBS Failed to create windows update package [HRESULT = 0x80070002 - ERROR_FILE_NOT_FOUND] 2019-11-15 10:51:41, Info CBS Failed to CreatePackage using worker session [HRESULT = 0x80070002] 2019-11-15 10:51:41, Info CBS Failed to create internal CBS package [HRESULT = 0x80070002]
The highlighted line appears to indicate that the service has identified that we have provided a symbolic link rather than an ordinary file and has therefore aborted the operation. At this point, we thought we understood how the patch had addressed the issue, however for completeness we wanted to check the modified code to be certain that there was no bypass possible. Returning to Process Monitor again to help us find the modified code, we actually noticed something surprising – our original exploit code was still causing the service to copy our target file!
So what was going on here? The file copy operation was now being performed by the PrivCopyFileToTargetFolder
method from CbsCore.dll
, and this method was called by the previously identified CopyManifestFilesAndVerifyContent
method. A decompilation of PrivCopyFileToTargetFolder
is shown below:
We can clearly see the call to CBSWdsLog
that generates the log file entry we observed and at first glance, this code seems fine. The logic is as follows:
- Call
GetFileAttributesW
on the file path passed to the service - Check bit 10 of the result
- This is the
FILE_ATTRIBUTE_REPARSE_POINT
flag that should be set for a symbolic link
- This is the
- Execute the copy file operation if this bit is not set (via a helper function)
- Otherwise, log the error message
Looking more closely, however, we noticed that the error message will get logged whenever the helper is not executed. In particular, the error will get logged if the specified file does not exist (since GetFileAttributesW
will return an error condition). Returning to the log file, we noticed the following entries:
2019-11-15 10:51:41, Info CBS Failed to copy catalog:\\?\C:\users\lowpriv\CBSSTEST\update.cat to private session store: \\?\C:\users\lowpriv\CBSSLPEdata\windows\CbsTemp\2104_24958890\ [HRESULT = 0x80070002 - ERROR_FILE_NOT_FOUND] 2019-11-15 10:51:41, Info CBS Failed to copy manifest: \\?\C:\users\lowpriv\CBSSTEST\update.mum to private store and verify the content. [HRESULT = 0x80070002 - ERROR_FILE_NOT_FOUND]
Again looking more closely, we see that the first entry is actually relating to the file update.cat
, not update.mum
as we expected, and correctly indicates that the file update.cat
could not be found. The CopyManifestFilesAndVerifyContent
method actually calls PrivCopyFileToTargetFolder
in three places. The first of these attempts to copy the file update.mum
from the specified PackageFolder
; the second call attempts to copy update.cat
. The first log entry shown above is written by the CopyManifestFilesAndVerifyContent
method when the second call to PrivCopyFileToTargetFolder
fails and it is this call to PrivCopyFileToTargetFolder
that causes the symbolic link error to be written to the log file.
The second log entry above is actually written by the CCbsPackage::PrepareInitialize
method that calls CopyManifestFilesAndVerifyContent
and we note that the messages are very similar. As we ourselves discovered, upon viewing the second entry (which references update.mum
) and the symbolic link error it is very easy to assume that the code is functioning correctly when passed a symbolic link.
So what happened when PrivCopyFileToTargetFolder
was called to copy update.mum
? Well it turns out that GetFileAttributesW
returns information about the target of the symbolic link, not the symbolic link itself, and the target file was copied without error.
So why was the code patched in this manner? The following text is taken directly from the MSDN documentation for the GetFileAttributesW
function:
Symbolic link behavior—If the path points to a symbolic link, the function returns attributes for the symbolic link.
Additional documentation is also available covering the behaviour of a number of file system functions when passed a path to a symbolic link. The following text is taken directly from this page:
GetFileAttributes
If the path points to a symbolic link, the function returns attributes for the symbolic link.
When a symbolic link is created using the mklink
tool, GetFileAttributes
does indeed return attributes for the link and this includes the FILE_ATTRIBUTE_REPARSE_POINT
flag. When a symbolic link is created with the CreateSymLink
tool, however, the documentation is incorrect and attributes are returned for the target of the link. The key here is that Windows actually supports a number of different types of symbolic link (see for example James Forshaw’s blog post ) and the documentation does not apply to all of these. The broken patch therefore appears to be a combination of faulty logic in the code and use of an API which has incompletely/incorrectly documented behaviour.
The issues with the initial patch were reported to Microsoft two days after the patch was released and assigned CVE-2020-0859. A further patch was released in March 2020.
Final Patch
Typically, fixing information disclosure vulnerabilities in services such as this can be more complicated than one might expect. The difficulty arises primarily because of the need to execute code with high privileges in order to carry out the required functionality but on behalf of users with limited rights, preventing the service from simply impersonating the low privilege user for the duration of the task. This does not appear to be the case for the Component Based Servicing Session object however, and Microsoft ultimately chose to fix the vulnerability described above by impersonating the calling user prior to calling the CopyManifestFilesAndVerifyContent
method. This appears to successfully mitigate the issue.
Incorrect Documentation and Future Work
The documentation referenced above covers the behaviour of a number of file system related APIs when processing symbolic links. In addition to GetFileAttributes
, testing indicates that this documentation is also incorrect for DeleteFile
when symbolic links are created with CreateSymLink
and it is likely that other APIs are similarly affected.
Having discovered a vulnerability caused in part by this incorrect documentation, we were curious to see if other Microsoft services made similar assumptions about the behaviour of these APIs. In general, this seems to be a relatively difficult question to answer without access to source code. In the case of GetFileAttributes
, however, it is straightforward to see when code is attempting to check for the presence of a reparse point or symbolic link by looking for references to the FILE_ATTRIBUTE_REPARSE_POINT
flag after the API call.
Making use of radare2
, we were able to quickly identify a large number of calls to GetFileAttributes
in standard Microsoft modules and then disassemble the code around each call. Simple post processing of this data then allowed us to identify over 60 instances of code implementing similar to logic to that shown above. Furthermore, in a number of cases the identified modules were associated with services executing with high privileges.
In general, given any particular example of such code it is non-trivial to determine how a low privilege user might exercise the code. We performed a very quick triage of the more interesting examples, but did not discover additional vulnerabilities. This analysis was time limited, however, so we do not discount the possibility that such vulnerabilities remain to be found.
Of course given access to source code, this problem would likely be considerably easier. In addition to reporting the flawed patch, we therefore raised a separate issue with Microsoft to highlight the incorrect documentation and the possibility that there could be vulnerabilities related to this in sensitive code. At the time of writing, Microsoft have accepted that the documentation is confusing but have no immediate plans to update it. The documentation will eventually be updated to clarify the behaviour but no patch updates will be released.
Radare2 Script and Sample Output
The following radare2
script was used to identify possibly incorrect use of the GetFileAttributes
method:
import sys import os import r2pipe import re apis = ["GetFileAttributes"] directory = os.fsencode(sys.argv[1]) p = re.compile(" bt .*0xa\n") for file in os.listdir(directory): filename = sys.argv[1] + "\\" + os.fsdecode(file) if filename.endswith(".dll") or filename.endswith(".exe"): f = open("GetFileAttributes.txt", "a+", encoding='UTF-8') try: r2 = r2pipe.open(filename) r2.cmd('aaa') found = False text = "" for api in apis: results = r2.cmdj('axtj @ `ii~%s[1]`' % api) for line in results: presult = r2.cmd("pd 50 @{}".format(line["from"])) presult = presult.replace("\r", "") if (p.search(presult)): found = True text += presult if (found): f.write("----------------------------------------------------------\n") f.write("Filename: %s\n" % filename) f.write("----------------------------------------------------------\n") f.write(text + "\n\n\n") f.close() except Exception as e: f.write(repr(e)) f.write(sys.exc_info()[0]) f.close() r2.cmd('quit')
An extract of the output from this script is shown below, highlighting the call to GetFileAttributes
and the test for the FILE_ATTRIBUTE_REPARSE_POINT
flag:
---------------------------------------------------------- Filename: c:\windows\system32\\AgentService.exe ---------------------------------------------------------- │ ; CODE XREF from fcn.14008e224 (0x14008e262) │ 0x14008e267 ff15bbb90300 <strong>call qword sym.imp.KERNEL32.dll_GetFileAttributesW ; [0x1400c9c28:8]=0x112644 reloc.KERNEL32.dll_GetFileAttributesW ; "D&\x11" ; DWORD GetFileAttributesW(LPCWSTR lpFileName)</strong> │ 0x14008e26d 448bf0 mov r14d, eax │ 0x14008e270 83f8ff cmp eax, 0xffffffff │ ┌─< 0x14008e273 7513 jne 0x14008e288 │ │ 0x14008e275 4d8bc7 mov r8, r15 │ │ 0x14008e278 488bd7 mov rdx, rdi │ │ 0x14008e27b 488bce mov rcx, rsi │ │ 0x14008e27e e819f7ffff call fcn.14008d99c │ ┌──< 0x14008e283 e911010000 jmp 0x14008e399 │ ││ ; CODE XREF from fcn.14008e224 (0x14008e273) │ │└─> 0x14008e288 4533e4 xor r12d, r12d │ │ 0x14008e28b 410fbae60a <strong>bt r14d, 0xa</strong> │ │┌─< 0x14008e290 0f83c9000000 jae 0x14008e35f │ ││ 0x14008e296 48837f1808 cmp qword [rdi + 0x18], 8 ; [0x18:8]=-1 ; 8 │ ┌───< 0x14008e29b 7205 jb 0x14008e2a2 │ │││ 0x14008e29d 488b17 mov rdx, qword [rdi] │ ┌────< 0x14008e2a0 eb03 jmp 0x14008e2a5 │ ││││ ; CODE XREF from fcn.14008e224 (0x14008e29b) │ │└───> 0x14008e2a2 488bd7 mov rdx, rdi │ │ ││ ; CODE XREF from fcn.14008e224 (0x14008e2a0) │ └────> 0x14008e2a5 48c745e80700. mov qword [var_18h], 7 │ ││ 0x14008e2ad 4c8965e0 mov qword [var_20h], r12 │ ││ 0x14008e2b1 66448965d0 mov word [var_30h], r12w │ ││ 0x14008e2b6 66443922 cmp word [rdx], r12w │ ┌───< 0x14008e2ba 7505 jne 0x14008e2c1 │ │││ 0x14008e2bc 4d8bc4 mov r8, r12 │ ┌────< 0x14008e2bf eb0e jmp 0x14008e2cf │ ││││ ; CODE XREF from fcn.14008e224 (0x14008e2ba) │ │└───> 0x14008e2c1 4983c8ff or r8, 0xffffffffffffffff │ │ ││ ; CODE XREF from fcn.14008e224 (0x14008e2cd) │ │┌───> 0x14008e2c5 49ffc0 inc r8 │ │╎││ 0x14008e2c8 6646392442 cmp word [rdx + r8*2], r12w │ │└───< 0x14008e2cd 75f6 jne 0x14008e2c5 │ │ ││ ; CODE XREF from fcn.14008e224 (0x14008e2bf) │ └────> 0x14008e2cf 488d4dd0 lea rcx, [var_30h] │ ││ 0x14008e2d3 e8f87ef7ff call fcn.1400061d0
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
信息学奥林匹克教程·提高篇
吴耀斌 / 湖南师范大学出版社 / 2003-1 / 24.00元
《信息学奥林匹克教程》(提高篇)既有各个算法设计基本思路的讲解及对求解问题的分析,注重了算法引导分析与不同算法的比较,又给出了具体的编程思路与参考程序,程序采用信息学竞赛流行的Turbo Pascal7.0语言编写,并注重结构化与可读性。一起来看看 《信息学奥林匹克教程·提高篇》 这本书的介绍吧!