F#ck da Antivirus! How to bypass antiviruses during pentest

Antiviruses are extremely useful tools – but not in situations when you need to remain unnoticed on an attacked network. Today, I will explain how to fool antivirus programs and avoid detection in compromised systems during penetration testing.

No doubt you had encountered situations when access to the attacked network is gained and just one little step separates you from the much-desired goal – but the antivirus does not allow you to make this step (e.g. prevents you from launching the required program). In many cases, you have only one attempt – and it fails because of the alarm raised by the antivirus. Sometimes antiviruses delete absolutely harmless files, not only executable ones. As a result, it becomes extremely difficult to conceal the real threat.


All information provided in this article is intended for educational purposes only. Neither the author nor the Editorial Board can be held liable for any damages caused by improper usage of this publication.

You may need to bypass antivirus software in two situations:

  • during an attack. In such cases, the payload is run either by a vulnerable application or, more often, by the user (social engineering). In most cases, this gives you a foothold and ensures your persistence in the system. The most important thing is to avoid detection; or 
  • during post-exploitation. In this case, it is you who runs the program on the compromised PC. It may be a sniffer, a privilege escalator, or other hacking software used to advance you through the network. For the post-exploitation purposes, the program has to be run by all means – even if you failed to launch it on the first try and the antivirus has raised several alarms.

In the first situation, you can use a well-known technique called “polymorphism”. Just make a few changes in the code without changing its functionality. A good tactics is to write the code yourself. For instance, you can implement a simple reverse shell using twenty strings of VBS code and successfully bypass any antivirus. The second case is more interesting and difficult; accordingly, it will be the main topic of this article.

Many useful tools allowing to bypass the antivirus can be created using Meterpreter, but first you need to run it on the target system. In each situation discussed below, your ultimate goal is to launch Meterpreter. This tool has all the required capabilities; if necessary, it can be used to run other specialized software directly in RAM. And everything that happens in RAM is virtually beyond the control of protection tools: a detailed and continuous memory analysis requires colossal computing costs that antiviruses cannot afford.

To bypass an antivirus, you have to think like an antivirus and try to predict its logic. Consider it a black box. You don’t know in detail how it works, but based on its behavior in different situations, you can conclude that it carefully monitors the sources from where you download executable files and also monitors whether these files were launched before (and if yes, how many times). Repeated launches of the same file will attract less attention from the antivirus.

Generally speaking, you have to deal with two protection mechanisms:

  • signature-based; and 
  • heuristic (behavioral) ones.

In signature analysis, the antivirus takes into account many factors, including the compiler. I scanned a harmless helloworld app written in C on VirusTotal, and you can see the results below.

  • i686-w64-mingw32-gcc – 11/68 detects;
  • msvc – 2/64 detects;
  • win-gcc – 0 detects.

With regards to the program’s behavior, keep in mind that even if you managed to bypass the signatures, you can still be detected because any migrate PID or sekurlsa::logonPasswords can be intercepted due to the use of typical combinations of WinAPI functions closely monitored by antiviruses.

I suggest focusing on the signature engine since, in most situations, it’s sufficient to bypass it. I purposively don’t mention any specific antiviruses in this article to avoid advertising (or anti-advertising) them. The techniques presented below aren’t designed against any specific AV program. The results will be verified using a running antivirus; the goal is to develop a universal method so that you don’t have to invent new bypass tricks every time. In each situation, your task is to covertly smuggle Meterpreter onto the compromised machine; then it will enable you to execute anything in memory and unleash the entire hacking arsenal at your disposal.


The best fight is the one that was avoided. Therefore, hackers and pentesters often use perfectly legitimate means against antiviruses. Yes, they don’t support many ‘advanced’ functions, but the minimum required functionality such tools provide (i.e. reverse shell with persistence, lateral movement, and a built-in proxy server with a flexible traffic redirection system for pivoting). The utilities are commonly used, well-known, and efficient: nc.exe, ncat.exe, socat.exe, and plink.exe. They are addressed in detail in my previous articles. Furthermore, Black Hats sometimes use common cloud-based remote administration tools (like RMS).

If you have to deploy a fully featured foothold in the compromised system (i.e. Metasploit and similar hacking tools), you can hide behind virtualization. A step-by-step guide is provided in my another article.

Shellcode injection

This well-known technique involves the injection of code into a running process that has already passed the testing. The idea is that the program shellcode_inject.exe and the shellcode itself are two different files. It is difficult for an antivirus to detect a threat if it’s split between several files, especially if each of them doesn’t pose any threat by itself.

shellcode_inject.exe is perfectly safe
shellcode_inject.exe is perfectly safe

Your shellcode will look even more harmless if you convert it into printable characters.

Creating an autonomous (not staged) shellcode. Looks harmless
Creating an autonomous (not staged) shellcode. Looks harmless

Looking at the contents of meter.txt, I would rather think that this is a Base64 string, not a shellcode.

It is necessary to note that the above example uses the meterpreter_reverse_tcp shellcode, not meterpreter/reverse_tcp. This autonomous code contains all the Meterpreter functions and won’t download anything over the network, thus, reducing the detection risk. But the combination of shellcode_inject.exe and meter.txt is dangerous. Let’s see whether the antivirus can recognize the threat.

Injecting Meterpreter
Injecting Meterpreter

Important: a system process was used for code injection; as a result, it immediately runs in the System context. And it seems that the tested antivirus was fooled by this trick (even though it noticed something wrong in shellcode_inject.exe).

Malicious code is executed bypassing the antivirus
Malicious code is executed bypassing the antivirus

Meterpreter enables the attacker to execute the payload directly in memory (i.e. bypassing the HDD).

Meterpreter executes malicious software in memory
Meterpreter executes malicious software in memory

For many years, I’ve been successfully using this simple trick. This time, it worked well, too.

Code caves

Each .exe file (PE format) contains some code. The code represents as a set of functions. During compilation, the functions are placed not directly one after the other, but with some alignment (16 bytes). Even larger empty spaces occur due to the alignment between sections (4096 bytes). Such alignments create a lot of small ‘code caves’ where additional code can be written. A lot depends on the compiler. But for most PE files, taking that you are primarily interested in the Windows OS, it looks like in the screenshot below.

Graphical representation of functions and caves between them
Graphical representation of functions and caves between them

What is such a ‘cave’?

Code cave
Code cave

If you determine the location of the caves more accurately (by signatures), you will get approximately the following picture in the executable file.

Searching for code caves in an executable file
Searching for code caves in an executable file
Visual location of caves in the file
Visual location of caves in the file

In this example, only 12-byte caves are shown; so, their actual number is much greater. Although the number of caves is large, they are obviously not sufficient to accommodate a fully featured program. Therefore, this method is suitable only for shellcode injection since the shellcode size rarely exceeds 1KB.

Let’s see if it’s possible to distribute a small multiple-step windows/meterpreter/reverse_tcp shellcode between these caves. The size of a code cave rarely exceeds 16 bytes; so, the shellcode should be split into pieces smaller than the basic blocks. This means that you have to insert additional jmp instructions to link them together and adjust the addresses of conditional jumps. Don’t worry, this is a fairly routine operation.

Shellcode fragmentation and injection into code caves
Shellcode fragmentation and injection into code caves

As a result, a shellcode 354 bytes in size was split into 62 pieces and injected into random caves between functions.

Initial executable file: empty caves occur between functions due to the alignment
Initial executable file: empty caves occur between functions due to the alignment
Modified executable file: infected caves between functions
Modified executable file: infected caves between functions

In theory, this approach should ensure polymorphism: every time, 2-3 shellcode instructions are placed to random caves (there is a special term for this: permutation). The code will be obfuscated even at the execution trace level due to the large number of jmp instructions between fragments.

Original shellcode execution trace
Original shellcode execution trace
Obfuscated shellcode execution trace
Obfuscated shellcode execution trace

Using this technique, you can bypass many ‘simple’ antiviruses.

Malicious code executed bypassing the antivirus
Malicious code executed bypassing the antivirus

However, this trick won’t fool ‘serious’ AV products.


Surprisingly, but the classical xoring of an executable file with a dynamic key still works well against even the most formidable antiviruses. Let’s take, for instance, some very dangerously-looking executable file and encrypt everything that is possible with xor. The encryption sections .text and .data will look something like this.

Xoring the specified addresses and sizes with the key 0x77
Xoring the specified addresses and sizes with the key 0x77

Next, let’s conceal the version info.

Encrypting resources of the executable file
Encrypting resources of the executable file

The .rdata section contains almost all suspicious strings. But the problem is that the directories of the import and deferred import tables (that cannot be touched!) are projected onto it; otherwise the file won’t run. Therefore, you have to find in this section an area containing mostly such strings and encrypt it.

Searching for the area that contains strings and encrypting it
Searching for the area that contains strings and encrypting it

Since you have encrypted everything in the executable file, when you run it, its offsets in the code won’t be correctly adjusted in accordance with the location address. Therefore, you have to disable ASLR:

PE->NT headers->Optional header->DllCharacteristics |=0x40

Time to check whether everything is concealed and your mimikatz doesn’t raise suspicions anymore.

The malicious content is successfully concealed
The malicious content is successfully concealed

Great. However, the file is inoperative in its current state since everything in it is encrypted. Prior to proceeding further, I recommend to run the file in a debugger to make sure that the PE structures weren’t damaged and the file is valid.

You have to write a small piece of machine code for decryption. Important: the key must be set at runtime (i.e. it won’t be saved anywhere in the code). In that case, the antivirus won’t detect any threats while running your app in the sandbox.

Your xor_stub.asm will maintain the initial state and add write permissions to encrypted sections.

Changing permissions of encrypted sections
Changing permissions of encrypted sections

Here and elsewhere, don’t forget to change the addresses of the WinAPI functions to your values. Now you have to adjust the entry point since a jump to this code will be inserted into it a bit later.

Restoring entry point instructions
Restoring entry point instructions

The next step is to request the decryption key from the user and dexor all encrypted areas.

Requesting the key and decrypting all xored areas
Requesting the key and decrypting all xored areas

Finally, you have to restore the initial state, fix the stack, and jump to the entry point.

Returning to the entry point
Returning to the entry point

Time to add an empty r-x section to mimikatz to place your xor_stub there.

Adding a section to store the decryption code
Adding a section to store the decryption code

Now you have to compile this assembler code and insert it into the newly-created section.

Compiling and inserting the decryption code
Compiling and inserting the decryption code

In the end, don’t forget to make a jump to your code from the entry point.

Passing control to the decryption code
Passing control to the decryption code

Voila! You run the shell and enter the decryption key (in this particular case, it’s the w character (0x77).

Malicious code is executed bypassing the antivirus
Malicious code is executed bypassing the antivirus

That’s it. Now let’s slightly automate this process and launch Meterpreter.

Automatic encryption performed using the described-above method
Automatic encryption performed using the described-above method

You execute the code and enter the w key.

Executing malicious code
Executing malicious code

The effect is the same.

Malicious code is executed bypassing the antivirus
Malicious code is executed bypassing the antivirus

Vuln inject (spawn)

I remember well one old case. I couldn’t open a Meterpreter session on the victim because of the antivirus and had to reexploit the oldie-goodie MS08-067 every time by running Meterpreter directly in memory. For some reason, the antivirus was unable to prevent this.

Apparently, antiviruses are primarily designed to detect malicious programs and shellcodes with known signatures (on the HDD and over the network, respectively) and prevent exploitation of popular vulnerabilities. But what if the vulnerability isn’t known to the antivirus yet?

In such a case, a pretty unusual technique is used; it’s based on the vulnerability injection principle (buffer overflow) and results in the unevident execution of arbitrary code. In fact, this is yet another variant of reflective code execution: the code bypasses the HDD and is present exclusively in RAM. But in the described-above situation, you also have to hide the malicious code entry point.

The antivirus static analyzer is unlikely to detect that the program contains a vulnerability and that arbitrary code is to be executed. This is a weak spot of modern computers, and I don’t expect the situation to change much in the near future. But can the antivirus detect the process in dynamics and react promptly?

To check this, you have to write and run a simple network service containing your invented 0-day vulnerability (buffer overflow on the stack) and exploit it. To make sure that everything works fine in newer Windows versions, you have to bypass DEP. But this isn’t a problem if the required ROP gadgets can be added to the program.

I am not going to explain here in detail what are buffer overflow and ROP chains: this is beyond the scope of the article. Instead of digging deep into this exciting topic, let’s just take an out-of-the-box solution. Important: you have to compile the service without ASLR support (and without DEP if you want):

cl.exe /c vuln_rop.c
link.exe /out:vuln_rop.exe vuln_rop.obj /nxcompat:no /fixed

This network service will operate in the listen and reverse connect modes. All data will be transmitted in encrypted form to avoid being detected by the signature analyzer. This is very important: some antiviruses ‘dislike’ Meterpreter so much that even a simple transmission of this software to any open port would inevitably provoke an alert with subsequent ban on the attacker’s IP address.

Technically, the resultant executable file is not malicious: it just contains a memory error. Otherwise, any program should be considered malicious since it may potentially contain bugs.

Running a handmade service with an embedded buffer overflow vulnerability
Running a handmade service with an embedded buffer overflow vulnerability

The vulnerable service has been successfully launched and waits for incoming data. Time to create a payload and run an exploit with it.

Exploiting the buffer overflow vulnerability
Exploiting the buffer overflow vulnerability

The vulnerable service receives your data and ‘accidentally’ runs the payload code because of the memory error.

Buffer overflow
Buffer overflow

The payload opens a Meterpreter session.

Malicious code is executed bypassing the antivirus
Malicious code is executed bypassing the antivirus

Important: all this mess takes place despite the running antivirus. In fact, just a few antivirus programs can successfully detect this trick. Let’s find out why. The code was injected in a seemingly unexpected way: through buffer overflow. Concurrently, the code wasn’t transmitted in the clear over the network; so, the signature engines should not detect any threats. But right before the execution, your machine code appears in memory. Apparently, at this point some antiviruses recognize the painfully familiar Meterpreter.

The antivirus does not trust an .exe file downloaded from an unknown source and launched for the first time. It emulates its execution and analyzes the code (perhaps each of its instructions). This comes at a price of performance, and antiviruses cannot afford to apply this procedure to all processes. Therefore, processes that are already validated at startup (e.g. system components or programs with known checksums) operate under less supervision. So, let’s inject the vulnerability into one of them!

Vuln inject (attach)

The easiest way to execute code in someone else’s address space (process) is to inject a library. DLL is not much different from EXE, and you can recompile your vulnerable service into a library format by simply changing main() to DllMain():

cl.exe /c vuln_rop.c
link.exe vuln_rop.obj /out:vuln_rop.dll /dll /nxcompat:no /fixed

For maximum portability, it’s preferable to use 32-bit programs; so, the vulnerability has to be injected into a 32-bit process. You can take any running process or run it yourself. On a 64-bit Windows system, you can always find 32-bit system programs in C:\Windows\SysWOW64.

Running a 32-bit process
Running a 32-bit process

Now you can add the vulnerability to any 32-bit process by simply injecting your library into it.

Injecting the library into a newly-launched 32-bit system process
Injecting the library into a newly-launched 32-bit system process

Your DLL without ASLR is successfully loaded at the standard address.

Vulnerable DLL has been loaded
Vulnerable DLL has been loaded

Now the target process with the injected buffer overflow vulnerability is ready to receive data over the network.

Vulnerable code operates in the context of a legitimate process
Vulnerable code operates in the context of a legitimate process

Since your vulnerable module was loaded at the address 0x10000000 (the default address for non-ASLR libraries), you have to slightly adjust the exploit code.

Adjusting the exploit code
Adjusting the exploit code

Time to run the exploit.

Running the exploit
Running the exploit

An overflow occurs in the context of a legitimate process

Buffer overflow in a legitimate process
Buffer overflow in a legitimate process

And the ‘malicious’ code is executed bypassing the antivirus.

Malicious code is executed bypassing the antivirus
Malicious code is executed bypassing the antivirus


The above-described technique uses the effect of ‘sudden’ code execution in memory through a 0-day vulnerability; in most cases, the antivirus is unable to predict it and block the threat. Loading a DLL into someone else’s process is a well-known trick, and it was used just for convenience: you hardly had to change anything.

In fact, you could use a more sophisticated method: substitute key instructions in the process memory at a point where user input is processed, thus, injecting a vulnerability there (kind of an antipatch). To make it exploitable, all you have to do is inject a couple of suitable ROP gadgets into the code caves. But so far, this is not required.

The technique that conceals code execution through buffer overflow isn’t new, although rather little known. In this article, I described the most trivial example, a buffer overflow on the stack, and it brought success. But there are also much more tricky memory errors resulting in RCE (hidden execution): use after free, double free, overflow in heap, format strings, etc. They offer a virtually unlimited potential for antivirus bypass techniques.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>