In this post, I will describe a Device Guard User Mode Code Integrity (UMCI) bypass I submitted to Microsoft in February. It has now been added as a deny rule to the bypass policy in the Device Guard deployment guide. For more context about Device Guard refer to the documentation or my last posts.
This bypass is a very simple case of a Microsoft signed executable that runs code without being aware of Constrained Language Mode or other UMCI concepts. This executable is easy to spot if you go looking for bypasses. I found it by listing all the Microsoft signed executables on my host and playing around with the ones that looked interesting. The simplicity of the bypass also demonstrates that when your Device Guard policy trusts every executable signed by Microsoft, you have quite an extensive attack surface. Microsoft produces a lot of software, and every developer might not be aware of their role in maintaining the Device Guard security perimeter. A lot of UMCI bypasses are not because of faulty code, as they do not violate any of the classic security concepts. Often, they just run other code by design.
The executable is called powershellcustomhost.exe, was installed by Visual Studio 2018, and can be found here:
C:\Program Files\IIS\Microsoft Web Deploy V3\Scripts\powershellcustomhost.exe
I haven’t looked into its actual purpose, but for the purpose of bypassing Device Guard, it serves as a very clean example.
Executing the application does not lead to any insights about its intended purpose as no usage information is presented:
But decompiling, with dnspy for example, shows us both the missing usage information;
And the main functionality:
As seen in the decompilation, the application takes a list of text files and runs them as Powershell scripts, bypassing the Constrained Language Mode that applies when running scripts with Powershell.exe. As a result, it is easy to use this executable to run Powershell that executes arbitrary .NET code or loads unsigned DLL’s, for example with Invoke-ReflectivePEInjection from PowerSploit.
After I reported this to Microsoft, the executable was added to the bypass policy at Microsoft recommended block rules. The bypass policy contains a deny rule for all known executables and scripts signed by Microsoft that allow an attacker to bypass a Device Guard policy. This bypass policy is intended by Microsoft to be merged with your own policy before deploying Device Guard. This ensures that policies including this current version of the bypass list will not be vulnerable to this bypass.
The problem with this approach is that already deployed policies are not affected by additions to the bypass policy. New techniques are therefore usable on all deployed Device Guard policies until they are merged with the new edition of the bypass policy and redeployed. New bypass executables are discovered regularly, so an administrator responsible for the deployment of Device Guard needs to check up on the bypass policy at regular intervals and adjust and redeploy policies accordingly. This is not something I expect to be the current practice.
I'm not quite done with Device Guard, and in my next post, I am going to show a bypass that uses a less obvious executable to run code.
Signed Microsoft binaries running code are always nice candidates for persisting on a compromised host. Since this executable does not have an easily recognizable icon or name like Powershell.exe, it might slip by a cursory or automatic scan of installed binaries. By using a script file with a name resembling a command line option, those pesky incident responders will have a harder time spotting this installed backdoor. The main problem is the descriptive name of the executable, something an alert incident responder would be triggered by. Also, since it is not a core Windows binary, it will be signed by "Microsoft Corporation" and not "Microsoft Windows", a trait only shared with a few other applications installed by default, like Windows Defender and OneDrive.
You can bypass Device Guard User Mode Code Integrity with the Microsoft signed executable powershellcustomhost.exe.