Monday, June 4, 2018

Device Guard and Application Whitelisting on Windows - An Airing of Grievances

Introduction

The purpose of this post is to highlight many of the frustrations I’ve had with Device Guard (rebranded as Windows Defender Application Control) and to discuss why I think it is not an ideal solution for most enterprise scenarios at scale. I’ve spent several years now at this point promoting its use, making it as approachable as possible for people to adopt but from my perspective, I’m not seeing it being openly embraced either within the greater community or by Microsoft (from a public evangelism perspective). Why is that? Hopefully, by calling out the negative experiences I’ve had with it, we might be able to shed a light on what improvements can be made, whether or not further investments should be made in Device Guard, or if application whitelisting is even really feasible in Windows (in its current architecture) for the majority of customer use cases.

In an attempt to prove that I’m not just here to complain for the sake of complaining, here is a non-exhaustive list of blog posts and conference presentations I’ve given promoting Device Guard as a solution:

  • Introduction to Windows Device Guard: Introduction and Configuration Strategy
  • Using Device Guard to Mitigate Against Device Guard Bypasses
  • Windows Device Guard Code Integrity Policy Reference
  • Device Guard Code Integrity Policy Auditing Methodology
  • On the Effectiveness of Device Guard User Mode Code Integrity
  • Code Integrity on Nano Server: Tips/Gotchas
  • Updating Device Guard Code Integrity Policies
  • Adventures in Extremely Strict Device Guard Policy Configuration Part 1 — Device Drivers
  • The EMET Attack Surface Reduction Replacement in Windows 10 RS3: The Good, the Bad, and the Ugly
  • BlueHat Israel (presented with Casey Smith) - Device Guard Attack Surface, Bypasses, and Mitigations
  • PowerShell Conference EU - Architecting a Modern Defense using Device Guard and PowerShell

  • For me, the appeal of Device Guard (and application whitelisting in general) was and remains as follows: Every… single… malware report I read whether its vanilla crimeware, red team/pentester tools, or nation-state malware has at least one component of their attack chain that would have been blocked and subsequently logged with a robust application whitelisting policy enforced. The idea that a technology could not only prevent, but also supply indications and warnings of well-funded nation-state attacks is extremely enticing. In practice however, at scale (and even on single systems to a lesser extent), both the implementation of Device Guard and the overall ability of the OS to enforce code integrity (particularly in user mode) begin to fall apart.

    The Airing of Grievances



    Based on my extensive experience working with Device Guard (which includes regularly subverting it), here is what I see as its shortcomings:

    • An application whitelisting solution that does not supply the ability to create temporary exemptions is unlikely to be a viable solution in the enterprise. This point becomes clear when you consider the following scenario: A new, prospective or current client asks you to join their teleconferencing solution with 30 minutes notice. Telling them that you cannot join because your enforced security solution won’t permit it is simply an unacceptable answer. Some 3rd party whitelisting solutions do permit temporary, quick exceptions to policy and audit accordingly. As a Device Guard expert myself, even if every component of a software package is consistently signed using the same code signing certificate (which is extremely rare), even I wouldn’t be able to build signer rules, update an existing policy, and deploy it in time for the client conference call.
    • Device Guard is not designed to be placed into audit mode for the purposes of supplementing your existing detections. I recently completed a draft blog post where I was going to highlight the benefits of using Device Guard as an extremely simple and effective means to supplement existing detections. After writing the post however, I discovered that it will only log the loading of an image that would have otherwise been blocked once per boot. This is unacceptable from a threat detection perspective because it would introduce a huge visibility gap. I can only assume that Device Guard in audit mode was only ever designed to facilitate the creation of an enforcement policy.
    • The only interface to the creation and maintenance of Device Guard code integrity policies is the ConfigCI PowerShell module which only works on Windows 10 Enterprise. As not only a PowerShell MVP and a Device Guard expert, I shamefully still struggle with using this very poorly designed module. If I still struggle with using the module, this doesn’t bode well for non-PowerShell and Device Guard experts.
    • Feel free to highlight precisely why I’m wrong with supporting evidence but I sense I’m one of the few people outside of Microsoft or even inside Microsoft who have supplied documentation on practical use cases for configuring and deploying Device Guard. The utter absence of others within Microsoft or the community embracing Device Guard at least supplies me with indirect evidence that it not a realistic preventative solution at scale. I’ll further note that I don’t feel that Device Guard was ever designed from the beginning as an enterprise security solution. It has the feel that it simply evolved as an extension of Secure Boot policy from the Windows RT era.
    • While the servicing efforts for PowerShell constrained language mode have been mostly phenomenal, the servicing of other Device Guard bypasses has been inconsistent at best. For example, this generic bypass still has yet to be fixed. There is an undocumented “Enabled:Dynamic Code Security” policy rule option that is designed to address that bypass (which is great that it's finally being address) but it suffers from a bug that prevents it from working as of Win 10 1803 (it fails to validate the trust of the emitted binary because it forgets to actually mark it as trusted). Additionally, Casey Smith’s “SquiblyTwo” bypass was never serviced, opening the door for additional XSL-based bypasses (which I can confirm exist but I can’t talk about at the time of this writing). Rather, it is just recommended that you blacklist wmic.exe. There is also no robust method to block script-based bypasses.
    • The strategy with maintaining AppLocker moving forward remains ambiguous. AppLocker still benefits to this day by its ability to apply rulesets to user and groups, unlike Device Guard. It also has a slightly better PowerShell module and a GUI.
    • Any new features in Device Guard are consistently not documented aside from me occasionally diffing code integrity policy schemas across Windows builds. For example, one of the biggest recent feature additions is the “Enabled:Intelligent Security Graph Authorization” policy rule option which is the feature that actually transformed Device Guard from a pure whitelisting solution to that of an application control solution, yet, it has only a single line mentioning the feature in the documentation.
    • As far as application whitelisting on Windows is concerned, from a user-mode enforcement perspective, staying on top of blocking new, non-PE based code execution vectors remains an intractable problem. Whether it’s the introduction of code execution vectors (e.g. Windows Subsystem for Linux) or old code execution techniques being rediscovered (e.g. the fact that you can embed arbitrary WSH scripts in XSL docs). People like myself, Casey Smith, Matt Nelson, and many others in the industry recognize the inability of vendors and those implementing application whitelisting solutions to keep pace with blocking/detecting signed applications that permit the execution of arbitrary, unsigned code which fundamentally subvert user mode code integrity (UMCI). This is precisely what motivates us to continue our research in identifying those target applications/scripts.

    So what is Device Guard good for then?


    What I still love about Device Guard is that it’s the only solution that allows you to apply policy to kernel images (even in the very early boot phase). Regardless of the application whitelisting solution, user mode policy configuration, deployment, and maintenance is really difficult. The appeal of driver enforcement is that Windows requires that all drivers be signed, meaning, the creation of signer rules is relatively straightforward and the set of required drivers is far smaller than the set of required user mode code.

    Aside from that, I honestly see very little benefit in using Device Guard for user-mode enforcement or detection aside from using it on systems with extremely consistent hardware and software configurations - e.g. point of sale, ATMs, medical devices, etc.

    For the record, I still use Device Guard to enforce kernel and user mode rules on my personal computers. I still cringe, however, any time I have to make updates to my policy, particularly, for software that isn’t signed or is inconsistently signed.

    Are you admitting that you wasted the past few years dedicating much of your research time to Device Guard?


    Absolutely not!!! I try my best to invest in new security technologies as a motivation to research new abuse and subversion opportunities and Device Guard was no exception. It motivated me to take a deep dive into code signing and signature enforcement which resulted in me learning about and abusing all the internals of subject interface packages and trust providers. It also motivated me to identify and report countless Device Guard and PowerShell Constrained Language Mode bypasses all of which not only bypass application whitelisting solutions but represent attacker tradecraft that subvert many AV/EDR solutions.

    I also personally have a hard time blindly accepting the opinions of others (even those who are established, respected experts in their respective domains) without personally assessing the efficacy and limitations of a security solution myself. As a result of all my Device Guard research, I now have a very good sense of what does work and what doesn’t work in an application whitelisting solution. I am very grateful for the opportunity that Device Guard presented to motivate me to learn so much more about code signing validation.

    What I’m hopeful for in the future


    While I don’t see a lot of investment behind Device Guard compared to other security technologies (like Defender and Advanced Threat Protection), I sense that Microsoft is throwing a lot of their weight behind Windows Defender System Guard runtime attestation, some of the details of which are slowly starting to surface which I’m really excited about assuming the attestation rule engine is extended to 3rd parties. This tweet from Dave Weston I can only assume highlights System Guard in action blocking semi-legitimate signed drivers whereas a relatively simple Device Guard policy would have implicitly blocked those drivers.

    Conclusion


    My intent is certainly not to dissuade people from assessing the feasibility of Device Guard in your respective environment. Rather, I want to be as open and transparent about the issues I’ve encountered over the years working with it. My hope is to ignite an open and honest conversation about how application whitelisting in Windows can be improved or if it’s even a worthwhile investment in the first place.

    As a final note, I want to encourage everyone to dive as deep as you can into technology you’re interested in. There are a lot (I can’t emphasize “a lot” enough) of curmudgeons and detractors who will tell you that you’re wasting your time. Don’t listen to them. Only you (and trusted mentors) should dictate the path of your curiosity! I may no longer be the zealous proponent of application whitelisting that I used to be but I could not be more grateful for the incredible technology Microsoft gave me the opportunity to dive into, upon which, I was able to draw my own conclusions.

    Tuesday, August 29, 2017

    Exploiting PowerShell Code Injection Vulnerabilities to Bypass Constrained Language Mode

    Introduction


    Constrained language mode is an extremely effective method of preventing arbitrary unsigned code execution in PowerShell. It’s most realistic enforcement scenarios are when Device Guard or AppLocker are in enforcement mode because any script or module that is not approved per policy will be placed in constrained language mode, severely limiting an attackers ability to execute unsigned code. Among the restrictions imposed by constrained language mode is the inability to call Add-Type. Restricting Add-Type makes sense considering it compiles and loads arbitrary C# code into your runspace. PowerShell code that is approved per policy, however, runs in “full language” mode and execution of Add-Type is permitted. It turns out that Microsoft-signed PowerShell code calls Add-Type quite regularly. Don’t believe me? Find out for yourself by running the following command:

    ls C:\* -Recurse -Include '*.ps1', '*.psm1' |
      Select-String -Pattern 'Add-Type' |
      Sort Path -Unique |
      % { Get-AuthenticodeSignature -FilePath $_.Path } |
      ? { $_.SignerCertificate.Subject -match 'Microsoft' }

    Exploitation


    Now, imagine if the following PowerShell module code (pretend it’s called “VulnModule”) were signed by Microsoft:

    $Global:Source = @'
    public class Test {
        public static string PrintString(string inputString) {
            return inputString;
        }
    }
    '@

    Add-Type -TypeDefinition $Global:Source

    Any ideas on how you might influence the input to Add-Type from constrained language mode? Take a minute to think about it before reading on.

    Alright, let’s think the process through together:
    1. Add-Type is passed a global variable as its type definition. Because it’s global, its scope is accessible by anyone, including us, the attacker.
    2. The issue though is that the signed code defines the global variable immediately prior to calling to Add-Type so even if we supplied our own malicious C# code, it would just be overwritten by the legitimate code.
    3. Did you know that you can set read-only variables using the Set-Variable cmdlet? Do you know what I’m thinking now?

    Weaponization


    Okay, so to inject code into Add-Type from constrained language mode, an attacker needs to define their malicious code as a read-only variable, denying the signed code from setting the global “Source” variable. Here’s a weaponized proof of concept:

    Set-Variable -Name Source -Scope Global -Option ReadOnly -Value @'
    public class Injected {
        public static string ToString(string inputString) {
            return inputString;
        }
    }
    '@

    Import-Module VulnModule

    [Injected]::ToString('Injected!!!')

    A quick note about weaponization strategies for Add-Type injection flaws. One of the restrictions of constrained language mode is that you cannot call .NET methods on non-whitelisted classes with two exceptions: properties (which is just a special “getter” method) and the ToString method. In the above weaponized PoC, I chose to implement a static ToString method because ToString permits me to pass arguments (a property getter does not). I also made my class static because the .NET class whitelist only applies when instantiating objects with New-Object.

    So did the above vulnerable example sound contrived and unrealistic? You would think so but actually Microsoft.PowerShell.ODataAdapter.ps1 within the Microsoft.PowerShell.ODataUtils module was vulnerable to this exact issue. Microsoft fixed this issue in either CVE-2017-0215, CVE-2017-0216, or CVE-2017-0219. I can’t remember, to be honest. Matt Nelson and I reported a bunch of these injection bugs that were serviced by the awesome PowerShell team.

    Prevention


    The easiest way to prevent this class of injection attack is to supply a single-quoted here-string directly to -TypeDefinition in Add-Type. Single quoted string will not expand any embedded variables or expressions. Of course, this scenario assumes that you are compiling static code. If you must supply dynamically generated code to Add-Type, be exceptionally mindful of how an attacker might influence its input. To get a sense of a subset of ways to influence code execution in PowerShell watch my “Defensive Coding Strategies for a High-Security Environment” talk that I gave at PSConf.EU.

    Mitigation


    While Microsoft will certainly service these vulnerabilities moving forward, what is to prevent an attacker from bringing the vulnerable version along with them?

    A surprisingly effective blacklist rule for UMCI bypass binaries is the FileName rule which will block execution based on the filename present in the OriginalFilename field within the “Version Info” resource in a PE. A PowerShell script is obviously not a PE file though - it’s a text file so the FileName rule won’t apply. Instead, you are forced to block the vulnerable script by its file hash using a Hash rule. Okay… what if there is more than a single vulnerable version of the same script? You’ve only blocked a single hash thus far. Are you starting to see the problem? In order to effectively block all previous vulnerable versions of the script, you must know all hashes of all vulnerable versions. Microsoft certainly recognizes that problem and has made a best effort (considering they are the ones with the resources) to scan all previous Windows releases for vulnerable scripts and collect the hashes and incorporate them into a blacklist here. Considering the challenges involved in blocking all versions of all vulnerable scripts by their hash, it is certainly possible that some might fall through the cracks. This is why it is still imperative to only permit execution of PowerShell version 5 and to enable scriptblock logging. Lee Holmes has an excellent post on how to effectively block older versions of PowerShell in his blog post here.

    Another way in which a defender might get lucky regarding vulnerable PowerShell script blocking is due to the fact that most scripts and binaries on the system are catalog signed versus Authenticode signed. Catalog signed means that rather than the script having an embedded Authenticode signature, its hash is stored in a catalog file that is signed by Microsoft. So when Microsoft ships updates, eventually, hashes for old versions will fall out and no longer remain “signed.” Now, an attacker could presumably also bring an old, signed catalog file with them and insert it into the catalog store. You would have to be elevated to perform that action though and by that point, there are a multitude of other ways to bypass Device Guard UMCI. As a researcher seeking out such vulnerable scripts, it is ideal to first seek out potentially vulnerable scripts that have an embedded Authenticode signature as indicated by the presence of the following string - “SIG # Begin signature block”. Such bypass scripts exist. Just ask Matt Nelson.

    Reporting


    If you find a bypass, report it to secure@microsoft.com and earn yourself a CVE. The PowerShell team actively addresses injection flaws, but they are also taking making proactive steps to mitigate many of the primitives used to influence code execution in these classes of bug.

    Conclusion


    While constrained language mode remains an extremely effective means of preventing unsigned code execution, PowerShell and it’s library of signed modules/scripts remain to be a large attack surface. I encourage everyone to seek out more injection vulns, report them, earn credit via formal MSRC acknowledgements, and make the PowerShell ecosystem a more secure place. And hopefully, as a writer of PowerShell code, you’ll find yourself thinking more often about how an attacker might be able to influence the execution of your code.

    Now, everything that I just explained is great but it turns out that any call to Add-Type remains vulnerable to injection due to a design issue that permits exploiting a race condition. I really hope that continuing to shed light on these issues, Microsoft will considering addressing this fundamental issue.