
Écriture de défi CTF pwn exploitant CVE-2021-4034 (pkexec) via la manipulation du tas, avec rétro-ingénierie basée sur Ghidra et un binaire d'aide personnalisé shelly.so.
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.
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:
libpolkit-gobject-1-0=0.105-26ubuntu1 libpolkit-agent-1-0=0.105-26ubuntu1 policykit-1=0.105-26ubuntu1.flag.txt appartenant à root:root dans le répertoire courant.challenge dans le répertoire courant.chal en tant qu'utilisateur non privilégié.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
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.
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");
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.