If you want to execute arbitrary code on an endpoint during a penetration test, red team, or assumed breach, chances are you’ll have to evade some kind of antivirus solution. AV engines use two detection methods to identify malicious code – signature-based and behavior-based detection.

Behavior-based detection

Behavior-based detection involves analyzing what code does when it executes and determining if that behavior is indicative of malicious behavior. Examples of a behavioral detection would be identifying the use of process hollowing or the use of CreateRemoteThread for DLL injection.

Signature-based detection

Signature-based detection involves looking for static signatures that match known-bad code. Examples of signature-based detection include matching file hashes to known malware and matching strings within the potential malware. Many an AV vendor has been known to mark a payload as malware simply because @harmj0y appeared somewhere within the file. In this blog, we’re going to evade Windows Defender by modifying the Mimikatz source code to evade signature-based detections.

Beating detection – Level 1: Text Replacement

Signature-based detection is brittle because it relies on matching specific signatures – often text strings – within the object being scanned. As a result, if we modify our payload so the relevant signatures are no longer found, we can evade signature-based detection. A well-known example of this is changing Mimikatz to Mimidogz. I’ve encountered AV products that have alerted simply because Will Schroeder’s Twitter handle, @harmj0y, appeared in a PowerShell script.

Now that we know what a signature-based detection is, how do we go about identifying what specific signatures are causing Windows Defender to identify our payload as malicious? Matt Hand (@matterpreter) created DefenderCheck to help identify exactly what bytes in a payload cause Defender to mark the payload as malicious.

I downloaded the Mimikatz source code and compiled it with Microsoft’s Visual Studio 2019. Before you can start compiling, you’ll need to make a few modifications. In the Solutions Explorer, right-click on mimikatz, and click Properties. You’ll need to change the Platform Toolset option from the default. At the time of writing, I set mine to “Visual Studio 2019 (v142).” Repeat this process for the mimilib solution as well.

I didn’t spend time digging into why this needed to be done, but line 7 of mimikatz/common modules/rpc/kull_m_rpc_ms-rprn.h was causing a build error. After deleting this line, I was able to build without any issues. Be warned, this is a super kludgey hack and will likely cause an issue when trying to dump from remote machines.

After compiling the source, I used DefenderCheck to see if the binary was detected as malicious. No surprises here, it was detected as HackTool:Win64/Mikatz!dha. DefenderCheck returns a hexdump of the bytes that caused Defender to alert on the payload. In the following screenshot, we can see that detection occurred in an error message string contained in the binary. The specific string appears to be mimikatz_doLocal.

I took an educated guess that it was the presence of mimikatz in the string that caused the detection, so I performed a search and replace to replace all instances of mimikatz with mimidogz and recompiled the binary. No more issues with these types of strings!

Beating detection – Level 2: DLL Names

I ran the new binary through DefenderCheck and found a new issue. This time the offending signature appeared to be wdigest.dll as shown here:

I searched for wdigest.dll in the source code and found it appeared in two files:

It took a while to find exactly what was needed to evade detection here. Wdigest.dll appears in a list of DLLs. I tried reordering that list but every attempt still resulted in detection. The next step is understanding how that list of DLLs is being used. We can see here that the list of DLLs is part of an array, version_libs[].

Digging a little further, we can see that the DLLs in version_libs are passed to GetFileVersionInfoSize and GetFileVersionInfo. Looking at the details for GetFileVersionInfo, we find that if the full path for the file being queried is not specified, the LoadLibrary search sequence is used. Specifically, if the file extension is omitted, the function will append both .dll and .exe to the file name. In the end, all that was required to bypass this particular signature was to remove the .dll from wdigest.dll!

Beating detection – Level 3: Function Names

Getting the binary working against an up-to-date version of Defender required many other changes. This included changing instances of the following strings: kull, kuhl, kiwi, sekurlsa, logonpasswords, credman.

Perhaps the most interesting signatures were for the following functions: I_NetServerAuthenticate2, I_NetServerReqChallenge, and I_NetServerTrustPasswordsGet. These functions are part of netapi32.dll. A stripped down version of this library is included in the mimikatz/lib directory as netapi32.min.lib. After some searching, I found a blog that discussed getting around this particular detection. First, I needed to create a .def file that I would use to build a new library module that would be included during the Mimikatz build process. The contents of the file are shown below. Here’s what’s happening: a library (DLL) may export one or more functions that can be used by other programs. Those functions are usually called by name, such as I_NetServerAuthenticate2 from netapi32.dll. It is possible, however, to also call the functions by their ordinal – a number which refers to the function.

LIBRARY netapi32.dll


                   I_NetServerAuthenticate2 @ 59

                   I_NetServerReqChallenge @ 65

                   I_NetServerTrustPasswordsGet @ 62



I needed to compile this .def file into a module using the Visual Studio developer console and the following command: lib /DEF:netapi32.def /OUT:netapi32.min.lib. After building netapi32.min.lib, I placed the file in the libx64 directory, replacing the original file. After rebuilding, mimikatz no longer contained the offending function names from netapi32.dll.

A final check with DefenderCheck showed the file is no longer being detected as malicious.

The final test

It was time to see if all this hard work would pay off. As you can see, I was able to execute Mimikatz and extract credentials without triggering Defender. Some of the modifications that were required can be seen in the screenshot, including mimidogz, securelsa, and loginpasswords.

What about other AV?

This same technique can be used with any payload you want to execute on a system running Defender. In fact, you can do it against any AV with just a little more work. PowerSploit’s Find-AVSignature.ps1 can help automate the process, but the basic method is a binary tree-style search. This process can be time consuming, and even if you get past signature-based detection, you may be caught by behavior analysis. However, in many cases, your efforts will be rewarded with code execution. Just keep in mind that because AV vendors are constantly updating their signatures, what works on Friday afternoon might not work on Monday morning when you need it.

Thank You!

Subscribe now to join our email list and continue getting up to date information on all of the live events, discussions, educational webcasts and giveaways

Red Siege Logo