Cryptographie rapide sous Windows

Comment obtenir un hash MD5 sous Windows, déchiffrer un bloc DES ?

j’ai totalement conscience d’évoquer dans ce post des algorithmes considérés comme dépassés, mais ces derniers sont toujours massivement utilisés dans les mécanismes internes de Windows
mimikatz 2.0 s’en sert lui aussi massivement ;)

Limitations

Les algorithmes évoqués dans ce post sont seulement les suivants :

MD4 LM DES (ECB)
MD5 NTLM RC4
SHA1    

Déjà vu sur Internet *

  • assemblage de bouts de code provenant d’Internet (réimplémentation des algorithmes)
  • utilisation des librairies d’OpenSSL
  • utilisation de la CryptoAPI
  • utilisation de la CNG

* je passe sur l’utilisation de CSP/PKCS#11 pour communiquer avec son HSM préféré ;)

A notre disposition sous Windows

Comme déjà listé, il est possible d’utiliser nativement la CryptoAPI, ou sa nouvelle version la CNG… mais n’est-ce-pas un peu lourd pour de si simples besoins ?

Windows incorpore déjà beaucoup de routines dans ses librairies système et, si l’on en utilise une grande partie via les SDK référençant ces API, l’on en occulte quelques-unes très pratiques !

Une majorité de ces API se situent dans la librairie advapi32.

API clairement identifiées

MD4Init MD5Init A_SHAInit
MD4Update MD5Update A_SHAUpdate
MD4Final MD5Final A_SHAFinal

API derrières SystemFunctionXXX

RtlEncryptDES1block1key SystemFunction001
RtlDecryptDES1block1key SystemFunction002
RtlEncryptDESMagicBlock1key SystemFunction003
RtlEncryptDESblocksECB SystemFunction004
RtlDecryptDESblocksECB SystemFunction005
RtlDigestLM SystemFunction006
RtlDigestNTLM SystemFunction007
RtlLMResponseToChallenge
NTLMv1 aussi sans doute
SystemFunction008
SystemFunction009
RtlDigestMD4only16Bytes SystemFunction010
SystemFunction011
RtlEncryptDES2blocks2keys SystemFunction012
SystemFunction014
SystemFunction020
SystemFunction022
RtlDecryptDES2blocks2keys SystemFunction013
SystemFunction015
SystemFunction021
SystemFunction023
RtlEncryptDES2blocks1key SystemFunction016
SystemFunction018
RtlDecryptDES2blocks1key SystemFunction017
SystemFunction019
RtlEncryptDES2blocks1DWORD SystemFunction024
SystemFunction026
RtlDecryptDES2blocks1DWORD SystemFunction025
SystemFunction027
RtlEqualMemory16Bytes SystemFunction030
SystemFunction031
RtlEncryptDecryptRC4 SystemFunction032
SystemFunction033
RtlCheckSignatureInFile SystemFunction035
Clé de session RPC
fonctions non identiques
SystemFunction028
SystemFunction029
SystemFunction034

Téléchargement

La majorité des API sont référencées via des SystemFunctionXXX qui peuvent déjà être liées par la librairie advapi32.lib du SDK ou WDK.

Malheureusement pas les API A_SHAFinal, A_SHAInit, A_SHAUpdate, MD4Final, MD4Init, MD4Update, MD5Final, MD5Init, MD5Update et RtlCheckSignatureInFile (SystemFunction035).

J’ai donc créé trois fichiers :

Exemple de petit code

Il est là pour exposer l’utilisation du CRYPTO_BUFFER, du MD5, SHA1 et DES-ECB

const BYTE data[] = "my super secret is a kiwi :)";
const BYTE key[] = "42 & 0xdeadbeef of course !";

CRYPTO_BUFFER
	bData    = {sizeof(data) - 1, sizeof(data) - 1, (PBYTE) data}, 
	bKey     = {sizeof(key) - 1, sizeof(key) - 1, (PBYTE) key},
	bCipData = {0, 0, NULL},
	bDecData = {0, 0, NULL}
;

MD5_CTX md5ctxInput, md5ctxOutput; // MD5 digest in context
SHA_CTX shactxInput, shactxOutput;
SHA_DIGEST shaInput, shaOutput;

NTSTATUS status;

MD5Init(&md5ctxInput);
MD5Update(&md5ctxInput, bData.Buffer, bData.Length);
MD5Final(&md5ctxInput);	// original data MD5 hash

A_SHAInit(&shactxInput);
A_SHAUpdate(&shactxInput, bData.Buffer, bData.Length);
A_SHAFinal(&shactxInput, &shaInput); // original data SHA1 hash

status = RtlEncryptDESblocksECB(&bData, &bKey, &bCipData);
if(status == STATUS_BUFFER_TOO_SMALL)
{
	if(bCipData.Buffer = (PBYTE) LocalAlloc(LPTR, bCipData.Length))
	{
		bCipData.MaximumLength = bCipData.Length;
		status = RtlEncryptDESblocksECB(&bData, &bKey, &bCipData);
	}
} else status = STATUS_NONCONTINUABLE_EXCEPTION;

if(NT_SUCCESS(status)) // Cipher operation OK ?
{
	status = RtlDecryptDESblocksECB(&bCipData, &bKey, &bDecData);
	if(status == STATUS_BUFFER_TOO_SMALL)
	{
		if(bDecData.Buffer = (PBYTE) LocalAlloc(LPTR, bDecData.Length))
		{
			bDecData.MaximumLength = bDecData.Length;
			status = RtlDecryptDESblocksECB(&bCipData, &bKey, &bDecData);
		}
	} else status = STATUS_NONCONTINUABLE_EXCEPTION;
} 

if(NT_SUCCESS(status)) // Decipher operation OK ?
{
	MD5Init(&md5ctxOutput);
	MD5Update(&md5ctxOutput, bDecData.Buffer, bDecData.Length);
	MD5Final(&md5ctxOutput); // deciphered data MD5 hash

	A_SHAInit(&shactxOutput);
	A_SHAUpdate(&shactxOutput, bDecData.Buffer, bDecData.Length);
	A_SHAFinal(&shactxOutput, &shaOutput); // deciphered data SHA1 hash
		
	// data compare works too, but it's for expose MD5 & SHA1 functions ;)
	wprintf(L"MD5  match : %s\n",
		RtlEqualMemory(md5ctxInput.digest, md5ctxOutput.digest, MD5_DIGEST_LENGTH) ? L"OK" : L"KO");
	wprintf(L"SHA1 match : %s\n",
		RtlEqualMemory(shaInput.digest, shaOutput.digest, SHA_DIGEST_LENGTH) ? L"OK" : L"KO");
}

LocalFree(bDecData.Buffer);
LocalFree(bCipData.Buffer);

Correction d’un en-tête Windows

Les seules API un tant soit peu présentes dans les en-têtes standards de Windows sont les suivantes :

  • RtlEncryptMemory
  • RtlDecryptMemory
  • RtlGenRandom

Lors de l’utilisation de ces fonctions, avec les fichiers proposés ou non, si les erreurs suivantes apparaissent lors de la création des liens :

error LNK2001: symbole externe non résolu _SystemFunction036
error LNK2001: symbole externe non résolu _SystemFunction040
error LNK2001: symbole externe non résolu _SystemFunction041

C’est que l’en-tête ntsecapi.h déclare incorrectement les prototypes des fonctions.
Il faut malheureusement les corriger :

BOOLEAN
RtlGenRandom(
    __out_bcount(RandomBufferLength) PVOID RandomBuffer,
    __in ULONG RandomBufferLength
    );
//...
NTSTATUS
RtlEncryptMemory(
    __inout_bcount(MemorySize) PVOID Memory,
    __in ULONG MemorySize,
    __in ULONG OptionFlags
    );

NTSTATUS
RtlDecryptMemory(
    __inout_bcount(MemorySize) PVOID Memory,
    __in ULONG MemorySize,
    __in ULONG OptionFlags
    );

en rajoutant la convention d’appel WINAPI (__stdcall) :

BOOLEAN WINAPI
RtlGenRandom(
    __out_bcount(RandomBufferLength) PVOID RandomBuffer,
    __in ULONG RandomBufferLength
    );
//...
NTSTATUS WINAPI
RtlEncryptMemory(
    __inout_bcount(MemorySize) PVOID Memory,
    __in ULONG MemorySize,
    __in ULONG OptionFlags
    );

NTSTATUS WINAPI
RtlDecryptMemory(
    __inout_bcount(MemorySize) PVOID Memory,
    __in ULONG MemorySize,
    __in ULONG OptionFlags
    );

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *