During a penetration testing engagement, it’s quite common to have antivirus software applications installed in a client’s computer. This makes it quite challenging for the penetration tester to run common tools while giving the clients a perception that their systems are safe but that’s not always the case.
Antivirus evasion is a broad topic and this article only presents very basic methods to bypass detection when the program is resting as a file in a non-volatile storage. Evasion techniques for a run time state are quite different and challenging at the same time because of behavior monitoring done by antiviruses.
In this article, I will be discussing a few techniques that can be used to bypass antivirus software applications like string manipulation and code substitution.
First will be finding the cause of the detection while the next step goes into how the detection can be bypassed.
For the demonstration, I will be using an object-oriented language specifically C# with the help of Visual Studio 2012. I grabbed a snippet from here specifically the functions “startup” and “USBSpread” while creating a new project to put both of these.
Please note that I have minimized the region of the code in the screenshot above to make it short. I’ll leave the credits where it is due for both those functions. After compiling the project and scanning it in VirusTotal, the result shows two antiviruses detecting it namely ESET and Sophos.
Now . How can we find out what’s causing the detection? Since we have a copy of the source code, what we can do is remove parts of the code line by line and rescan it. To start off, we have commented out the whole “USBSpread” function :
Notice that only ESET is now detecting it and detection by Sophos disappeared. Uncomment the function
comment out “Startup”
the detection found by ESET now disappeared and Sophos has reappeared. From what we did, we can conclude that having the “startup” function actually triggers detection from ESET while having the “USBSpread” function actually triggers detection from Sophos. Sounds easy to identify the detection right?
Bypassing the Detection
After being able to identify where the detection came from, we can now try to work out how it can be bypassed.. To successfully bypass the antivirus detecting it, we need to continuously do the previous step while working on a fix line by line. To add up, I’ve listed a few methods like string manipulation and code substitution that usually work but sometimes also trigger more detection so these methods are quite “experimental”.
This method simply points to how a normal string can be converted into another form while being evaluated with the same meaning. To understand this better, suppose we have a registry path:
In C#, if we declare this, it should look something like:
There are numerous ways on how we can change the form of that string while maintaining the original meaning when the code executes. Here are some very basic ways to do it:
Using an encoder tool, enter the string “SOFTWARE\Microsoft\Windows\CurrentVersion\Run” and click “encode”. You should get something like this as the result:
Now we copy that string back to the source code with the following evaluator and notice that when the program runs, the string still gets evaluated to the original form:
This is a pretty simple solution and it actually works sometimes too! Converting the string to its numeric ASCII form looks like this:
In the image above, I have converted the “\” character into its numeric ASCII form. If we check out the ASCII table, the character “\” is equivalent to 92 in decimal. Doing some other simple calculations basically differentiate the original code from the current one.
Encryption can also take part in this like having your string encrypted and written down to a binary file. The program can then load it by browsing and decrypting the contents during execution .
This method on the other hand requires more in-depth knowledge of programming because it requires understanding of what a specific line of code or what a specific function does. For example, suppose we have a code that downloads the contents of a web page:
With code substitution, an understanding of what the code does is essential because we will need to find a replacement of the code by commands while achieving the same logic and goal at the end. In this case, the goal of the code above tries to get the client IP address in the network where the program is running. This can also be achieved with the use of this code:
Notice that the output is the same which means the code logic does the same functionality and goal but the way it is done is different. If ever the first code is detected by a few antiviruses, it can be substituted with the next one or vice-versa. There are other more ways to grab the IP address but I’ll leave that part as a research for the readers.
Going back to the example program, let’s start with Sophos. At this point, the function “startup” is commented out to stop ESET from detecting it while we fix the first one. This antivirus was previously detecting the method “USBSpread” and after some trial and error, the detection was still popping up even after commenting out the whole function contents:
Once uploaded for scanning in VirusTotal, the result was 0/65!
- Reversed the string making it “nuR\\noisreVtnerruC\\swodniW\\tfosorciM\\ERAWTFOS” – Another detection popped up
- Moving the variable outside the function making it a global variable:
static string temp = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
– ESET still detecting
- Converting each characters to their decimal number equivalent in the ASCII table:
static string temp = ((char)83).ToString() + ((char)79).ToString() ... ;
– ESET still detecting
- Removed the string contents after “SOFTWARE” leaving: string temp = “SOFTWARE”; – ESET still detecting
At this point, ESET was still detecting the program even if the registry path doesn’t really make sense anymore so this might not be the “real” thing being flagged by ESET or there is another line of code in which if combined with the current string, gets detected. Once this happens, we need to carefully go back each step and see what could probably be the issue. While leaving the code uncommented:
string temp = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
Plus having this code commented out:
destination = System.IO.Path.Combine(destination, "nvdisp.exe");
We actually get 0/65 from VirusTotal!
- Changing the string “nvdisp.exe” to “ThisIsAnotherTest.exe” – ESET still detecting
- Moving variable outside the function making it a global variable: static string dest = “nvdisp.exe” – ESET still detecting
- Encoding the string to base64 deriving to the code:
destination = System.IO.Path.Combine(destination, System.Text.Encoding.Default.GetString(Convert.FromBase64String("Im52ZGlzcC5leGUi")));
– Another detection popped up
Now, there should be a lot of test cases here but to cut the testing short, since some basic string and variable manipulation don’t work, we can try to do some code substitution. Apparently, any programmer can understand what the “startup” function does. It simply adds the program to the Windows start up so it can execute once Windows boots. There are numerous ways to add a program in the Windows start up. This could be through copying the executable in “C:\Users\<USER>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup” or maybe by using a scheduled task.
The “startup” function in this case was replaced by this simple code:
Once compiled and scanned the result gives us a rate of 0/65.