How to Port Microsoft.Workflow.Compiler.exe Loader to Veil
Immediately after reading Matt Graeber’sblog 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 he outlines in his post, you can take essentially any C# code and execute it with Microsoft.Workflow.Compiler.exe.
Setting up the payload
At this point, I wanted to use known good C# code that I know would work. In my case, the tool I trust and work on is Veil, because I know that the code that it outputs works. In fact, 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. Here, I wanted to start with a base C# shellcode injection payload that doesn’t obfuscate its own source code, in case i need 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.
Testing the payload
After following the remainder of the Veil payload generation process, I received the C# output. The first thing I wanted to test is if this payload would work out of the box. Following Matt’s post, I first properly created an XML file to pass-in to Microsoft.Workflow.Compiler.exe and created a Cobalt Strike listener to accept the callback. I then 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 the Veil output would work out of the box. At this point, I reviewed the original POC that was published for more details and 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 to 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 removing 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 are interested in more posts like this, you can find them here and if you have any questions, please don’t hesitate to contact us at FortyNorth Security.