Tuesday, January 22, 2019

One... Two... Three Micropatches For Three Windows 0days

A Short Micropatching Trilogy

by Mitja Kolsek, the 0patch Team


While we're busy ironing out the wrinkles before 0patch finally exits its adolescence (i.e., Beta) and becomes a fully responsible adult able to pay for its own rent, we did find some time to produce... not one, ... not two, ... but three 0day micropatches in the past few days. That's right, at this very moment you can get three 0days on your Windows computer micropatched for free! All you have to do is register a free 0patch account and install 0patch Agent.

Let's quickly go through each of these 0days and see what they allow attackers to do, and how we micropatched them. Then we can return to the wrinkle-ironing mode and bring you the best patching experience that we possibly can.


0day #1: the "angrypolarbearbug"


This 0day, dubbed "angrypolarberbug" by its author SandboxEscaper who published it last month, allows a local unprivileged process to get any chosen file on the system overwritten with the content of a Windows Error Reporting XML file. The attacker has very little control over the content of this XML file so the demonstration provided by SandboxEscaper was a local denial of service by corrupting a critical system file pci.sys, which prevents the system from booting. One can imagine potentially finding some other file to overwrite that would lead to execution of attacker's code under higher privileges such as SYSTEM or Administrator - but to our knowledge, such example has not been published.

The crux of this vulnerability is in the fact that the C:\ProgramData\Microsoft\Windows\WER\Temp\ folder, where the Windows Error Reporting Service is creating a temporary XML file, has inheritable permissions that include read, write and delete access for Authenticated Users (which includes the local attacker). This means that whenever anyone creates a new file there without specifying its permissions, any process on the system, including a low-privileged malicious process, will be able to replace that file with another file. And that's what SandboxEscaper's proof-of-concept does: it waits for the XML file to appear, and then quickly replaces it with a hard link to the chosen target file (e.g., pci.sys). When Windows Error Reporting Service subsequently re-opens this XML file for writing, it actually opens the linked-to file, and writes to that file.

Our micropatch makes a small change: when Windows Error Reporting Service creates the XML file, it now specifies permissions that are otherwise exactly the same as before, except that Authenticated Users don't have delete permissions on it. This keeps error reporting working while preventing the exploit from deleting the XML file (and thus also from creating a hard link in its place).

We currently have this micropatch for fully updated 64-bit Windows 10 version 1803. (Contact support@0patch.com to express your interest in porting to other versions.) Note that this issue doesn't seem to affect Windows 7, where error reporting works a bit differently.

You can see this micropatch in action in the following video.


And here's the source code for this micropatch.


;Micropatch for wer.dll version 10.17134.471
;
;How it works:
; a vulnerable call CreateFileW responsible for creating a temporary report XML file
; which inherits loose C:\ProgramData\Microsoft\Windows\WER\Temp\ permissions is replaced by
; a call to ConvertStringSecurityDescriptorToSecurityDescriptor which creates a new security
; descriptor from ACE string that gets supplied to a new CreateFileW call.
; The new security descriptor has no DELETE permissions for AuthenticatedUsers group
; on report XML so a regular user can no longer change it to a hard link.

MODULE_PATH "..\AffectedModules\wer.dll_10.17134.471_64bit\wer.dll"
PATCH_ID 344
PATCH_FORMAT_VER 2
VULN_ID 4657
PLATFORM win64

patchlet_start
PATCHLET_ID 1
PATCHLET_TYPE 2
PIT kernel32.dll!CreateFileW,advapi32.dll!ConvertStringSecurityDescriptorToSecurityDescriptorA,kernel32.dll!LocalFree
PATCHLET_OFFSET 0x00059bd7
JUMPOVERBYTES 11
N_ORIGINALBYTES 2

 code_start

  mov qword [rsp+10h], r8 ; dwShareMode
  mov qword [rsp+8h], rdi ; storing a global variable
  mov qword [rsp], rcx    ; lpFileName
  

  call arg0_StringSecurityDescriptor

  ; args for ConvertStringSecurityDescriptorToSecurityDescriptor
  ; we changed (A;;0x13019f;;;AU) to (A;;GRSD;;;AU) - meaning

  ; AuthenticatedUsers can Read and Delete only
  db "D:(A;;FA;;;BA)(A;;GRGW;;;AU)(A;;0x13019f;;;SU)(A;;0x13019f;;;LS)(A;;0x13019f;;;NS)(A;;0x13019f;;;WR)(A;;0x13019f;;;AC)(A;;0x13019f;;;S-1-15-2-2)",0
 
  arg0_StringSecurityDescriptor:
  pop rcx ; rcx=arg0_StringSecurityDescriptor
 
  mov rdx, 01h ; arg1: StringSDRevision=SDDL_REVISION_1
 
  ;arg2: this arg is part of SECURITY_ATTRIBUTES struct so we have to create this first

                   ; sa requires 18h of space
  sub rsp, 20h     ; but we're allocating 8 more than required nLength to keep stack alignment
  lea r8, [rsp+8h] ;arg2: SecurityDescriptor=&sa.lpSecurityDescriptor
 
  ;init sa:
  mov dword [rsp],18h    ;sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  mov dword [rsp+10h],1h ;sa.bInheritHandle=FALSE
 
  xor r9d,r9d ; SecurityDescriptorSize=NULL
 
  sub rsp, 20h ; allocate homespace

  call PIT_ConvertStringSecurityDescriptorToSecurityDescriptorA
 
  ;copy CreateFileW args 5,6 and 7 to a new stack frame
  mov rax, [rsp+60h] ; dwCreationDisposition
  mov qword [rsp],rax
  mov rax, [rsp+68h] ; dwFlagsAndAttributes
  mov qword [rsp+8h],rax
  mov rax, [rsp+70h] ; hTemplateFile
  mov qword [rsp+10h],rax
 
  ;obtain CreateFileW args 1,2,3,4
  mov rcx, [rsp+40h] ; lpFileName
  mov edx, 0C0000000h ; dwDesiredAccess
  mov r8, [rsp+50h] ; dwShareMode
  lea r9,[rsp+20h] ;lpSecurityAttributes
 
  sub rsp, 20h ; alloc homespace
  call PIT_CreateFileW
  mov [rsp+28h],rax ; store result
 

  ;free the security descriptor:
  mov rcx,[rsp+48h] ; sa.lpSecurityDescriptor
  call PIT_LocalFree ;LocalFree(sa.lpSecurityDescriptor)
  mov rax,[rsp+28h] ;restore result
  mov rdi,[rsp+68h] ;restore the global variable
  add rsp, 60h      ;restore stack
 
 code_end

patchlet_end
 


0day #2: the "readfile" (subsequently assigned
CVE-2019-0636)

[Update 2/14/2019: Microsoft has fixed this issue with February 2019 Updates and assigned it CVE-2019-0636.]

The "readfile" 0day was also published by SandboxEscaper last month. This one allows an unprivileged process running on a Windows computer to obtain the content of arbitrary file, even if permissions on such file don't allow it read access. The proof-of-concept demonstrates reading the content of another user's desktop.ini file from user's desktop, but the author suggests reading Office history files (and other index or history files with known paths) could reveal further paths to interesting files belonging to other users.

The flaw lies in Windows Installer, more specifically in its advertisement functionality, which can be triggered using the MsiAdvertiseProduct function. We're not going to dive into details here; suffice it to say that when a product (i.e., its MSI installation package) is advertised, Windows Installer (running as SYSTEM) takes this MSI file, parses it, then creates a temporary MSI file in C:\Windows\Installer folder and copies the content of the original MSI file in it. (It does much more with it then but that's irrelevant for this issue.) SandboxEscaper noticed that the original MSI file is opened twice, and that a symbolic link can be made that points to a regular MSI file on the first open, but points to some other file on the second open - which results in the content of the latter file being copied to the temporary MSI file. Due to file permissions on the temporary MSI file, which allow Everyone read access, an attacker can thus trick the Windows Installer Service to copy the content of arbitrary file to the temporary MSI file, and then read that file to obtain said content.

Micropatching this issue required us to familiarize ourselves with the inner working of Windows Installer. It turned out that the function copying the source MSI file into the temporary MSI file already supports setting two different permission sets on the temporary file: (1) inherited permissions, and (2) permissions of the source MSI file. Its behavior in advertising a product uses the inherited permissions (which allow the attacker to read the file), so we used four strategically-placed patchlets to force it to use permissions of the source MSI file instead. With the micropatch in place, product advertisement still works as before, but since the permissions on the temporary MSI file are the same as on the file being copied, the attacker gains nothing from this process.

We currently have this micropatch for fully updated 64-bit Windows 10 version 1803 and fully updated 64-bit Windows 7. (Contact support@0patch.com to express your interest in porting to other versions.)

You can see this micropatch in action in the following video.



And here's the source code for this micropatch; four patchlets, each with a single instruction.



MODULE_PATH "..\AffectedModules\msi.dll_5.0.17134.228_64bit.dll\msi.dll"
; Windows 10 version 1803
PATCH_ID 345
PATCH_FORMAT_VER 2
VULN_ID 4658
PLATFORM win64

patchlet_start

 PATCHLET_ID 1
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x002edb76 ; Injecting after eax is set to FlagsAndAttributes

                            ; in CMsiFileCopy::OpenDestination

 code_start

   or r12d, 0x8000 ; we set the 15th bit of FlagsAndAttributes,
                   ; which will cause the execution to flow towards using

                   ; source file's ACL

 code_end

patchlet_end


patchlet_start

 PATCHLET_ID 2
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x002edbe9 ; Overwriting code that checks value #12 in 

                            ; MSI record, and setting eax to 1
 JUMPOVERBYTES 20

 code_start

   mov eax, 1 ; we set eax to 1 to simulate IsNull returning false

 code_end

patchlet_end


patchlet_start

 PATCHLET_ID 3
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x002edd0e ; Overwriting code that checks value #12 in

                            ; MSI record, and setting eax to 1
 JUMPOVERBYTES 22

 code_start

   mov eax, 1 ; we set eax to 1 to simulate IsNull returning false

 code_end

patchlet_end


patchlet_start

 PATCHLET_ID 4
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x002edd61 ; Overwriting code that checks value #12 in 

                            ; MSI record, and setting eax to 1
 JUMPOVERBYTES 6

 code_start

   mov eax, 1 ; we set eax to 1 to simulate IsNull returning false

 code_end

patchlet_end
 



0day #3: the "Windows Contacts arbitrary code execution"


This 0day was published by a ZDI researcher John Page after Microsoft had exceeded ZDI's 90-day window for fixing a reported issue. The issue was initially reported as related to VCF files (which are by default associated with the Windows Contacts application) but Page subsequently added that CONTACT files (also by default associated with Windows Contacts) can be used to achieve the same.

The issue is in the fact that almost any string provided via a VCF or CONTACT file in the web site URL or email value (yes, we figured this one out ourselves :) ends up being used as an argument to a ShellExecute call. While ShellExecute is a handy function for opening URLs in user's default browser, its problem is that before doing that, it tries to "launch" the provided string on the local computer; to illustrate, provided with "www.microsoft.com", ShellExecute would first attempt to locate and launch a local executable called www.microsoft.com, and only failing that, it would open www.microsoft.com in your browser. This behavior has produced many a vulnerability before (see this article for some examples) and will undoubtedly continue to bless us with more in the future. To exploit this issue, the attacker must get the user to open a malicious VCF or CONTACT file and click on the displayed web site or email link, which launches attacker's executable that must also be present on user's computer or a network share.

We analyzed what happens when the web site or email link is clicked, which led us to a SafeExecute function in wab32.dll. This function does some parsing of the URL to accommodate mshelp:// URLs, and then calls ShellExecute. We simply added some logic before this call to make sure that if the URL doesn't start with "mailto:", "http://" or "https://", it gets prepended with "http://" to prevent any possible launching of local executables. We have initially considered adding sanitization in the code that reads the file but then discovered that the code displaying the link is prone to, yes, HTML injection - which made any sanitization too complex and potentially bypassable.

We currently have this micropatch for fully updated 64-bit Windows 10 version 1803 and fully updated 64-bit Windows 7. (Contact support@0patch.com to express your interest in porting to other versions.)

You can see this micropatch in action in the following video. The video also nicely demonstrates how a micropatch can be applied to a running process: we enable the micropatch while Windows Contacts is displaying the malicious contact card, and the result of clicking on the link gets changed.



And here's the source code for this micropatch.


;Micropatch for wab32.dll version 6.1.7601.17699
;
;How it works:
; in SafeExecute an unsanitized pszUrl - a user-controlled parameter is passed to ShellExecute.
; This patch adds a series of checks for valid pszUrl prefixes and eventually,
; if no valid prefix is found, adds a http:// prefix to pszUrl.

MODULE_PATH "..\AffectedModules\wab32.dll_6.1.7601.17699\wab32.dll"
PATCH_ID 347
PATCH_FORMAT_VER 2
VULN_ID 4656
PLATFORM win64

patchlet_start
PATCHLET_ID 1
PATCHLET_TYPE 2
PATCHLET_OFFSET 0x00087f7a
PIT SHLWAPI.dll!StrCmpNICW,msvcrt.dll!memmove
JUMPOVERBYTES 0
N_ORIGINALBYTES 1

 code_start
 

  ;store registers:
  push rsi
  push rcx
  push rdx
  push r8
  push r8                      ; extra space for prefix variable
  push rax                     ; store pszUrl
  ;check lpFile for valid prefixes:
  call mailto
  db __utf16__('mailto:'),0
  mailto:
  pop rcx                      ; pszStr1 = "mailto:"
  mov rdx, rax                 ; pszStr2 = pszUrl
  mov r8, 07h                  ; nChar
  sub rsp, 20h                 ; homespace
  call PIT_StrCmpNICW
  test rax,rax
  jz skip                      ; if found, exit patch
   call https
   db __utf16__('https://'),0
   https:
   pop rcx                     ; pszStr1 = "https://"
   mov rdx, [rsp+20h]          ; pszStr2 = pszUrl
   mov r8, 08h                 ; nChar
   call PIT_StrCmpNICW
   test rax,rax
  jz skip                      ; if found, exit patch
   call http
   db __utf16__('http://'),0
   http:
   pop rcx                     ; pszStr1 = "http://"
   mov [rsp+28h], rcx          ; store prefix
   mov rdx, [rsp+20h]          ; pszStr2 = pszUrl
   mov r8, 07h                 ; nChar
   call PIT_StrCmpNICW
   test rax,rax
  jz skip                      ; if found, exit patch
   mov r8, 1024h               ; num
   mov rdx, [rsp+20h]          ; Src = pszUrl
   lea rcx,[rdx+0eh]           ; Dst
   call PIT_memmove            ; moving pszUrl of max 1024h in size to *pszUrl+0eh
   mov rcx, [rsp+20h]          ; Dst = pszUrl
   mov rdx, [rsp+28h]          ; Src = "http://"
   mov r8, 0eh                 ; num
   call PIT_memmove            ; copying http:// to *pszUrl
  skip:
   ;revert stack:
   add rsp, 20h                ; revert homespace
   pop rax
   pop r8                      ; blank pop
   pop r8
   pop rdx
   pop rcx
   pop rsi
 

 code_end
 

patchlet_end
 


So here they are, three micropatches for three 0days. We don't know if and when Microsoft is going to fix these issues (likely in the following months); meanwhile, if you have our Agent installed and registered, these micropatches are already on your computer and applied to all affected processes. Otherwise, register a free 0patch account and install 0patch Agent to get these micropatches applied. As always, once Microsoft fixes any of these 0days, its associated micropatch will automatically stop applying - in other words, you don't have to worry about future Windows updates.

Again, please note that we haven't ported these micropatches to all supported Windows versions (see each section above for version information); if you're interested in patching your particular version of Windows, don't hesitate contacting us at support@0patch.com.

Cheers!

@mkolsek
@0patch