Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2021-4034-CTF-writeup | Kitploit
Outils/GitHubGitHub/wechicken456/cve-2021-4034-ctf-writeup
ExploitationRétro-ingénierieCTFApprentissage et ÉducationExploitation de BinairesLabs et Pratique
GitHubwechicken456/cve-2021-4034-ctf-writeup

CVE-2021-4034-CTF-writeup

Voir le dépôt

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager
2il y a 2 ansPas encore vérifié

CVE-2021-4034-CTF-writeup

Il s'agit d'un challenge CTF de type pwn que j'ai écrit en C et qui demande à l'utilisateur d'exploiter la vulnérabilité CVE-2021-4034. Les joueurs reçoivent 2 binaires dans le répertoire challenge de ce dépôt. Le binaire chal implémente le challenge CTF et shelly.so est un binaire auxiliaire.

Comment émuler ce challenge

Au moment de la rédaction de ce writeup, le Dockerfile n'est pas encore terminé. Le Dockerfile est nécessaire pour déployer ce challenge lors d'un CTF en direct, mais pas localement : vous pouvez tout de même émuler ce challenge localement en configurant les permissions utilisateur et en installant les paquets vulnérables comme suit:

  1. Pour exploiter cette vulnérabilité avec succès, les joueurs qui ne sont pas sur une machine Linux doivent d'abord installer une VM Linux. Ensuite, vous devrez installer un noyau vulnérable. Instructions à ce sujet : [https://askubuntu.com/a/700221]
  2. Installez les paquets libpolkit-gobject-1-0=0.105-26ubuntu1 libpolkit-agent-1-0=0.105-26ubuntu1 policykit-1=0.105-26ubuntu1.
  3. Créez un fichier flag.txt appartenant à root:root dans le répertoire courant.
  4. Créez un utilisateur non privilégié. Passez à cet utilisateur.
  5. Téléchargez les fichiers du dossier challenge dans le répertoire courant.
  6. Exécutez chal en tant qu'utilisateur non privilégié.

Analyse à l'aveugle

L'exécution du binaire chal nous donne une vague idée de ce que fait ce binaire :``` WELCOME TO THE HUB CTRL+ALT+DELICIOUS We're not just a sandwich hub. We are the beacon of flavors, serving a symphony in every byte

  1. ENTER THE HUB
  2. QUIT 1 Order number: 0x7ffde93681f0 Enter your name: tin
  3. ADD NEW ORDER
  4. EDIT ORDER
  5. SHOW ORDER
  6. CANCEL ORDER
  7. CHECKOUT
  8. DONE 1 Pick your bread: aaaa Select your spread: bbbb Choose your veg: cccc Slam your meat & egg: dddc Any side notes for the cook? 0000
  9. ADD NEW ORDER
  10. EDIT ORDER
  11. SHOW ORDER
  12. CANCEL ORDER
  13. CHECKOUT
  14. DONE 1 Pick your bread: AAAA Select your spread: BBBB Choose your veg: CCCC Slam your meat & egg: DDDD Any side notes for the cook? 1111
  15. ADD NEW ORDER
  16. EDIT ORDER
  17. SHOW ORDER
  18. CANCEL ORDER
  19. CHECKOUT
  20. DONE 3 Enter order index: 0 aaaa, bbbb, cccc, dddc, 0000
  21. ADD NEW ORDER
  22. EDIT ORDER
  23. SHOW ORDER
  24. CANCEL ORDER
  25. CHECKOUT
  26. DONE 3 Enter order index: 1 AAAA, BBBB, CCCC, DDDD, 1111
  27. ADD NEW ORDER
  28. EDIT ORDER
  29. SHOW ORDER
  30. CANCEL ORDER
  31. CHECKOUT
  32. DONE 4 Enter order index: 1
  33. ADD NEW ORDER
  34. EDIT ORDER
  35. SHOW ORDER
  36. CANCEL ORDER
  37. CHECKOUT
  38. DONE 3 Enter order index: 1 Invalid index!
  39. ADD NEW ORDER
  40. EDIT ORDER
  41. SHOW ORDER
  42. CANCEL ORDER
  43. CHECKOUT
  44. DONE 5 /notes ./tin/notes
  45. ADD NEW ORDER
  46. EDIT ORDER
  47. SHOW ORDER
  48. CANCEL ORDER
  49. CHECKOUT
  50. DONE 6
  51. ENTER THE HUB
  52. QUIT 2 Come again :)
root@kitploit:~
En jouant avec la taille de l’entrée dans la fonction `Add` et les indices des fonctions `Cancel`, on n’obtient rien de spécial (pas de débordement ni d’erreur de segmentation). Cependant, quelques points intéressants :
  - Il semble que les commandes soient placées dans une liste (une liste chaînée peut-être) et qu’elles soient indexées à partir de 0 ?
  - Il y a ce `Order number: 0x7ffde93681f0` qui semble afficher un emplacement sur la pile ?
  - Aussi, le programme crée un répertoire portant notre nom d’entrée ainsi que 3 exécutables, dont l’un est le fichier binaire d’assistance qui nous est fourni :```sh
peasant@Tin-VM:~/Desktop$ ls
chal  chal.c  Dockerfile shelly.so  solve.py  tin
peasant@Tin-VM:~/Desktop$ ls -l tin/
total 28
-rwxrwx--- 1 peasant vboxsf     5 Feb  4 14:33 notes
-rwxrwx--- 1 peasant vboxsf    20 Feb  4 14:33 recipe
-rwxr-x--- 1 peasant vboxsf 16488 Feb  4 14:33 shelly.so
peasant@Tin-VM:~/Desktop$ cat tin/notes 
0000
peasant@Tin-VM:~/Desktop$ cat tin/recipe 
aaaa
bbbb
cccc
dddc

Les exécutables contiennent notre entrée.


Analyse Ghidra

Vous pouvez jouer avec les autres fonctions et espérer tomber sur quelques bugs (ce qui est probable), mais je vais aller droit au but et ouvrir le programme dans Ghidra.

En comparant les chaînes qui apparaissent pendant l'exécution du programme avec celles présentes dans Ghidra, nous pouvons renommer certaines fonctions FUN_* en noms familiers :```C undefined8 main(void)

{ int iVar1; size_t sVar2; undefined2 *puVar3; long in_FS_OFFSET; int opt; int local_1c; char *local_18; long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28); local_18 = "/recipe"; print("WELCOME TO THE HUB CTRL+ALT+DELICIOUS\n"); print( "We're not just a sandwich hub. We are the beacon of flavors, serving a symphony in every by te\n\n" ); while( true ) { print("1. ENTER THE HUB\n"); print("2. QUIT\n"); __isoc99_scanf(&DAT_001030c5,&opt); getc(stdin); if (opt != 1) break; printf("Order number: %p\n",&local_18); print("Enter your name: "); __isoc99_scanf(&DAT_0010334f,&DAT_00105120); sVar2 = strlen(&DAT_00105120); puVar3 = (undefined2 *)malloc(sVar2 + 2); DAT_00105100 = puVar3; *puVar3 = 0x2f2e; *(undefined *)(puVar3 + 1) = 0; strcpy((char *)(DAT_00105100 + 1),&DAT_00105120); iVar1 = FUN_001022f0(DAT_00105100,&DAT_00105060); if (iVar1 == -1) { mkdir((char *)DAT_00105100,0x1c0); } DAT_00105140 = 0; order_cnt = 0; for (local_1c = 0; local_1c < 10; local_1c = local_1c + 1) { *(undefined8 *)(&ptr_array + (long)local_1c * 8) = 0; } main_menu(); } print("Come again :)\n");

root@kitploit:~
Le `printf("Order number: %p\n",&local_18);` **affiche l'adresse d'une variable locale sur la pile.**

Une vérification rapide dans gdb nous montre que l'adresse divulguée est l'adresse du pointeur vers la chaîne constante `/recipe`:```gdb
...
Order number: 0x7fffffffdfc0
...
gef➤  x/gx 0x7fffffffdfc0
0x7fffffffdfc0:	0x0000555555559020
gef➤  x/s 0x0000555555559020
0x555555559020:	"/recipe"

Nous voyons un appel mkdir, qui crée un répertoire avec notre nom d'entrée dans le répertoire courant.

Cela est cohérent avec notre observation lorsque nous avons exécuté le programme. Ensuite, il initialise une variable avant d'appeler la fonction main_menu, qui ressemble à ceci :```C while( true ) { while( true ) { while( true ) { while( true ) { while( true ) { while( true ) { print("1. ADD NEW ORDER\n"); print("2. EDIT ORDER\n"); print("3. SHOW ORDER\n"); print("4. CANCEL ORDER\n"); print("5. CHECKOUT\n"); print("6. DONE\n"); __isoc99_scanf(&DAT_001030c5,&local_40); getc(stdin); if (local_40 != 1) break; add_order(); } if (local_40 != 2) break; edit_order(); } if (local_40 != 3) break; show_order(); } if (local_40 != 4) break; cancel_order(); } if (local_40 != 5) break; checkout(); } if (local_40 == 6) break; if (local_40 == 0x539) { print( "\nGORDON RAMSAY: Finally, a worthy opponent, our battle will be legendary! I BET YOU CAN 'T GUESS THE SECRET RECIPE.\n" ); fgets(inp,0x20,stdin); getrandom(random-bytes,0x10,0); for (local_3c = 0; local_3c < 0x10; local_3c = local_3c + 1) { if (inp[local_3c] != random-bytes[local_3c]) { print("...Nuh Uh!...\n"); /* WARNING: Subroutine does not return */ exit(0); } print("...Ooh Yes.. sCruMpTioUs..."); } print("Fine... I'll give you a taste.\n"); FUN_00101504(); }

root@kitploit:~
Si vous n'êtes pas familier avec REV, il s'agit de la décompilation de l'instruction `switch` en C. Il y a une option intéressante, alias `0x539`.  Elle permet aux joueurs de deviner `0x10` octets aléatoires. Si tous les octets sont égaux, alors elle appelle `FUN_00101504();` qui appelle `system("cat flag.txt");`. Sinon, le programme se termine. 

Cependant, forcer 16 octets aléatoires par force brute équivaut à essayer toutes les 256**16 = 340282366920938463463374607431768211456 possibilités. Bonne chance avec celle-ci lol. 

Même si vous passez toutes les possibilités, le programme reste non privilégié, donc il ne peut pas lire le flag. Cette instruction `cat flag.txt` est intentionnelle, non seulement pour piéger les joueurs inexpérimentés en leur faisant choisir cette option de menu `0x539`, mais aussi pour empêcher les joueurs de simplement appeler cette fonction pour lire le flag, ce que nous aborderons plus tard.
***
## Ajouter```C
  int iVar1;
  undefined8 *puVar2;
  void *pvVar3;
  undefined8 *ptr2;
  
  if (order_cnt < 10) {
    puVar2 = (undefined8 *)malloc(0x30);
    print("Pick your bread: ");
    readline(puVar2 + 1,8);
    print("Select your spread: ");
    readline(puVar2 + 2,8);
    print("Choose your veg: ");
    readline(puVar2 + 3,8);
    print("Slam your meat & egg: ");
    readline(puVar2 + 4,9);
    iVar1 = order_cnt;
    pvVar3 = malloc(0x30);
    *(void **)(&ptr_array + (long)iVar1 * 8) = pvVar3;
    *puVar2 = *(undefined8 *)(&ptr_array + (long)order_cnt * 8);
    print("Any side notes for the cook? ");
    readline(*puVar2,0x30);
    puVar2[5] = 0;
    if (DAT_00105140 != (undefined8 *)0x0) {
      for (ptr2 = DAT_00105140; ptr2[5] != 0; ptr2 = (undefined8 *)ptr2[5]) {
      }
      ptr2[5] = puVar2;
      puVar2 = DAT_00105140;
    }
    DAT_00105140 = puVar2;
    order_cnt = order_cnt + 1;
  }
...

Si certaines variables ne vous semblent pas aussi lisibles que ça, c'est parce que j'ai pris le temps de les renommer ainsi, ce que vous devriez faire pendant la rétro-ingénierie pour garder le fil. Bon, passons aux points principaux :

  • puVar2 est un chunk malloc de taille 0x30.
  • Le champ 0 est le pointeur puVar3, qui fait 8 octets et pointe vers les notes du chunk courant.
  • Les 4 champs suivants font chacun 8 octets, mais on peut lire 9 octets dans le 4ᵉ champ, donc on peut déborder d'1 octet dans le 5ᵉ champ.
  • Cependant, la ligne puVar2[5] = 0; écrase de toute façon notre débordement d'1 octet avec NULL.
  • Ensuite, si la variable globale DAT_00105140 == 0, on la définit simplement avec le nouveau chunk, donc ce pourrait être un pointeur head pour la liste ?
  • Sinon, on boucle jusqu'au dernier chunk de la liste courante, et on définit son 5ᵉ champ avec le nouveau chunk. => Le 5ᵉ champ est le pointeur vers le chunk suivant dans la liste chaînée.

Modifier```C

... print("Enter order index: "); __isoc99_scanf(&DAT_001030c5,&local_20); getc(stdin); if ((local_20 < 0) || (order_cnt <= local_20)) { print("Invalid index!\n"); } else { local_18 = head; for (local_1c = 0; local_1c != local_20; local_1c = local_1c + 1) { local_18 = (undefined8 *)local_18[5]; } print("Pick your bread: "); readline(local_18 + 1,8); print("Select your spread: "); readline(local_18 + 2,8); print("Choose your veg: "); readline(local_18 + 3,8);WE print("Slam your meat & egg: "); readline(local_18 + 4,9); print("Any side notes for the cook? "); readline(*local_18,0x30); } ...

root@kitploit:~
Il y a une vérification pour notre index d'entrée, donc nous ne pouvons pas modifier des emplacements arbitraires. 

**Cependant, la lecture de 9 octets dans le 4ème champ qui déborde d'un octet dans le 5ème champ est toujours là !!! Nous pouvons écraser 1 octet dans le pointeur `nxt`**
***
## Afficher```C
...
    if (local_18 != (undefined8 *)0x0) {
      printf("%s, %s, %s, %s, %s\n",local_18 + 1,local_18 + 2,local_18 + 3,local_18 + 4,*local_18);
    }
...

%s imprimera jusqu'au caractère NULL, donc si notre 4e champ est long de 8 octets, alors le 4e %s imprimera le 4e champ de notre chunk + la valeur du pointeur nxt dans le 5e champ. => Nous obtenons une fuite de heap !!!*


Cancel

Rien d'intéressant ici.


Checkout```C

strcpy(local_d8,dir_name); sVar2 = strlen(dir_name); strcpy(local_d8 + sVar2,"/recipe"); creat(local_d8,0x1c0); iVar1 = open(local_d8,2); if (iVar1 == -1) { print("Error opening file f.\n"); /* WARNING: Subroutine does not return / exit(0); } chmod(local_d8,0x1f8); strcpy(local_98,dir_name); sVar2 = strlen(dir_name); strcpy(local_98 + sVar2,"/notes"); printf("%s %s\n","/notes",local_98); creat(local_98,0x1c0); __fd = open(local_98,2); if (__fd == -1) { print("Error opening file f_notes.\n"); / WARNING: Subroutine does not return */ exit(0); } chmod(local_98,0x1f8);

root@kitploit:~
Il crée donc les fichiers `recipe` et `notes` dans le répertoire portant le nom de notre entrée. Les instructions `chmod` mettent ces fichiers en mode exécutable : `0x1f8` et `0x1c0` correspondent respectivement à `0700` et `0770` en base octale.```C
...
  for (local_ec = 0; (local_e0 != (char **)0x0 && (local_ec < order_cnt)); local_ec = local_ec + 1)
  {
    sVar2 = strlen((char *)(local_e0 + 1));
    write(iVar1,local_e0 + 1,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen((char *)(local_e0 + 2));
    write(iVar1,local_e0 + 2,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen((char *)(local_e0 + 3));
    write(iVar1,local_e0 + 3,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen((char *)(local_e0 + 1));
    write(iVar1,local_e0 + 4,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen(*local_e0);
    write(__fd,*local_e0,sVar2);
    write(__fd,&DAT_00103127,1);
    local_e0 = (char **)local_e0[5];
  }
  iVar1 = close(iVar1);
  if (-1 < iVar1) {
    iVar1 = close(__fd);
    if (-1 < iVar1) {
      local_58 = 0x2f706d742f207063;
      local_50 = 0x732e796c6c656873;
      local_48 = 0x206f;
      local_40 = 0;
      local_38 = 0;
      local_30 = 0;
      local_28 = 0;
      local_20 = 0;
      strcpy((char *)((long)&local_48 + 2),dir_name);
      system((char *)&local_58);
      if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
      }
      return;
    }
  }
...

Cet énorme bloc de code désordonné écrit en gros le contenu de nos morceaux dans ces fichiers, puis exécute la commande cp /tmp/shelly.so dir_name, où dir_name est le nom que nous donnons au programme au début.


FUN_00101e84```C

void FUN_00101e84(void)

{ long in_FS_OFFSET; char *local_18; long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28); puts("HOLD UP, LET HIM COOK."); local_18 = (char *)0x0; execve("/usr/bin/pkexec",&local_18,(char **)&ptr_array); if (local_10 != *(long )(in_FS_OFFSET + 0x28)) { / WARNING: Subroutine does not return */ __stack_chk_fail(); } return; }

root@kitploit:~
C'est une fonction intéressante. Elle appelle `pkexec` avec un `argv` NULL et le pointeur du tableau `notes` comme variables d'environnement, ce qui est la commande vulnérable dans notre CVE-2021-4034. Cela suggère-t-il que nous devons façonner nos `notes` et rediriger l'exécution vers cette fonction d'une manière ou d'une autre ?

***
## shelly.so
En l'ouvrant dans Ghidra, on comprend qu'il s'agit évidemment d'une bibliothèque `set-UID-root`, semblable à celles utilisées dans l'exploit de la CVE.

***
# Résumé
Quelques points importants :
1. Nous avons une fuite de pile au début du programme (numéro de commande) qui contient l'adresse de la chaîne constante `/recipe` utilisée pour créer le fichier `recipe`.
2. Le champ 0 d'un chunk est son pointeur `notes`.
3. Le champ 5 d'un chunk est le pointeur `nxt` vers le chunk suivant dans la liste chaînée.
4. `Show` peut divulguer une adresse du tas qui est le champ 5 dans un chunk.
5. `Edit` peut écraser 1 octet dans le champ 5. **C'EST LE SEUL BUG D'ÉCRITURE ARBITRAIRE !!**
6. `FUN_00101e84` appelle `pkexec` avec argv NULL et les variables d'environnement que nous contrôlons (le tableau `notes`).
***

# Exploitation

## Disposition des chunks du tas
Une inspection rapide dans gdb peut nous indiquer le décalage entre les différents chunks de notre programme :```gdb
...
Pick your bread: aaaa
Select your spread: a
Choose your veg: a
Slam your meat & egg: a
Any side notes for the cook? 0000
1. ADD NEW ORDER
2. EDIT ORDER
3. SHOW ORDER
4. CANCEL ORDER
5. CHECKOUT
6. DONE
1
Pick your bread: bbbb
Select your spread: b
Choose your veg: b
Slam your meat & egg: b
Any side notes for the cook? 1111
...
gef➤  search-pattern aaaa
[+] Searching 'aaaa' in memory
[+] In '[heap]'(0x55555555a000-0x55555557b000), permission=rw-
  0x55555555aae8 - 0x55555555aaec  →   "aaaa" 
gef➤  x/30gx 0x55555555aae8-0x18
0x55555555aad0:	0x0000000000000000	0x0000000000000041
0x55555555aae0:	0x000055555555ab20	0x0000000061616161
0x55555555aaf0:	0x0000000000000061	0x0000000000000061
0x55555555ab00:	0x0000000000000061	0x000055555555ab60
0x55555555ab10:	0x0000000000000000	0x0000000000000041
0x55555555ab20:	0x0000000030303030	0x0000000000000000
0x55555555ab30:	0x0000000000000000	0x0000000000000000
0x55555555ab40:	0x0000000000000000	0x0000000000000000
0x55555555ab50:	0x0000000000000000	0x0000000000000041
0x55555555ab60:	0x000055555555aba0	0x0000000062626262
0x55555555ab70:	0x0000000000000062	0x0000000000000062
0x55555555ab80:	0x0000000000000062	0x0000000000000000
0x55555555ab90:	0x0000000000000000	0x0000000000000041
0x55555555aba0:	0x0000000031313131	0x0000000000000000
0x55555555abb0:	0x0000000000000000	0x0000000000000000

Ainsi, les chunks sont de taille 0x40 (incluant 0x10 octets de métadonnées), et ils sont séparés (du début du chunk courant au début du chunk suivant) par un décalage de 0x40+0x40 = 0x80. Cela vient du fait que les notes sont allouées à chaque fois que nous allouons un chunk, elles se retrouvent donc toujours entre deux chunks consécutifs.

Écriture vers un emplacement arbitraire

Avant de passer à l'exploit, comment abuser des bugs de dépassement d'1 octet pour écrire à une adresse arbitraire ? Nous pouvons utiliser la stratégie suivante :

  1. Allouer 3 chunks A , B , C.
  2. Fuiter le pointeur nxt du 2ème chunk (2ème point de la section Summary).
    • L'adresse fuitée (qui est le chunk suivant) est C.
    • Ensuite, l'adresse du chunk courant sera B = C-0x80.
    • Soit x l'octet le MOINS significatif de B.
  3. Déborder l'octet x + 8 dans le 5ème champ du chunk courant. Le pointeur nxt résultant sera B + 8.
    • Il faut être prudent ici : en utilisant l'extrait de mémoire ci-dessus, si on prend le chunk courant comme étant le premier, c.-à-d. A = 0x55555555aae0, alors l'adresse fuitée serait B = 0x55555555ab60 et x = 0xe8.
    • Si on écrase le dernier octet dans le 5ème champ de A à 0x55555555ab08 avec , le pointeur résultant sera , ce qui n'est pas ce que l'on voulait.

1ère façon - sauter vers "cat flag.txt"

Il y a un moyen de sauter vers cette fonction, que je ne vais pas aborder. Mais avant d'essayer, jetez un œil au Dockerfile. Remarquez-vous quelque chose à propos de flag.txt ?``` ... chown root:root /home/ctf/flag.txt ... USER peasant CMD ["/home/ctf/start.sh"]

root@kitploit:~
***IL APPARTIENT À ROOT, TANDIS QUE LE PROGRAMME EST EXÉCUTÉ ET APPARTIENT À UN UTILISATEUR NON PRIVILÉGIÉ***.
Ainsi, même si vous exécutez "cat flag.txt", le flag ne s'affichera pas, car le programme n'a pas la permission d'accéder au fichier. Cela implique que nous devons élever nos privilèges à root pour lire le flag.
## 2e méthode - CVE-2021-4034
Pour exploiter CVE-2021-4034, nous avons besoin de la configuration suivante:
1. Un répertoire nommé `GCONV_PATH=.`
2. Dans ce répertoire, nous avons besoin d'un fichier exécutable qui porte le nom du répertoire dans lequel notre fichier de configuration `gconv-modules` sera placé. Appelons-le `recipe` dans notre défi.
3. Dans le répertoire `recipe`, nous avons besoin d'un fichier nommé `gconv-modules` dont nous contrôlons le contenu, et d'une bibliothèque dynamique qui nous ouvrira un shell (peut-être s'agit-il du binaire fourni `shelly.so`)?
4. Ensuite, dans notre `gconv-modules`, nous devons avoir le même CHARSET qu'à l'étape 5 ci-dessous, et le nom de notre bibliothèque dynamique `shelly.so` comme ceci: `module UTF-8// SHELLY// shelly 2`.
5. Après tout cela, nous appelons `pkexec` avec argv NULL et le tableau de variables d'environnement élaboré = `{"recipe", "PATH=GCONV_PATH=.", "CHARSET=SHELLY", "SHELL=shelly", NULL}`.

Maintenant, nous exploitons ce défi pour obtenir la configuration ci-dessus.

***
### Objectif 1-2
Les étapes 1-2 sont faciles: il suffit de se connecter avec un nom d'utilisateur `GCONV_PATH=.` pour créer ce répertoire. Ensuite, nous effectuons un `checkout` pour créer l'exécutable `recipe` dans ce répertoire. Puis nous revenons au premier menu.
***
### Objectif 3-4
Nous nous connectons avec le nom d'utilisateur `recipe` pour créer ce répertoire. Maintenant, nous *devons* créer un fichier nommé `gconv-modules`, mais `checkout` ne crée que les fichiers `recipe` et `notes`.

Et si nous écrasions l'emplacement mémoire de la chaîne `/notes` pour qu'elle devienne `/gconv-modules`? Cela pourrait fonctionner, mais cela nécessite que cette mémoire soit accessible en écriture. Vérifions cela dans gdb:```gdb
gef➤  search-pattern /notes
[+] Searching '/notes' in memory
[+] In '/home/peasant/Desktop/chal'(0x555555559000-0x55555555a000), permission=rw-
  0x555555559010 - 0x555555559016  →   "/notes" 

Et elle est inscriptible !

Comment réaliser l'écriture ?

  1. Nous utilisons la stratégie d'écriture arbitraire décrite ci-dessus pour LIRE arbitrairement l'emplacement de l'adresse de pile divulguée. Cela nous donne l'emplacement de /recipe.
  2. Une inspection rapide dans gdb nous indique que le décalage entre /recipe et /notes est 0x10. Soustrayez cette valeur de l'adresse de /recipe obtenue à l'étape 1 pour obtenir l'adresse de /notes.
  3. Utilisez la stratégie d'écriture arbitraire ci-dessus pour remplacer /notes par /gconv-modules.

En ce qui concerne l'écriture du contenu correct dans le fichier gconv-modules, nous savons que lors du checkout, le contenu des notes des chunks sera écrit dans /notes, qui est maintenant /gconv-modules. Pour être sûrs, nous pouvons simplement fournir la chaîne module UTF-8// SHELLY// shelly 2 à chaque notes.

Ainsi, dès que nous appelons checkout, cela créera 2 fichiers recipe et gconv-modules et écrira quelques lignes module UTF-8// SHELLY// shelly 2 (nous utilisons shelly car c'est le nom de la bibliothèque .so set-uid-root fournie) dans le fichier gconv-modules. Il copiera également un shelly.so dans le même répertoire.

Objectif 5

  1. Nous allouons 4 chunks dont les notes sont les variables d'environnement correspondantes. Le tableau de pointeurs notes contiendra 4 pointeurs vers ces 4 notes et un pointeur NULL final, ce qui est exactement ce que nous voulions.
  2. Allouez des chunks supplémentaires et utilisez la même stratégie que pour l'objectif 3-4 pour écraser le RIP de la fonction main par notre fonction secrète FUN_00101e84.
    • Comment savoir où se trouve le RIP ? Vous vous souvenez de notre adresse de pile divulguée au début du programme ? Lancez gdb pour déterminer son décalage par rapport au RIP, puis ajoutez ce décalage à l'adresse divulguée réelle pour obtenir le RIP.
    • Comment savoir où se trouve main ? Vous vous souvenez de notre adresse divulguée de /recipe à l'étape 2 de l'objectif 3-4 ? Faites de même ici !
  3. Profitez de votre shell :)

Le script de résolution avec commentaires est fourni dans ce dépôt. N'hésitez pas à me contacter si vous avez des questions :)

Télécharger l’outil
x
nxt
0x55555555**ab**e8 != 0x55555555**aa**e8
  • Par conséquent, il faut choisir un chunk courant et un chunk suivant dont leur 2ÈME octet le MOINS significatif sont identiques.
  • Nous avons donc maintenant le 3ème chunk à B + 8 au lieu de C. Éditez le chunk B afin que son 1er champ (bread) contienne l'adresse target à laquelle nous voulons écrire.
  • Éditez le 3ème chunk, ce qui écrasera le contenu à partir de B + 8. Le pointeur notes de ce 3ème chunk est target ! Donc tout ce que nous écrirons dans notes sera écrit dans target !