Pete Hinchley: How to Run a PowerShell Script when PowerShell.exe is Blocked by AppLocker

I recently needed to run a PowerShell script in an environment where PowerShell.exe was blocked by an AppLocker policy (it was deemed to be a security risk). In this environment, signing the script with a trusted code signing certificate, or moving the script to a "trusted location", wasn't an option, as the actual PowerShell executable required to run scripts was explicitly blocked.

In this particular environment, the AppLocker policy allowed users to execute programs that resided within certain trusted locations, such as c:\windows. I was therefore able to overcome the restriction by creating a custom program capable of running a PowerShell script without invoking PowerShell.exe. I just needed to copy the compiled code, which I named PowerSaw.exe to c:\windows, and from that point forward I was able to execute any PowerShell script.

Note: This technique could be thwarted by using AppLocker to specifically block System.Management.Automation.dll (which the custom program uses to invoke PowerShell scripts).

Here is the code:

using System.Management.Automation;
using System.Collections.ObjectModel;

namespace PowerSaw {
  class Program {
    static void Main(string[] args) {
      if (args.Length == 0) return;
      using (PowerShell PowerShellInstance = PowerShell.Create()) {
        string Script = System.IO.File.ReadAllText(args[0]);
        PowerShellInstance.AddScript(Script);
        Collection<PSObject> PSOutput = PowerShellInstance.Invoke();
      }
    }
  }
}

Note: If you intend to create a new Visual Studio project based on this code, you will need to add a project reference to System.Management.Automation.dll. The path to this file can be found by running the following command from a PowerShell console:

[PSObject].Assembly.Location

In my environment, this yielded:

C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll

To use the compiled executable (PowerSaw.exe) to run a PowerShell script named c:\scripts\foo.ps1, open a command prompt and enter:

powersaw.exe c:\scripts\foo.ps1

I haven't configured the program to return output from the invoked PowerShell script. This capability could be added, but typically you would just write the script so that output was sent to a log.

It's also worth noting that this technique will bypass AppLocker script policies. For example, an AppLocker policy that denies users from running scripts under c:\scripts will not allow:

powershell.exe -file c:\scripts\foo.ps1

However, the same script could be executed via PowerSaw.exe.

Similarly, PowerSaw.exe will bypass PowerShell execution policies. For example, if the machine execution policy is set to Restricted, you won't be able to run any script using PowerShell.exe, but you can using PowerSaw.exe.