From 453d88282f965589a3ebba62e511447800b9744b Mon Sep 17 00:00:00 2001 From: Yorick Barbanneau Date: Mon, 17 Apr 2023 18:00:20 +0200 Subject: [PATCH] Add TD8 --- .../secu_logicielle/td8-gdb/files/Makefile | 27 ++ .../secu_logicielle/td8-gdb/files/modif-rev.c | 21 + content/secu_logicielle/td8-gdb/files/modif.c | 18 + .../secu_logicielle/td8-gdb/files/modif2.c | 19 + .../secu_logicielle/td8-gdb/files/modif3.c | 19 + content/secu_logicielle/td8-gdb/files/optim.c | 17 + content/secu_logicielle/td8-gdb/files/stack.c | 15 + content/secu_logicielle/td8-gdb/index.md | 452 ++++++++++++++++++ 8 files changed, 588 insertions(+) create mode 100644 content/secu_logicielle/td8-gdb/files/Makefile create mode 100644 content/secu_logicielle/td8-gdb/files/modif-rev.c create mode 100644 content/secu_logicielle/td8-gdb/files/modif.c create mode 100644 content/secu_logicielle/td8-gdb/files/modif2.c create mode 100644 content/secu_logicielle/td8-gdb/files/modif3.c create mode 100644 content/secu_logicielle/td8-gdb/files/optim.c create mode 100644 content/secu_logicielle/td8-gdb/files/stack.c create mode 100644 content/secu_logicielle/td8-gdb/index.md diff --git a/content/secu_logicielle/td8-gdb/files/Makefile b/content/secu_logicielle/td8-gdb/files/Makefile new file mode 100644 index 0000000..4d0d77b --- /dev/null +++ b/content/secu_logicielle/td8-gdb/files/Makefile @@ -0,0 +1,27 @@ +CFLAGS=-g -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-array-bounds -Wno-stringop-overflow -m32 -no-pie -fno-pie +LDFLAGS=-m32 +LDLIBS=-lpthread +C=$(wildcard *.c) +O=$(C:.c=) +O2=$(C:.c=.O2) + +pframe: + curl -o pframe.tgz https://dept-info.labri.fr/~thibault/SecuLog/pframe.tgz && \ + tar -xf pframe.tgz &&\ + rm -rf pframe.tgz + +.gdbinit: + +configure: pframe .gdbinit + $(shell echo "python import pframe" > .gdbinit) +%.O2: %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -O2 $^ $(LDLIBS) -o $@ + +all: $O $(O2) + +PHONY: gdb_% +gdb_%: $(subst gdb_,,%) + PYTHONPATH=${PWD}/pframe${PYTHONPATH:+:${PYTHONPATH}} gdb $< + +clean: + rm -f $O $(O2) diff --git a/content/secu_logicielle/td8-gdb/files/modif-rev.c b/content/secu_logicielle/td8-gdb/files/modif-rev.c new file mode 100644 index 0000000..1a354a2 --- /dev/null +++ b/content/secu_logicielle/td8-gdb/files/modif-rev.c @@ -0,0 +1,21 @@ +#include + +void f(int *x) { + int i; + for (i = 0; i < 10000; i++) + (*x)++; + (*x) *= 2; +} + +void g(int *y) { + return f(y); +} + +int main(void) { + int b = 0; + int a = 0; + int c = 0; + g(&a); + printf("%d\n", a); + return 0; +} diff --git a/content/secu_logicielle/td8-gdb/files/modif.c b/content/secu_logicielle/td8-gdb/files/modif.c new file mode 100644 index 0000000..e1e789c --- /dev/null +++ b/content/secu_logicielle/td8-gdb/files/modif.c @@ -0,0 +1,18 @@ +#include + +void f(int *x) { + (*x)++; +} + +void g(int *y) { + return f(y); +} + +int main(void) { + int b = 1; + int a = 2; + int c = 3; + g(&a); + printf("%d\n", a); + return 0; +} diff --git a/content/secu_logicielle/td8-gdb/files/modif2.c b/content/secu_logicielle/td8-gdb/files/modif2.c new file mode 100644 index 0000000..d94ebb5 --- /dev/null +++ b/content/secu_logicielle/td8-gdb/files/modif2.c @@ -0,0 +1,19 @@ +#include +#include + +void f(char *x, int n) { + memset(x, '\0', n); +} + +void g(char *y, int n) { + return f(y, n); +} + +int main(void) { + int b = 1234567890; + int c = 1234567890; + char a[] = "Hello, you!"; + g(a, sizeof(a) + 1); + printf("%p:%d %p:%d %p\n", &b, b, &c, c, &a); + return 0; +} diff --git a/content/secu_logicielle/td8-gdb/files/modif3.c b/content/secu_logicielle/td8-gdb/files/modif3.c new file mode 100644 index 0000000..faed15c --- /dev/null +++ b/content/secu_logicielle/td8-gdb/files/modif3.c @@ -0,0 +1,19 @@ +#include +#include +#include + +void f(char *x, int n) { + memset(x, '\0', n); +} + +void g(char *y, int n) { + return f(y, n); +} + +int main(void) { + char *a = strdup("aaa"); + g(a, sizeof(a)+1); + printf("%p\n", &a); + free(a); + return 0; +} diff --git a/content/secu_logicielle/td8-gdb/files/optim.c b/content/secu_logicielle/td8-gdb/files/optim.c new file mode 100644 index 0000000..b98f909 --- /dev/null +++ b/content/secu_logicielle/td8-gdb/files/optim.c @@ -0,0 +1,17 @@ +#include + +int __attribute__((noinline)) f(int x) { + return x+1; +} + +int __attribute__((noinline)) g(int y) { + int z = y+1; + int a = f(z); + printf("%d\n", a); + return a; +} + +int main(void) { + printf("%d\n", g(1)); + return 0; +} diff --git a/content/secu_logicielle/td8-gdb/files/stack.c b/content/secu_logicielle/td8-gdb/files/stack.c new file mode 100644 index 0000000..6183490 --- /dev/null +++ b/content/secu_logicielle/td8-gdb/files/stack.c @@ -0,0 +1,15 @@ +#include + +int f(int *x) { + return *x+1; +} + +int g(int y) { + int z = y+1; + return f(&z); +} + +int main(void) { + printf("%d\n", g(1)); + return 0; +} diff --git a/content/secu_logicielle/td8-gdb/index.md b/content/secu_logicielle/td8-gdb/index.md new file mode 100644 index 0000000..58878f0 --- /dev/null +++ b/content/secu_logicielle/td8-gdb/index.md @@ -0,0 +1,452 @@ +--- +title: "Sécurité logicielle : TD8 Utiliser GDB" +date: 2023-04-07 +author: + - Yorick Barbanneau + - Gwendal Aupee +tags: ["gdb", "x86", "debug"] +categories: ["Sécurité logicielle", "TD"] +--- + +## Partie 1 + +Commençons par récupérer l'ensemble des éléments de `x` à savoir + + * son contenu (une adresse) + * le contenu pointé cette adresse + * son adresse + +``` +(gdb) p x +$1 = (int *) 0xffffd690 +(gdb) p *x +$2 = 2 +(gdb) p &x +$3 = (int **) 0xffffd680 +``` + +Regardons maintenant le contenu de la pile et retrouvons les différents +élements: + +``` +0xffffd6b8 0xf7ffcff4 +0xffffd6b4 0x00000070 +0xffffd6b0 0x00000000 +0xffffd6ac 0xf7c23295 +0xffffd6a8 0x00000000 +0xffffd6a4 0xffffd6c0 +0xffffd6a0 0x00000001 +0xffffd69c 0x00000001 +0xffffd698 0x08049198 +0xffffd694 0xffffd6a8 +0xffffd690 0x00000002 <- La valeur de x (son adresse sur la pile) +0xffffd68c ... 0xf7fc14a0 +0xffffd688 arg3 0xf7c1ca2f +0xffffd684 arg2 0xf7fd98cb +0xffffd680 arg1 0xffffd690 <- L'adresse de x (&x) +0xffffd67c ret@ 0x0804917b +0xffffd678 bp sp 0xffffd694 +``` + +La *backtrace* de `f()`: + +``` +(gdb) bt +#0 f (x=0xffffd690) at stack.c:4 +#1 0x0804917b in g (y=1) at stack.c:9 +#2 0x08049198 in main () at stack.c:13 +``` + +Adresse de retour dans pframe: + +``` +... +0xffffd680 arg1 0xffffd690 +0xffffd67c ret@ 0x0804917b <- adresse de retour de f() (vers g()) +0xffffd678 bp sp 0xffffd694 +``` + +Après un `up`, voici les adresses demandées: + +``` +Breakpoint 1, f (x=0xffffd690) at stack.c:4 +4 return *x+1; +(gdb) up +#1 0x0804917b in g (y=1) at stack.c:9 +9 return f(&z); +(gdb) p y +$1 = 1 +(gdb) p&y +$2 = (int *) 0xffffd69c +(gdb) p z +$3 = 2 +(gdb) p &z +$4 = (int *) 0xffffd690 +(gdb) +``` + +## Partie 2 + +Lançons `gdb` sur optim.02 et posons notre point d'arrêt sur `f()` + +``` +Breakpoint 1, f (x=2) at optim.c:4 +4 return x+1; +(gdb) bt +#0 f (x=2) at optim.c:4 +#1 0x080491c1 in g (y=1) at optim.c:9 +#2 0x08049068 in main () at optim.c:15 +(gdb) up +#1 0x080491c1 in g (y=1) at optim.c:9 +9 int a = f(z); +(gdb) p z +$1 = 2 +(gdb) p &z +Can't take address of "z" which isn't an lvalue. +(gdb) +``` +Effectivement `gdb` ne peut nous afficher la valeur. + +Après avoir désassemblé la fonction `g()`, nous pouvons effectivement voir que +`z` n'existe pas en tant que tel: il **est directement mis sur la pile**. + +``` + 0x080491b4 <+4>: mov 0x10(%esp),%eax + 0x080491b8 <+8>: add $0x1,%eax <- z + 0x080491bb <+11>: push %eax +``` + +Lorsqu'on essaye d'afficher `a`, *gdb* nous affiche que cette variable a été +optimisée + +``` +(gdb) p a +$2 = +``` + +En observant le code assembleur, ici encore la variable `a` n'existe que sur la +pile dans `%eax`. Le programme passe ensuite `%eax` dans `%ebx` pour le passer à +la fonction `printf` + +``` + ... + 0x080491c5 <+21>: mov %eax,%ebx + 0x080491c7 <+23>: push $0x804a008 + 0x080491cc <+28>: call 0x8049040 +``` + +Dans la version optimisée du programme, le compilateur a donc réduit au maximum +la création de variables. + +La *backtrace* complète, `a` est aussi `` : + +``` +(gdb) bt full +#0 f (x=2) at optim.c:4 +No locals. +#1 0x080491c1 in g (y=1) at optim.c:9 + z = 2 + a = +#2 0x08049068 in main () at optim.c:15 +``` + +### Partie 3 + +Nous lançons puis plaçons un point d'arrêt sur `main()`: + +``` +(gdb) b main +Breakpoint 1 at 0x804918a: file modif.c, line 12. +(gdb) r +Breakpoint 1, main () at modif.c:12 +12 int b = 1; +(gdb) wa a +Watchpoint 2: a +``` + +Après avoir continué l'exécution de notre programme, `gdb` s'arrête lors de la +modification de `a`: + +``` +[...] +(gdb) c +Continuing. +Watchpoint 2: a +Old value = -134474120 +New value = 2 +main () at modif.c:14 +14 int c = 3; +``` + +Nous pouvons maintenant afficher `%eip` et désassembler: + +``` +(gdb) p $eip +$2 = (void (*)()) 0x8049198 +(gdb) disassemble +Dump of assembler code for function main: + 0x08049179 <+0>: lea 0x4(%esp),%ecx + 0x0804917d <+4>: and $0xfffffff0,%esp + 0x08049180 <+7>: push -0x4(%ecx) + 0x08049183 <+10>: push %ebp + 0x08049184 <+11>: mov %esp,%ebp + 0x08049186 <+13>: push %ecx + 0x08049187 <+14>: sub $0x14,%esp + 0x0804918a <+17>: movl $0x1,-0xc(%ebp) + 0x08049191 <+24>: movl $0x2,-0x14(%ebp) <- initialisation de a +=> 0x08049198 <+31>: movl $0x3,-0x10(%ebp) <- %eip + ... +``` + +On peut demander à gdb de calculer l'adresse de `%ebp -14` et de la comparer +avec l'adresse de `a`: + +``` +(gdb) p $ebp - 0x14 +$8 = (void *) 0xffffd694 +(gdb) p &a +$9 = (int *) 0xffffd694 +``` + +Demandons à `gdb` de nous afficher le contenu de cette case mémoire: + +``` +#0 main () at modif.c:14 +(gdb) p/x *(int*)($ebp - 0x14) +$8 = 0x2 +``` + +Continuons maintenant l'exécution + +``` +(gdb) c +Continuing. + +Watchpoint 2: a + +Old value = 2 +New value = 3 +f (x=0xffffd694) at modif.c:5 +``` + +On peu observer l'incrémentation de `a` dans la fonction `f()`: + +``` +(gdb) disassemble +Dump of assembler code for function f: + 0x08049156 <+0>: push %ebp + 0x08049157 <+1>: mov %esp,%ebp + 0x08049159 <+3>: mov 0x8(%ebp),%eax + 0x0804915c <+6>: mov (%eax),%eax + 0x0804915e <+8>: lea 0x1(%eax),%edx <-incrémentation de a + 0x08049161 <+11>: mov 0x8(%ebp),%eax + 0x08049164 <+14>: mov %edx,(%eax) <- a est placé dans %eax +=> 0x08049166 <+16>: nop + 0x08049167 <+17>: pop %ebp + 0x08049168 <+18>: ret +End of assembler dump. +``` + +La variable `a` est incrémentée en utilisant `lea`, le résultat de cette +opération est placé dans `%ebx` avant d'être positionnée dans `%eax`. C'est ce +qui cause l'arrêt de *gdb*. + +Après avoir relancé le programme, nous obtenons l'adresse de `a`: + +``` +Breakpoint 1, main () at modif.c:12 +12 int b = 1; +(gdb) p &a +$1 = (int *) 0xffffd694 +``` + +Effectivement en positionnant un `watch` sur l'adresse de `a`, on obtient le +même comportement: + +``` +(gdb) wa *(int *)0xffffd694 +Hardware watchpoint 2: *(int *)0xffffd694 +(gdb) c +Continuing. +Hardware watchpoint 2: *(int *)0xffffd694 +Old value = -134474120 +New value = 2 +main () at modif.c:14 +14 int c = 3; +``` + +## Partie 4 + + +Exécution du programme `modif2`: + +``` +./modif2 +0xffe7d18c:1234567890 0xffe7d188:1234567680 0xffe7d17c +``` + +On voit bien que le second entier est différent du premier. Exécutons le +programme avec `gdb` en posant un *watchpoint* sur les deux entiers. Pour ce +faire nous allons poser un *breackpoint* sur `main()`, exécuter pas à pas pour +repérer l'initialisation des deux entiers et poser nos *watchpoint* dessus: + +``` +(gdb) b main +Breakpoint 1 at 0x80491ad: file modif2.c, line 13. +(gdb) r +[...] +13 int b = 1234567890; +(gdb) n +14 int c = 1234567890; +(gdb) wa b +Hardware watchpoint 2: b +(gdb) wa c +Hardware watchpoint 3: c +``` + +Nous voyons biens que ces entiers ont été initialisée avec *1234567890*. +Continuons l'exécution du programme, nous pouvons alors voir que la variable `c` +est modifiée dans `memset-sse2.S` : + +``` +[...] +Old value = 1234567890 +New value = 1234567680 +__memset_sse2_rep () at ../sysdeps/i386/i686/multiarch/memset-sse2-rep.S:173 +``` + +En désassemblant,, nous pouvons effectivement voir que nous sommes au beau +milieu de la fonction `memset`: + +``` +(gdb) disass +Dump of assembler code for function __memset_sse2_rep: +[...] + 0xf7d779fc <+108>: mov %eax,-0xd(%edx) + 0xf7d779ff <+111>: mov %eax,-0x9(%edx) + 0xf7d77a02 <+114>: mov %eax,-0x5(%edx) + 0xf7d77a05 <+117>: mov %al,-0x1(%edx) +=> 0xf7d77a08 <+120>: mov 0x8(%esp),%eax + 0xf7d77a0c <+124>: pop %ebx + 0xf7d77a0d <+125>: ret +[...] +End of assembler dump. +``` +Maintenant remontons dans la fonction appelante: + +``` +(gdb) up +#1 0x0804917d in f (x=0xffffd67c "", n=13) at modif2.c:5 +5 +(gdb) disassemble +Dump of assembler code for function f: + 0x08049166 <+0>: push %ebp + 0x08049167 <+1>: mov %esp,%ebp + 0x08049169 <+3>: sub $0x8,%esp + 0x0804916c <+6>: mov 0xc(%ebp),%eax + 0x0804916f <+9>: sub $0x4,%esp + 0x08049172 <+12>: push %eax + 0x08049173 <+13>: push $0x0 + 0x08049175 <+15>: push 0x8(%ebp) + 0x08049178 <+18>: call 0x8049050 +=> 0x0804917d <+23>: add $0x10,%esp + 0x08049180 <+26>: nop + 0x08049181 <+27>: leave + 0x08049182 <+28>: ret +End of assembler dump. +``` + +Nous allons maintenant relancer notre exécution en posant un point d'arrêt sur +l'adresse `0x08049178`. + +Lançons maintenant notre exécution: + +``` +(gdb) b * 0x08049178 +Breakpoint 1 at 0x8049178: file modif2.c, line 5. +(gdb) r +[...] +Breakpoint 1, 0x08049178 in f (x=0xffffd67c "Hello, you!", n=13) at modif2.c:5 +(gdb) p &x[0] +$3 = 0xffffd67c "Hello, you!" +(gdb) up +(gdb) up +(gdb) p &c +$5 = (int *) 0xffffd688 +(gdb) p &b +$6 = (int *) 0xffffd68c +``` + +Maintenant que nous avons les adresses, intéressons nous à la fonction `memset`. +Sa signature est `memset(void s, int c, size_t n)`, elle remplie `n` élément de +la zone mémoire `s` avec `c`. + +D'après `gdb`, voici l'appel de cette fonction dans `f()` : +`memset(x, '\0', n);` où `x` est un pointeur vers `a` et `n` est égal à *13*. + + Adresse | Variable | Memset +---------|:--------:|-------- + ... | ... | ... +0xffffd68c | b | +0xffffd68b | c | +0xffffd68a | c | +0xffffd689 | c | +0xffffd688 | c | memset[12] +0xffffd687 | a[11] | memset[11] +0xffffd686 | a[10] | memset[10] + ... | ... | ... +0xffffd67e | a[2] | memset[2] +0xffffd67d | a[1] | memset[1] +0xffffd67c | a[0] | memset[0] + +Comme nous pouvons le voir sur le tableau, le `memset` écrase le **bit de poids +faible** de notre variable `c`. Nous avons la cause de sa modification! + +Le calcul est simple : adresse de `a` + 12 bits = `0xfffd67c + 0xc = 0xfffd688`, +notre `memset` empiète bien sur `c`. + +Le bug est simple le développeur a ajouter un à `sizeof(a)` surement pour +prendre en compte le caractère `/0`, or cette opération est effectuée dès +l'initialisation de notre constante : + +``` +(gdb) frame 2 + +#2 0x080491de in main () at modif2.c:16 +16 g(a, sizeof(a) + 1); +``` + +## Partie 5 + +Effectivement, lors de l'exécution du programme avec *Valgring*, celui-ci +reporte une erreur: + +``` +[...] +==4666== Invalid write of size 1 +==4666== at 0x4049FF0: memset (in /usr/libexec/valgrind/vgpreload_memcheck-x86-linux.so) +==4666== by 0x804919C: f (modif3.c:6) +==4666== by 0x80491B6: g (modif3.c:10) +==4666== by 0x80491ED: main (modif3.c:15) +==4666== Address 0x428a02c is 0 bytes after a block of size 4 alloc'd +==4666== at 0x4040660: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-x86-linux.so) +==4666== by 0x4101315: strdup (strdup.c:42) +==4666== by 0x80491D9: main (modif3.c:14) +[...] +``` +Après avoir lancé *Valgrind* avec le paramètre `--vgdb-error=1` et lancé gdb +avec `target remote | /usr/bin/vgdb --pid=`, nous pouvons observer la même +erreur que pour la partie précédente: + +``` +(gdb) bt +#0 0x04049ff0 in _vgr20210ZZ_libcZdsoZa_memset () from /usr/libexec/valgrind/vgpreload_memcheck-x86-linux.so +#1 0x0804919d in f (x=0x428a028 "", n=5) at modif3.c:6 +#2 0x080491b7 in g (y=0x428a028 "", n=5) at modif3.c:10 +#3 0x080491ee in main () at modif3.c:15 +(gdb) frame 3 +#3 0x080491ee in main () at modif3.c:15 +15 g(a, sizeof(a)+1); +``` + +Un a été ajouté à `sizeof(a)` **alors qu'il ne faut pas**.