Monday, November 2, 2020

UAC bypasses from COMAutoApprovalList

Intro

(This post is made with permission of Arush Agarampur - an original author of all methods described below). 

Here and below we assume Windows user account created by default with default settings. Windows COM object model is a complicated technology and is an essential part of almost every UAC bypass since Windows 7 release in 2009. The point of interest in this technology is a set of classes with enabled elevation, thus when you run interfaces based on them you can skip confirmation dialog from consent.exe (depending on UAC settings).

The definition of this elevation enabled class must look like this:

HKEY_CLASSES_ROOT\CLSID
   {CLSID}
       Elevation
          Enabled: 1

Live example:

Pic 1. SPPLUAObject Class.

These entries always exist only in HKEY_LOCAL_MACHINE (HKEY_CLASSES_ROOT is just a subkey of HKLM\Software) and during elevation process Windows expect them to be only in this hive. This automatically prevents users from elevating COM classes they did not have the privileges to register and prevent altering existing keys because of permissions set on these keys.

There is a few utilities that can help you walk through COM classes in the Windows. First is a very old tool from Microsoft - "OleView - OLE-COM Object Viewer" shipped together with Visual C++, and second is a relatively new tool from James Forshaw "OleView.NET", available with source at https://github.com/tyranid/oleviewdotnet 

Both are good enough for our task and I can suggest you try them all. Forshaw tool however has some features that especially very handy for our task and which MS OleView does not have.

What happens when elevated COM object created? 

There are few types of COM classes and they are handled differently. Here are the types that are used in this article described UAC bypass methods:

COM class that support elevation with linked dll that implements this class (it is specified in InProcServer32 registry subkey). A special application called DllHost.exe will be started with High IL and command line parameter /ProcessId:{CLSID} where CLSID belongs to requested COM class. Internally this is managed by combase.dll. This mechanic is called Dll Surrogate. The idea behind this is a very similar to that used in SCM with svchost.exe application instances hosting service dlls. In short - the requested code will be loaded in additional process and in case of critical failure this process will die leaving requestor alive and able to respond. In case of COM classes it also makes possible transparent elevation when your requestor code will still be running at Medium IL and COM code will run at High IL in separate process.

COM class that supports elevation and has LocalServer32 subkey set, which specifies the location of the COM server application to launch (parameter ServerExecutable). Server executable will have "-Embedding" parameter set as command line.

That is important part as some security researchers can assume that for elevation detection is enough inspect DllHost.exe parameters in logs. No, it is not sufficient indicator as it won't cover case above when default system-supplied surrogate is not used.

Pic 2. OleView.NET DllHost.exe surrogate listing.
 

Number of such autoelevated COM classes is a really big. Just a small list of them available in kernelmode.info archive Autoelevated COM objects, list (win7-win10). However starting from Windows 10 RS1 even if COM class has elevation ability enabled it doesn't mean it will be elevated. This innovation was a part of complex UAC update introduced in Redstone 1.  

Special COMAutoApprovalList was introduced (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UAC\COMAutoApprovalList). Location of this list prevents it from being edited without sufficient privileges.

Pic 3. COMAutoApprovalList as seen on Redstone 5.

It values are checked by consent.exe in the internal function named CuiIsCOMClassAutoApprovable called from CuiCheckElevationAutoApprovalMedium before processing COM class as autoelevated. This gives Windows ability to selectively ban autoelevation of arbitrary COM class without modification of existing registry keys and further impact on applications. Introduction of this list already blocked several UAC bypass methods based on autoelevated COM objects (UninstallStringLauncher and CreateNewLink). From the attacker perspective this save a lot of time validating existing UAC bypass methods. This list is also working as a starting point for exploring undiscovered UAC bypasses and few of them described next.

ActiveX Install Broker UAC bypass

The first method based on autoelevated COM object with CLSID {BDB57FF2-79B9-4205-9447-F5FE85F37312}, this is "Internet Explorer Add-on Installer" coclass. This COM object already has an exploitation backstory. In 2014 it was mentioned in the "Digging for Sandbox Escapes" at BlackHat 2014 USA by James Forshaw as part of attack surface in IE sandbox breakouts. It seems this particular usage was addressed by Microsoft later, however this COM object is still valuable for UAC bypass. This coclass contain several interfaces and from revese-engineering and behavior analysis two of them are of interest to us.

  1. IexAxiAdminInstaller
  2. IeAxiInstaller2

Pic 4. Internet Explorer Add-on Installer class interfaces.

This type of COM uses server executable that hosts code. In this case server executable is "C:\Program Files\Internet Explorer\IEInstal.exe" and exactly it will be started when instance of class will be created. These interfaces allows running specific file via RunSetupCommand method which is the part of IEAxiInstaller2 interface. This method is pretty much similar to RunSetupCommand documented on MSDN https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa768010(v=vs.85).

Thus main idea here is to force this method execute our file as "setup command", since this host application is elevated our code will also be elevated and UAC will be bypassed.

The full call chain following to the "setup command" execution is the following:

  1. Allocate elevated object of IEAxiAdminInstaller (CLSID: {BDB57FF2-79B9-4205-9447-F5FE85F37312}, IID: {9AEA8A59-E0C9-40F1-87DD-757061D56177}).
  2. Initialize admin installation by calling IEAxiAdminInstaller->InitializeAdminInstaller. This will give us unique string GUID that is used in further calls.
  3. Query IEAxiInstaller2 broker object with QueryInterface from IEAxiAdminInstaller. (IID: {BC0EC710-A3ED-4F99-B14F-5FD59FDACEA3})
  4. IEAxiInstaller2 calls VerifyFile method. This routine will copy file (which is given as method parameter) with slightly modified name to the newly created temp directory which has modify permissions granted to Administrators and resides inside current user temporary directory. Next it will do WinVerifyTrust over that file. If file is not signed by MS or something went wrong - method will return error. Note that signature must be embedded, as it seems this method won't check files signed via catalog.
  5. IEAxiInstaller2 calls RunSetupCommand with parameter set to previously validated file.

The problem here is that temporary directory created during VerifyFile call has modify permissions granted to the Administators so we can use another autoelevated COM interface - well known IFileOperation and alter data inside this folder. However we need to do this after VerifyFile call and before RunSetupCommand otherwise nothing will work. Also we cannot use RunSetupCommand to run something with advanced command line - Microsoft code validates command line and does not expects anything advanced in it, otherwise causing method to return failure. File to verify also must be signed by Microsoft with embedded signature. Here is how we can alter above call scheme and bypass UAC.

  1. Same as before.
  2. Same as before.
  3. Same as before.
  4. Call IEAxiInstaller2->VerifyFile on Microsoft binary from system32, something lightweight with embedded digital signature like for example our beloved consent.exe. It will be copied to temp folder with new name. Remember new name (for our example [1]consent.exe) and location (inside newly created temporary directory in user temp).
  5. Allocate IFileOperation object and use it to replace [1]consent.exe with our payload executable. Name must be the same -> [1]consent.exe.
  6. Run IEAxiInstaller2->RunSetupCommand with parameter set to the forged file.
  7. ...
  8. Profit.

Don't forget to kill temp directory after this method completion. This method implemented in UACMe v3.5.1 as #64. Original author implementation can be found in "References" at the end of this article.

Security Center UAC Bypass 

Second method based on autoelevated COM object with CLSID {E9495B87-D950-4AB5-87A5-FF6D70BF3E90}, this is Security Center and it uses default system-supplied surrogate DllHost.exe.

Pic 5. Security Center class.


IWscAdmin interface is of interest here. It has method called
DoModalSecurityAction which depending on supplied parameters calls ShellExecuteExW. This call is done with URL as parameter. In our case it is "http://go.microsoft.com/fwlink/?LinkId=534032". 

How this can be turned into fully working UAC bypass? Because we are dealing with over-complicated Windows Shell it is very simple but at first glance you can miss this opportunity. We can hijack shell protocol handler "http" for current user and force it use our payload thus instead of browser you will have our payload executed when ShellExecuteExW will be called with this URL as parameter.

Hijacking protocol handler is the main problem here. Before Windows 8 it is all simple - you just create protocol handler key and then modify HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice ProgId value with that name. With Windows 8 and above it is all complicated because protocol settings now encrypted and verified with a hash, so we cannot change it like before anymore. Personally, I don't really know purpose of this, was it result of "set %browser% as default app" wars or whatever, so this change for me is just annoying. In Windows 10 it is all now managed with help of SystemSettings application. Reverse engineering of this app in recent Windows 10 versions shows it uses call to SystemSettings.Handlers.dll!SetUserAssoc internal function to perform association. This suggest it can be used for our task. However to make it compatible with all Windows versions starting from 7 up to recent 10 it was required to dig deeper and locate the key function which is the same for all versions. 

And it was found, it is unexported shell32.dll function called UserAssocSet, it present in Windows at least since Windows XP. The SetUserAssoc function later pass it parameters to UserAssocSet call where this function looks like completely copy-pasted from shell32.dll to the SystemSettings.Handlers.dll.

Quick googling for that symbol revealed interesting article in Chinese which is covering detective story of finding and understand how all this brand new association works. Author(s) used it to register browser default app. Link to it included in the References at the end of this article. So final steps for this UAC bypass method are the following:

  1. Create registry protocol entry somewhere in HKEY_CURRENT_USER\Software\Classes.
  2. Query UserAssocSet function address from shell32.dll .text section using known patterns. 
  3. Register new association for "http" by calling UserAssocSet with first parameter set to UASET_PROGID.
  4. Allocate elevated object IWscAdmin (CLSID: {E9495B87-D950-4AB5-87A5-FF6D70BF3E90}, IID: {49ACAA99-F009-4524-9D2A-D751C9A38F60}).
  5. Call IWscAdmin->Initialize (it is required for interface to work as it contain some GUI stuff).
  6. Call IWscAdmin->DoModalSecurityAction with "action" parameter set to 103 to trigger ShellExecuteEx call.
  7. ...
  8. Profit.

During analysis of shell32 from number of Windows 10 builds it was found out that UserAssocSet is a subject of multiple internal changes, including number of parameters and functions from where this routine called. However first three parameters of it are always the same and generic prototype of this routine doesn't changed since Windows XP.

 HRESULT UserAssocSet(
    UASET set,
    LPCWSTR pszExt,
    LPCWSTR pszSet,
    [DWORD dwFlags]);

dwFlags parameter is variable and may present on some Windows 10 builds and not present on others. It is another enumeration of type ASSOC_MAKE_DEFAULT_FLAGS which complete definition is unknown and out of interest for us. However Windows 10 versions that has this parameter require it to be set to specific value which can be determinated with disassembler.

Association registration maybe removed later with another call of UserAssocSet this time with first parameter set to UASET_CLEAR. This UAC bypass method implemented in UACMe v3.5.2 as #65. It uses signature search method to locate UserAssocSet

In attempt to improve signature search I've looked on possibility of going out directly to internal routine that has this UserAssocSet call through IApplicationAssociationRegistration interface (https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-iapplicationassociationregistration). In the mentioned Chinese article author(s) used this interface to get to the internal one (called surprisingly as IApplicationAssociationRegistrationInternal) and use methods from it. By the way you can ignore what MSDN says about this interface limitations - MSDN information is a partial lie. Unfortunately Microsoft altered this interface heavily since this article publication. They not only changed interface ID but also redesigned methods so they does not have call to UserAssocSet now. I will leave this question to those who want to improve method.

P.S.

As you can see COM autoelevation objects are still have a lot of potential even 11 years after Windows 7 release. No doubt there are more surprises still need to be revealed as this is classic security through obscurity model.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.