It is often the the case that we want to perform some more thorough testing by using actual malware executables, and perhaps different command and control techniques during our test. We want to vary our techniques in order to find out where the clipping threshold of defense technologies is set and be able to comprehensively report back on what techniques were effective on a system versus what techniques were not. In most environments, the most commonly deployed endpoint protection technology is an Antivirus engine.
Antivirus has become very effective at detecting off-the-shelf 32-bit malware executables from the Metasploit framework but tends to be lacking in the 64-bit arena. Additionally, we find that network resident defenses are well-tuned to 32-bit second stage payloads from Metasploit but less capable of seeing a 64-bit second stage payload. In my experience, the AV engines are not exclusively looking at the shellcode but also matching on the assembly code that constitutes the stub loader for Metasploit executables generated by the msfvenom command.
When Metasploit payloads are generated they use a standard template executable in both the 32-bit and 64-bit cases. The standard templates are in the form of precompiled executables in the framework’s data directory. In addition to the templates, the Metasploit project provides a source code directory in the framework.
Focusing specifically on Windows, we can find both the 32-bit template source in C and the 64-bit template source in assembly, both of which are in the “/usr/share/metasploit-framework/data/templates/src/pe/exe” directory on a KALI distribution.
In both the 32 and 64-bit cases, the template source has a very similar function. It allocates a buffer of 4096 bytes in memory and puts the string “PAYLOAD:” at the beginning of this buffer. The string “PAYLOAD:” is placed into the buffer as a constant that indicates a starting place for “msfvenom” to use when creating a new payload executable.
That starting place is an address in memory which msfvenom knows can be used to copy shellcode into. The size of the available buffer for shellcode is the allocated buffer size in the template EXE minus eight (the length of the string “PAYLOAD:”). Msfvenom will take the chosen payload, encode it with the appropriate encoder (if specified), and prepend no-operation (NOP) sled bytes if also chosen.
The final executable in the 32-bit case has been compiled from C source code. In the C source code, the shellcode is called by casting the payload buffer to a pointer to a function (which has no function parameters).
The final executable in the 64-bit case has been compiled from assembly code. The assembly code function allocates an executable buffer of memory, copies the shellcode into that memory, and executes it using a CALL instruction. This is a very similar technique used by many different tools, including the awesome Powershell toys we all use.
64-bit assembly source code for EXE template
Armed with this knowledge, I decided to see how one single AV engine (Avast) reacted when I simply took the 64-bit executable template and copied it to a Windows system. Note that I did not even put any shellcode payload into the EXE but only took the template itself.
It was not really surprising that Avast immediately triggered an alert. Let’s face it, matching on the assembly opcodes for the template is a pretty easy way of triggering an alert without having to actually examine the shellcode payload.
Avast tells me this is bad!
Staying focused on the 64-bit case, there is absolutely no reason why I cannot recompile this assembly code and modify it as much or as little as I want to. We only need to make sure that, at some point, it calls the two required bits of code to copy the payload into an executable memory segment we allocated and then executes it.
Case 1: For my first level of fun, I simply recompiled the same source assembly code. Not surprisingly, Avast flagged this.
Case 2: I changed the buffer length to 8192 bytes, and recompiled. Nothing other than the buffer length was changed. Avast completely failed this test by not flagging a single alert. How do I know? Well I compiled it on the system that Avast was also running. Note that the instructions for compiling the assembly code are helpfully listed in the commands of the source code.
Last section of x64 assembly listing
Case 3: I modified all of the values in the assembly code to 8192, then took my newly generated executable template and created two different payloads with it. One of the payloads used the 64-bit XOR encoding on the shellcode, while the other used no encoding at all.
I then copied the payload files to my Windows 7 machine running Avast. I forced Avast to scan them, and they passed with flying colors! Then I executed them and shell was mine.
With case 3, I was particularly amused at Avast’s DEEP SCAN, which seemed to indicate that it was looking really hard at what was going on! But then, it told me that all was fine and the malware was happily executed.
New assembly source code listing with 8192 buffer length.
64-bit payload using new template, and no encoding.
64-bit payload using new template and XOR encoding
New payloads in a directory on the Windows system!
Go ahead and scan my directory…
I am safe, what a relief!
Oh no, I might get caught here! Phew…
And now it’s shell time.
Conclusion: My theory and practical experience was that AV vendors are looking at the templates rather than the shellcode itself. In this specific instance, we saw immediate success with only a minor assembly code modification and absolutely no encoding of a 64-bit shellcode payload.
Why choose Avast? No specific reason other than I needed a solution in a hurry to execute my test. I will be repeating the experiment with other AV engines to see what my mileage looks like. There are many possible variations on this technique but like so much in life, it is better to start simple and ramp up as needed. Happy hunting!