Monday, July 10, 2017

Bypassing Device Guard with .NET Assembly Compilation Methods

Tl;dr

This post will describe a Device Guard user mode code integrity (UMCI) bypass (or any other application whitelisting solution for that matter) that takes advantage of the fact the code integrity checks are not performed on any code that compiles C# dynamically with csc.exe. This issue was reported to Microsoft on November 14, 2016. Despite all other Device Guard bypasses being serviced, a decision was made to not service this bypass. This bypass can be mitigated by blocking csc.exe but that may not be realistic in your environment considering the frequency in which legitimate code makes use of these methods - e.g. msbuild.exe and many PowerShell modules that call Add-Type.

Introduction

When Device Guard enforces user mode code integrity (UMCI), aside from blocking non-whitelisted binaries, it also only permits the execution of signed scripts (PowerShell and WSH) approved per policy. The UMCI enforcement mechanism in PowerShell is constrained language mode. One of the features of constrained language mode is that unsigned/unapproved scripts are prevented from calling Add-Type as this would permit arbitrary code execution via the compilation and loading of supplied C#. Scripts that are approved per Device Guard code integrity (CI) policy, however, are under no such restrictions, execute in full language mode, and are permitted to call Add-Type. While investigating Device Guard bypasses, I considered targeting legitimate, approved calls to Add-Type. I knew that the act of calling Add-Type caused csc.exe – the C# compiler to drop a .cs file to %TEMP%, compile it, and load it. A procmon trace of PowerShell calling Add-Type confirms this:

Process Name Operation  Path
------------ ---------  ----
csc.exe      CreateFile C:\Users\TestUser\AppData\Local\Temp\bfuswtq5.cmdline
csc.exe      CreateFile C:\Users\TestUser\AppData\Local\Temp\bfuswtq5.0.cs
csc.exe      CreateFile C:\Users\TestUser\AppData\Local\Temp\CSC3FBE068FE0A4C00B4A74B718FAE2E57.TMP
csc.exe      CreateFile C:\Users\TestUser\AppData\Local\Temp\CSC3FBE068FE0A4C00B4A74B718FAE2E57.TMP
csc.exe      CreateFile C:\Users\TestUser\AppData\Local\Temp\RES1A69.tmp
cvtres.exe   CreateFile C:\Users\TestUser\AppData\Local\Temp\CSC3FBE068FE0A4C00B4A74B718FAE2E57.TMP
cvtres.exe   CreateFile C:\Users\TestUser\AppData\Local\Temp\RES1A69.tmp
csc.exe      CreateFile C:\Users\TestUser\AppData\Local\Temp\RES1A69.tmp
csc.exe      CreateFile C:\Users\TestUser\AppData\Local\Temp\RES1A69.tmp
csc.exe      CreateFile C:\Users\TestUser\AppData\Local\Temp\bfuswtq5.dll
csc.exe      CreateFile C:\Users\TestUser\AppData\Local\Temp\CSC3FBE068FE0A4C00B4A74B718FAE2E57.TMP

Upon seeing these files created, I asked myself the following questions:
  1. Considering an approved (i.e. whitelisted per policy) PowerShell function is permitted to call Add-Type (as many Microsoft-signed module functions do), could I possibly replace the dropped .cs file with my own? Could I do so quickly enough to win that race?
  2. How is the .DLL that’s created loaded? Is it subject to code integrity (CI) checks?

Research methodology
Let’s start with the second question since exploitation would be impossible if CI would prevent the loading of a hijacked, unsigned DLL. To answer this question, I needed to determine what .NET methods were called upon Add-Type being called. This determination was relatively easy by tracing method calls in dnSpy. I quickly traced execution of the following .NET methods:
Once the Microsoft.CSharp.CSharpCodeGenerator.Compile method is called, this is where csc.exe is ultimately invoked. After the Compile method returns, FromFileBatch takes the compiled artifacts, reads them in as a byte array, and then loads them using System.Reflection.Assembly.Load(byte[], byte[], Evidence). This is the same method called by msbuild.exe when compiling inline tasks – a known Device Guard UMCI bypassed discovered by Casey Smith. Knowing this, I gained the confidence that if I could hijack the dropped .cs file, I would end up having a constrained language mode bypass, allowing arbitrary unsigned code execution. What we’re referring to here is known as a “time of check time of use” (TOCTOU) attack. If I could manage to replace the dropped .cs file with my own prior to csc.exe consuming it, then I would win that race and perform the bypass. The only constraints imposed on me, however, would be that I would need to write a hijack payload within the constraints of constrained language mode. As it turns out, I was successful.

Exploitation

I wrote a function called Add-TypeRaceCondition that will accept attacker-supplied C# and get an allowed call to Add-Type to compile it and load it within the constraints of constrained language mode. The weaponized bypass is roughly broken down as follows:
  1. Spawn a child process of PowerShell that constantly tries to drop the malicious .cs file to %TEMP%.
  2. Maximize the process priority of the child PowerShell process to increase the likelihood of winning the race.
  3. In the parent PowerShell process, import a Microsoft-signed PowerShell module that calls Add-Type – I chose the PSDiagnostics process for this.
  4. Kill the child PowerShell process.
  5. At this point, you will have likely won the race and your type will be loaded in place of the legitimate one expected by PSDiagnostics.
In reality, the payload wins the race a little more than 50% of the time. If Add-TypeRaceCondition doesn’t work on the first try, it will almost always work on the second try.

Do note that while I weaponized this bypass for PowerShell, this can be weaponized using anything that would allow you to overwrite the dropped .cs file quickly enough. I've weaponized the bypass using a batch script, VBScript, and with WMI. I'll leave it up to the reader to implement a bypass using their language of choice.

Operational Considerations

It's worth noting that while an application whitelisting bypass is just that, it also serves as a method of code execution that is likely to evade defenses. In this bypass, an attacker need only drop a C# file to disk which results in the temporary creation of a DLL on disk which is quickly deleted. Depending upon the payload used, some anti-virus solutions with real-time scanning enabled could potentially have the ability to quarantine the dropped DLL before it's consumed by System.Reflection.Assembly.Load.

Prevention

Let me first emphasize that this is a .NET issue, not a PowerShell issue. PowerShell was simply chosen as a convenient means to weaponize the bypass. As I’ve already stated, this issue doesn’t just apply to when PowerShell calls Add-Type, but when any application calls any of the CodeDomProvider.CompileAssemblyFrom methods. Researchers will continue to target signed applications that make such method calls until this issue is mitigated.

A possible user mitigation for this bypass would be to block csc.exe with a Device Guard rule. I would personally advise against this, however, since there are many legitimate Add-Type calls in PowerShell and presumably in other legitimate applications. I’ve provided a sample Device Guard CI rule that you can merge into your policy if you like though. I created the rule with the following code:

# Copy csc.exe into the following directory
# csc.exe should be the only file in this directory.
$CSCTestPath = '.\Desktop\ToBlock\'
$PEInfo = Get-SystemDriver -ScanPath $CSCTestPath -UserPEs -NoShadowCopy

$DenyRule = New-CIPolicyRule -Level FileName -DriverFiles $PEInfo -Deny
$DenyRule[0].SetAttribute('MinimumFileVersion', '65535.65535.65535.65535')

$CIArgs = @{
    FilePath = "$($CSCTestPath)block_csc.xml"
    Rules = $DenyRule
    UserPEs = $True
}

New-CIPolicy @CIArgs

Detection

Unfortunately, detection using free, off-the-shelf tools will be difficult due to the fact that the disk artifacts are created and subsequently deleted and by the nature of System.Reflection.Assembly.Load(byte[]) not generating a traditional module load event that something like Sysmon would be able to detect.

Vendors with the ability to hash files on the spot should consider assessing the prevalence of DLLs created by csc.exe. Files with low prevalence should be treated as suspicious. Also, unfortunately, since dynamically created DLLs by their nature will not be signed, there will be no code signing heuristics to key off of.

It's worth noting that I intentionally didn't mention PowerShell v5 ScriptBlock logging as a detection option since PowerShell isn't actually required to achieve this bypass.

Conclusion

I remain optimistic of Device Guard’s ability to enforce user mode code integrity. It is a difficult problem to tackle, however, and there is plenty of attack surface. In most cases, Device Guard UMCI bypasses can be mitigated by a user in the form of CI blacklist rules. Unfortunately, in my opinion, no realistic user mitigation of this particular bypass is possible. Microsoft not servicing such a bypass is the exception and not the norm. Please don’t let this discourage you from reporting any bypasses that you may find to secure@microsoft.com. It is my hope that by releasing this bypass that it will eventually be addressed and it will provide other vendors with the opportunity to mitigate.

Previously serviced bypasses for reference:

Thursday, January 5, 2017

PowerShell is Not Special - An Offensive PowerShell Retrospective

“PowerShell is not special.”

During Jared Haight’s excellent DerbyCon presentation, he uttered this blasphemous sentence. As someone who has invested the last five years of his life learning and mastering PowerShell, at a surface level, it was easy to dismiss such a claim. However, I’ve done a lot of introspection about my investment in offensive PowerShell and the more I thought about it, the more I began to realize that PowerShell really isn’t that special! Before you bring out the torches and pitchforks, allow me apply context.

My first exposure to PowerShell was from Dave Kennedy and Josh Kelley during their DEF CON presentation – PowerShell OMFG. Initially, I considered PowerShell to be amusing from a security perspective. I was just getting my start in infosec, however, and I had a lot of other things that I needed to focus on. Not long after that talk, Chris Campbell (@obscuresec) then took a keen interest in PowerShell and heavily advocated that we start using it on our team. My obsession for PowerShell wasn’t solidified until I realized that it could be used as a shellcode runner. When I realized that there really wasn’t anything PowerShell couldn’t do, my interest in and promotion of offensive PowerShell was truly realized.

For years, I did my part in developing unique offensive capabilities in PowerShell to the approval of many in the community and to the disappointment of defenders and employees of Microsoft. At the time, their disappointment and frustration was justified to an extent. When I started writing offensive PowerShell code, v3 hadn’t been released so the level of detection was laughable. Fast forward to now – PowerShell v5 (which is available downlevel to Windows 7). I challenge anyone to identify a single language – scripting, interpreted, compiled, or otherwise that has better logging than PowerShell v5. Additionally, if defenders choose to employ whitelisting to enforce trusted PowerShell code, both AppLocker and Device Guard do what many still (mistakenly) believe the execution policy was intended to do – actually perform signature enforcement of PowerShell code.

While PowerShell has become extremely popular amongst pentesters, red-teamer, criminals, and state-sponsored actors, let’s not forget that we’re still getting compromised by compiled payloads every... freaking... day. PowerShell really is just a means to an end in achieving an attacker’s objective - now at the cost of generating significant noise with the logging offered by PowerShell v5. PowerShell obviously offers many distinct advantages for attackers that I highlighted years ago but defenders and security vendors are slowly but surely catching up with detecting PowerShell attacks. Additionally, with the introduction of AMSI, for all of its flaws, we now have AV engines that can scan arbitrary buffers in memory.

So in the context of offense, this is why I say that PowerShell really isn’t special. Defenders truly are armed with the tools they need to detect and mitigate against PowerShell attacks. So the next time you find yourself worrying about PowerShell attacks, make sure you’re worrying equally, if not more about every other kind of payload that could execute on your system. Don’t be na├»ve, however, and write PowerShell off as a “solved problem.” There will always continue to be innovative bypass/evasion research in the PowerShell space. Let’s continue to bring this to the public’s attention and the community will continue to benefit from the fruits of offensive and defensive research.

References for securing/monitoring PowerShell: