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 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 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
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
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,
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
.exe to the file name. In the end, all that was required to bypass this particular signature was to remove the
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_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
netapi32.dll. It is possible, however, to also call the functions by their
ordinal – a number which refers to the function.
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
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
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.
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
Related StoriesView More
Introduction to Sliver
By Red Siege | November 7, 2022
By: Justin Palk, Security Consultant Around the time Tim decided he was going to give a Siegecast on selecting a C2, I finished building out a test Windows AD domain […]Learn More
Moving beyond T4 – Deconstructing Nmap Tuning
By Red Siege | July 6, 2022
by Alex Norman, Senior Security Consultant Nmap -T4 -iL targets.txt This is a very common scan string that many people use to get initial recon done on assessments and, to […]Learn More
Creating a Simple Windows Domain for Offensive Testing: Part 4
By Red Siege | June 23, 2022
By: Justin Palk, Security Consultant This is part four of my series of blog posts on creating a windows domain for offensive security testing. In part 1, I stood up […]Learn More