Creating AV Resistant Malware — Part 2

Brendan Ortiz
Independent Security Evaluators
8 min readDec 1, 2020

--

In part two of this series, we’re going to be creating the AV destroyer part of our malware! The DLL we will be building is based on Rasta-mouse’s ASBBypass DLL. However, we will be modifying this DLL to increase our level of stealth. The reason for that is most public offensive tooling on the internet will already be added to an AV database.

In this stage, we create a malicious DLL that will perform the following tasks:

  1. Disable AMSI.
  2. Tamper with Event Tracing for Windows.
  3. Evade detection while doing it.
  4. Create a script that loads our DLL into memory and then executes it.

If you’ve never made a DLL before, download the GitHub repo I linked above or open visual studio and select the C# Windows Class Library options. It’s important to note that AMSI has visibility into .NET starting from version 4.8, so it’s important to choose a .NET framework lower than that if you’re concerned about it.

The malicious DLL Looks like the following:

Global Variables for our DLL.

First, we create a class named whatever you’d like; however, know that this will be how you invoke your DLL. The class defines a few global variables. The byte arrays are the bytes required to patch the x64 or x86 versions of ETW and the AMSI AmsiScanBuffer function. The AMSI patch works by inserting the value of AMSI_RESULT_CLEAN and then a return statement at the beginning of the AmsiScanBuffer function. This action means that the AmsiScanBuffer function has no opportunity to scan our code before it returns a CLEAN result. After the patch, our PowerShell process will be free from AMSI influence.

Disabling Event Tracing for Windows is also imperative because it will temporarily or permanently cease the flow of logging in the currently running process. It works by patching the EtwEventWrite function from ‘ntdll.dll’ that’s loaded in the PowerShell process memory. This patch is very similar to the AMSI patch.

The next functions are simple support functions. The first converts strings from base64 format to their ASCII representatives. This action is an attempt to hide trigger words from AV. The second function determines the current architecture the system is using. If the system is a 64-bit version of Windows it will return true, otherwise false.

Supporting Functions

Our next two functions are the meat and bones of our malicious DLL. The first Bypass() is our execution point that we will be calling when we want to bypass AMSI in PowerShell. The second PatchMem is the function we’ll be using to do the AMSI and ETW tampering.

Bypass and PatchMem functions

The bypass function simply calls our supporting is64bit() function and if it returns true continues with our 64bit versions of our patches. If it’s not 64bit then it continues with the x86 version of our patches.

PatchMem takes as arguments the patch bytes we’re using to set the AmsiScanBuffer function to return AMSI_RESULT_CLEAN the second it enters. Next, it will take the name of a library we want to open, in our case we want to open ‘amsi.dll’, which contains our AmsiScanBuffer function, and ‘ntdll.dll’ which contains the EtwEventWrite function. The next argument is the function we want to load in memory, our targets will be the functions just mentioned. Finally, an offset can be used to once again increase our level of stealth. Instead of calling the AmsiScanBuffer function directly, which will most definitely be picked up by AV, we can use relative addressing to call a function that won’t be picked up by AV and then increase our pointers’ value until we’re at our target functions. This also bypasses an ALSR problem. Instead of calling the AmsiScanBuffers’ address directly, which will change every reboot, we can use an offset relative to the target modules base address. Hence the use of the offset variables in the ‘global variables’ screenshot above.

Finding the reliable relative addressing is above the scope of this blog. However, if interested DLL Export Viewer was the tool used for this action.

PatchMem works by performing the following tasks:

  1. Gets a handle to amsi.dll by calling the LoadLibrary Windows API.
  2. Using the handle from LoadLibrary we then call the GetProcaddress Windows API with the name of the function we want to get a pointer to AmsiScanBuffer.
  3. To patch bytes relative to the function pointer returned by GetProcAddress in step 2, we add the offset value to the function pointer.
  4. Code segments in memory are typically read only, therefore we need to change the permissions so we can edit the targeted region. This is done using the VirtualProtect Windows API call. The 0x40 stands for EXECUTE_READWRITE. Meaning we’re changing the area of memory to be executable, readable, and writable.
  5. Next, we use Windows Marshal’s copy method to copy our patch into the region of memory we just changed permissions for.
  6. Finally, we revert the area of memory to its original memory protection state which should be READ_EXECUTE.

Now that we’ve defined the Bypass() and PatchMem() functions we will create our supporting functions. The supportin functions include Win32 which defines the Windows API calls we used in PatchMem.

PatchAmsi and Win32 Class

PatchAmsi function takes as arguments the chosen patch which is determined in Bypass() and the offset we’re patching to avoid detection. To help avoid detection by AV we also obfuscate the target dll ‘amsi.dll’ by base64 encoding the name and decoding it upon use (more advanced methods may use variations of pattern scanning memory or enumerating loaded dlls and hashing their name, then using a hash value as a comparison — eliminating the possibility of AVs detecting us using suspicious strings).

PatchETW works in the same way, except we use a bit of a different detection avoidance approach. Instead of base64 encoding the string ‘ntdll.dll’ we split the string and have the program concatenate it during run time. Both functions are passed their respective target functions and offsets.

Finally, we define the Win32 class, this class contains our Windows API calls used in the Patch Memory Function. All that is required at this point is to compile on release!

Next, I want to introduce you to another defense evasion technique that is incredibly simple and yet super effective. VisualStudio supports a tool named DotFuscated which you can download using their Get Tools and Features option. It works by taking .NET assemblies and obfuscating the names of Types, Methods, and Fields. It’s simple, effective, and best of all free!

DotFuscated Results.

To use it, select the target assembly and press build! Once the assembly’s recompiled with the obfuscated names, it is that much less likely to trigger signature-based anti-virus solutions.

The next step is building the custom-script.ps1 PowerShell script, which I introduced in Part 1. The final result of this script will be our entire stage 2 payload. The goal is to load the malicious DLL just created without it ever touching the disk. Therefore, the assembly needs to be in a format that can be read over HTTP and simultaneously loaded into memory. Thus, the following utility script to convert the assembly bytes into a base64 format is used.

Base64 conversion utility script

This script takes as parameters an Infile, which is our malicious DotFuscated DLL, grabs the contents of it with a Byte set as the encoding, and converts the contents to base64. If OutFile is specified then it will put the base64 string to a file, otherwise, it will print it to the screen.

Once we have our base64 encoded DLL we can use PowerShell to call .NET’s System.Reflection method to load our assembly without it ever touching disk! If you remember our payload is going to be downloaded and invoked over HTTP. The final payload for our second stage looks like the following:

Final Stage 2 Payload

(After publishing I found that PowerView has functionality to compress and base64 encode your DLL and will create a script to load it automatically. If interested please refer to the following URL: https://powersploit.readthedocs.io/en/latest/ScriptModification/Out-CompressedDll/)

This payload is incredibly simple, the DLL was the complex and heavy lifter. Once the DLL is loaded into memory using the reflection technique, we can call our Amsi class we defined in our DLL and use the Bypass() function. If you recall the Bypass() function is our execution point for our DLL.

Here is an example of how this works, I will be executing the string “amsiscanbuffer” because this is a known malicious string for AMSI. Once we execute our script our PowerShell process will no longer trigger AV protection mechanisms.

Example of the Bypass Payload Working.

That concludes Part 2 I hope you enjoyed it. Part 3’s objective will be creating the malicious DLL that acts as a dropper for our final encrypted shell code.

Brendan Ortiz is an Associate Security Analyst at Independent Security Evaluators, a firm of security specialists that provide a wide range of services including custom security assessments and software development. ISE also runs IoT Village, which hosts talks by expert security researchers who dissect real-world exploits and hacking contests consisting of off-the-shelf IoT devices.

Twitter: @ISESecurity

Sign up to get our latest blogs.

--

--