Immediately after reading Matt Graeber’s blog post on Microsoft.Workflow.Compiler.exe, I wanted to dive into this technique and understand how I can use this on an assessment. Matt wrote an extremely detailed blog post which allowed me to modify what he documented for my purposes. As documented in Matt’s post, you can take essentially any C# code and execute it with Microsoft.Workflow.Compiler.exe.
At this point, I wanted to use known good C# code that I know would work. In my case, I trust a tool that I work on: Veil. I know that the code that Veil outputs works, so I generally like to use this as a base if I want to develop a POC for a stager of some sort in various languages. In this case, I want to start with a base C# shellcode injection payload that doesn’t obfuscate its own source code in case i needed to debug the code. This is easily done by selecting the “cs/shellcode_inject/virtual.py” payload.
After selecting the virtual payload, I set the “COMPILE_TO_EXE” option to “N”, this will instruct Veil to only return the source code output.
After following the remainder of the Veil payload generation process, you will receive the C# output. The first thing I wanted to test was if this payload would work out of the box. After properly creating an XML file to pass in to Microsoft.Workflow.Compiler.exe, as documented in Matt’s post, and creating a Cobalt Strike listener to accept the callback, I tried to run the Veil output and received the following result:
Needless to say, I didn’t receive a callback. I assumed as much, but wanted to see if it Veil output would work out of the box. At this point, I needed to review the original POC that was published for more details. I noticed a bunch of differences:
- The class and method were both the same name (“Foo”)
- No namespace was declared in the original proof of concept
- The base C# code included “using System.Workflow.Activities”
- The method was a public method that did not have a void return
I modified the Veil C# output to reflect the above changes, as shown below:
I tried calling this C# code again, and received the following:
This was both a great sign, and a bad sign. My first thought was that the Veil C# shellcode was actually being invoked, however it was crashing. So my next thought was that I was running into an architecture issue. The first modification I wanted to make was to swap the shellcode out from Veil’s original output with x64 shellcode. Thankfully, Cobalt Strike lets us easily generate x86 and x64 shellcode, which is exactly what I did.
The only modification that I made from the Cobalt Strike output is I removed all extra data, such as the datatype declaration, size, comments, and spacing between bytes in the byte array. I then swapped the modified Cobalt Strike output with the Veil shellcode, and attempted to run the Microsoft.Workflow.Compiler.exe again. This time, the result was something different:
I hope this helps document the process that we took along handling the issues we encountered. If you have any questions, please don’t hesitate to contact us at FortyNorth Security.