Normal view

There are new articles available, click to refresh the page.
Before yesterdayExploit Monday

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

4 June 2018 at 11:41

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.

    Simple CIL Opcode Execution in PowerShell using the DynamicMethod Class and Delegates

    2 October 2013 at 00:09
    tl:dr version

    It is possible to assemble .NET methods with CIL opcodes (i.e. .NET bytecode) in PowerShell in only a few lines of code using dynamic methods and delegates.



    I’ll admit, I have a love/hate relationship with PowerShell. I love that it is the most powerful scripting language and shell but at the same time, I often find quirks in the language that consistently bother me. One such quirk is the fact that integers don’t wrap when they overflow. Rather, they saturate – they are cast into the next largest type that can accommodate them. To demonstrate what I mean, observe the following:


    You’ll notice that [Int16]::MaxValue (i.e. 0x7FFF) understandably remains an Int16. However, rather than wrapping when adding one, it is upcast to an Int32. Admittedly, this is probably the behavior that most PowerShell users would desire. I, on the other hand wish I had the option to perform math on integers that wrapped. To solve this, I originally thought that I would have to write an addition function using complicated binary logic. I opted not to go that route and decided to assemble a function using raw CIL (common intermediate language) opcodes. What follows is a brief explanation of how to accomplish this task.


    Common Intermediate Language Basics

    CIL is the bytecode that describes .NET methods. A description of all the opcodes implemented by Microsoft can be found here. Every time you call a method in .NET, the runtime either interprets its opcodes or it executes the assembly language equivalent of those opcodes (as a result of the JIT process - just-in-time compilation). The calling convention for CIL is loosely related to how calls are made in X86 assembly – arguments are pushed onto a stack, a method is called, and a return value is returned to the caller.

    Since we’re on the subject of addition, here are the CIL opcodes that would add two numbers of similar type together and would wrap in the case of an overflow:

    IL_0000: Ldarg_0 // Loads the argument at index 0 onto the evaluation stack.
    IL_0001: Ldarg_1 // Loads the argument at index 1 onto the evaluation stack.
    IL_0002: Add // Adds two values and pushes the result onto the evaluation stack.
    IL_0003: Ret // Returns from the current method, pushing a return value (if present) from the callee's evaluation stack onto the caller's evaluation stack.

    Per Microsoft documentation, “integer addition wraps, rather than saturates” when using the Add instruction. This is the behavior I was after in the first place. Now let’s learn how to build a method in PowerShell that uses these opcodes.


    Dynamic Methods

    In the System.Reflection.Emit namespace, there is a DynamicMethod class that allows you to create methods without having to first go through the steps of creating an assembly and module. This is nice when you want a quick and dirty way to assemble and execute CIL opcodes. When creating a DynamicMethod object, you will need to provide the following arguments to its constructor:

    1) The name of the method you want to create
    2) The return type of the method
    3) An array of types that will serve as the parameters

    The following PowerShell command will satisfy those requirements for an addition function:

    $MethodInfo = New-Object Reflection.Emit.DynamicMethod('UInt32Add', [UInt32], @([UInt32], [UInt32]))

    Here, I am creating an empty method that will take two UInt32 variables as arguments and return a UInt32.

    Next, I will actually implement the logic of the method my emitting the CIL opcodes into the method:

    $ILGen = $MethodInfo.GetILGenerator()
    $ILGen.Emit([Reflection.Emit.OpCodes]::Ldarg_0)
    $ILGen.Emit([Reflection.Emit.OpCodes]::Ldarg_1)
    $ILGen.Emit([Reflection.Emit.OpCodes]::Add)
    $ILGen.Emit([Reflection.Emit.OpCodes]::Ret)

    Now that the logic of the method is complete, I need to create a delegate from the $MethodInfo object. Before this can happen, I need to create a delegate in PowerShell that matches the method signature for the UInt32Add method. This can be accomplished by creating a generic Func delegate with the following convoluted syntax:

    $Delegate = [Func``3[UInt32, UInt32, UInt32]]

    The previous command states that I want to create a delegate for a function that accepts two UInt32 arguments and returns a UInt32. Note that the Func delegate wasn't introduced until .NET 3.5 which means that this technique will only work in PowerShell 3+. With that, we can now bind the method to the delegate:

    $UInt32Add = $MethodInfo.CreateDelegate($Delegate)

    And now, all we have to do is call the Invoke method to perform normal integer math that wraps upon an overflow:

    $UInt32Add.Invoke([UInt32]::MaxValue, 2)

    Here is the code in its entirety:


    For additional information regarding the techniques I described, I encourage you to read the following articles:

    Introduction to IL Assembly Language
    Reflection Emit Dynamic Method Scenarios
    How to: Define and Execute Dynamic Methods

    Reverse Engineering InternalCall Methods in .NET

    16 November 2013 at 19:52
    Often times, when attempting to reverse engineer a particular .NET method, I will hit a wall because I’ll dig in far enough into the method’s implementation that I’ll reach a private method marked [MethodImpl(MethodImplOptions.InternalCall)]. For example, I was interested in seeing how the .NET framework loads PE files in memory via a byte array using the System.Reflection.Assembly.Load(Byte[]) method. When viewed in ILSpy (my favorite .NET decompiler), it will show the following implementation:
     
     
    So the first thing it does is check to see if you’re allowed to load a PE image in the first place via the CheckLoadByteArraySupported method. Basically, if the executing assembly is a tile app, then you will not be allowed to load a PE file as a byte array. It then calls the RuntimeAssembly.nLoadImage method. If you click on this method in ILSpy, you will be disappointed to find that there does not appear to be a managed implementation.
     
     
    As you can see, all you get is a method signature and an InternalCall property. To begin to understand how we might be able reverse engineer this method, we need to know the definition of InternalCall. According to MSDN documentation, InternalCall refers to a method call that “is internal, that is, it calls a method that is implemented within the common language runtime.” So it would seem likely that this method is implemented as a native function in clr.dll. To validate my assumption, let’s use Windbg with sos.dll – the managed code debugger extension. My goal using Windbg will be to determine the native pointer for the nLoadImage method and see if it jumps to its respective native function in clr.dll. I will attach Windbg to PowerShell since PowerShell will make it easy to get the information needed by the SOS debugger extension. The first thing I need to do is get the metadata token for the nLoadImage method. This will be used in Windbg to resolve the method.
     
     
    As you can see, the Get-ILDisassembly function in PowerSploit conveniently provides the metadata token for the nLoadImage method. Now on to Windbg for further analysis…
     
     
    The following commands were executed:
     
    1) .loadby sos clr
     
    Load the SOS debugging extension from the directory that clr.dll is loaded from
     
    2) !Token2EE mscorlib.dll 0x0600278C
     
    Retrieves the MethodDesc of the nLoadImage method. The first argument (mscorlib.dll) is the module that implements the nLoadImage method and the hex number is the metadata token retrieved from PowerShell.
     
    3) !DumpMD 0x634381b0
     
    I then dump information about the MethodDesc. This will give the address of the method table for the object that implements nLoadImage
     
    4) !DumpMT -MD 0x636e42fc
     
    This will dump all of the methods for the System.Reflection.RuntimeAssembly class with their respective native entry point. nLoadImage has the following entry:
     
    635910a0 634381b0   NONE System.Reflection.RuntimeAssembly.nLoadImage(Byte[], Byte[], System.Security.Policy.Evidence, System.Threading.StackCrawlMark ByRef, Boolean, System.Security.SecurityContextSource)
     
    So the native address for nLoadImage is 0x635910a0. Now, set a breakpoint on that address, let the program continue execution and use PowerShell to call the Load method on a bogus PE byte array.
     
    PS C:\> [Reflection.Assembly]::Load(([Byte[]]@(1,2,3)))
     
    You’ll then hit your breakpoint in WIndbg and if you disassemble from where you landed, the function that implements the nLoadImage method will be crystal clear – clr!AssemblyNative::LoadImage
     
     
    You can now use IDA for further analysis and begin digging into the actual implementation of this InternalCall method!
     
     
    After digging into some of the InternalCall methods in IDA you’ll quickly see that most functions use the fastcall convention. In x86, this means that a static function will pass its first two arguments via ECX and EDX. If it’s an instance function, the ‘this’ pointer will be passed via ECX (as is standard in thiscall) and its first argument via EDX. Any remaining arguments are pushed onto the stack.
     
    So for the handful of people that have wondered where the implementation for an InternalCall method lies, I hope this post has been helpful.

    ❌
    ❌