Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Android
  3. Tests sous l'architecture ARM
Extrait - Android Les fondamentaux de la sécurité des smartphones et tablettes
Extraits du livre
Android Les fondamentaux de la sécurité des smartphones et tablettes
1 avis
Revenir à la page d'achat du livre

Tests sous l'architecture ARM

Introduction

Dans ce chapitre, nous allons étudier les bases fondamentales des processeurs ARM (Advanced Risc Machine) ainsi que les différents types de vulnérabilités existant dans le monde ARM. L’exploitation des failles de sécurité sous l’architecture ARM (comme sous d’autres architectures) constitue un des piliers fondamentaux du hacking éthique. La plupart des smartphones Android, téléviseurs tels que les Smart TV, calculatrices, certains robots ménagers et autres objets connectés fonctionnent aujourd’hui avec des processeurs ARM. Il est donc essentiel pour un testeur en sécurité Android de bien comprendre l’architecture ARM ainsi que les risques de sécurité qui y sont associés.

Le hacking éthique couvre une vaste gamme de méthodes permettant d’accéder à des données personnelles ou de prendre le contrôle de systèmes informatiques sans le consentement légal de leur propriétaire. L’exploitation de failles applicatives est cependant une branche spécifique du hacking éthique qui implique de comprendre la manière dont un programme déjà compilé et exécuté sur un système fonctionne.

L’exploitation binaire (ou de programme compilé) implique souvent la recherche de vulnérabilités...

Le processeur ARM

Les processeurs ARM (Advanced Risc Machine), où RISC signifie Reduced Instruction Set Computer, ont vu le jour dans l’année 1983 grâce à la société britannique Acorn Computer (ARM signifiait alors Acorn Risc Machine), qui les a utilisés pour la première fois à partir de 1987 dans son type d’ordinateur Archimedes 32 bits. C’est une famille de processeurs à jeux d’instructions réduites, ce qui signifie qu’ils ne pourront exécuter qu’un nombre réduit de types d’instructions. Leur architecture est en effet moins complexe que sur d’autres familles de processeurs, ce qui facilitera notre apprentissage. De ce fait, ce type de processeur offre des performances élevées tout en restant cependant très compact. Il est de plus en plus miniaturisé (ce qui est très pratique pour être contenu dans un smartphone) et de plus en plus puissant. Il est très peu gourmand en énergie, ce qui est également un gros avantage, il nous fait donc gagner de l’autonomie de batterie. Avec tous ces avantages, le processeur ARM est très prisé, à tel point qu’il commence à se déployer de plus en plus sur les ordinateurs portables. Quelques systèmes d’exploitation sont déjà compatibles avec les processeurs ARM....

Notions fondamentales

Dans cette section, nous allons étudier quelques notions fondamentales et indispensables avant d’aller plus loin. En effet, nous avons tout d’abord besoin de savoir de quoi le processeur ARM est composé, comment il fonctionne, les différents registres utilisés, etc. Nous utiliserons l’environnement ARM 32 bits, donc avant la version ARM v8. À partir des versions ARM v8, les processeurs sont basés sur du 64 bits.

1. Les différents modes de fonctionnement

Le processeur ARM prend en charge plusieurs modes de fonctionnement, sept, pour être précis.

1. Mode utilisateur (US) : il s’agit de l’état d’exécution normal du programme ARM. Il est utilisé pour exécuter la plupart des programmes d’applications.

2. Mode d’interruption rapide (FIQ) : comme son nom l’indique, il est utilisé pour gérer les interruptions rapides.

3. Mode Superviseur (SVC) : c’est un mode protégé pour le fonctionnement du système d’exploitation.

4. Mode Abort (ABT) : il est utilisé quand une interruption est signalée par le système de mémoire suite à une impossibilité de charger une instruction ou des données.

5. Mode d’interruption (IRQ) : il est utilisé pour gérer les interruptions générales de manipulation, donc pour les interruptions à usage général.

6. Mode système (SYS) : il s’agit du mode utilisateur privilégié pour les utilisateurs système d’exploitation.

7. Mode non défini (UND) : il est utilisé quand une instruction indéfinie est exécutée sur le système et qu’une exception se produit.

De tous les modes, le mode utilisateur (USR) est le seul mode non privilégié alors que tous les autres représentent le mode privilégié de l’architecture ARM. Les modes privilégiés sont utilisés pour traiter les interruptions ou les exceptions ainsi que pour accéder aux ressources protégées.

Le mode système (SYS) peut être utilisé à partir d’un autre mode privilégié en modifiant le bit de mode du registre d’état du programme actuel...

Un peu d’assembleur

Le langage assembleur est également très important pour une meilleure compréhension. Il est important de savoir que les codes opération ARM ont une taille de 32 bits et sont alignés sur les mots, c’est-à-dire que leur adresse est divisible par 4 et qu’une instruction contiendra 4 octets. Les bits du compteur de programme (PC) sont toujours à zéro, car les instructions ARM sont toujours alignées sur les mots (faisant 4 octets). Cependant, dans le cas d’autres jeux d’instructions tels que Thumb, les instructions sont alignées sur un demi-mot et ne feront donc plus que 2 octets.

1. Les instructions de traitement de données

Il y a tout d’abord les instructions de traitement de données pouvant être divisées en quatre grandes catégories :

  • les instructions arithmétiques,

  • les instructions logiques,

  • les instructions de comparaison,

  • les instructions SIMD (Single Instructions Multiples Data).

Les instructions arithmétiques

Celles-ci effectuent une opération arithmétique sur deux opérandes sources au maximum et écrivent le résultat dans un registre de destination. Elles peuvent également éventuellement mettre à jour les indicateurs de code de condition, en fonction du résultat. Parmi les deux opérandes sources, l’un est toujours un registre, l’autre peut avoir deux formes de base : il peut s’agir d’une valeur immédiate d’un registre ou d’une valeur de registre décalée. Si l’opérande est un registre décalé, la quantité de décalage peut être une valeur immédiate ou la valeur d’un autre registre.

Les opérations arithmétiques générales comprennent les additions (ADD), les soustractions (SUB), les soustractions inverses (RSB), les additions avec retenue (ADC), les soustractions avec retenue (SBC) et les soustractions inverses avec retenue (RSC).

Voyons un exemple d’utilisation : op{cond} Rd, Rs, Opérande2

Dans cet exemple, op peut effectuer des actions telles que ADD, SUB, RSB, ADC, SBC ou RSC, cond est un code conditionnel facultatif et "s" est le suffixe facultatif. Si "s" est spécifié, les indicateurs de code de condition...

Préparation de l’environnement

Il est temps de passer à l’installation et à la préparation de notre environnement de test sous l’architecture ARM. Pour les utilisateurs possédant déjà ce type d’architecture, il faudra simplement installer un environnement de programmation. Pour les autres, la procédure est un peu longue, comme toutes les préparations d’environnements de tests. Pour effectuer tous les exercices dans la section à venir sur la plateforme ARM, vous pouvez soit configurer un environnement virtuel basé sur l’architecture ARM, soit acheter des appareils ARM tels que Raspberry Pi, BeagleBoard, PandaBoard, ARMini, Cubox, etc. Dans cette section, nous verrons comment configurer un environnement de test virtuel pour réaliser nos exploitations basées sur ARM.

Téléchargement et installation

Commençons par télécharger la dernière version d’Ubuntu pour travailler dans cet environnement. Actuellement, il s’agit de la version 18.04.2 LTS (Long-Term Support), disponible en téléchargement sur le site officiel à cet endroit : https://www.ubuntu.com/download/desktop

Dans le chapitre Tester des applications mobiles, nous avons vu comment installer Santoku-Linux dans VirtualBox ; nous procéderons à l’installation d’Ubuntu de la même manière. Quand nous aurons installé Ubuntu dans VirtualBox, nous mettrons le système...

Buffer overflow

Le buffer (tampon en français) est une zone ou un espace mémoire temporaire utilisé pour stocker des données. Le débordement de la mémoire tampon se produit généralement lorsque les données écrites dans celle-ci sont plus grandes que la taille de la mémoire tampon allouée. Alors, en raison de contrôle insuffisant, elles débordent de la mémoire tampon et écrivent dans des emplacements de mémoire adjacents, alors qu’elles peuvent être des données importantes utilisées par le système ou le programme ou encore des adresses de retour, etc. Les variables locales agissent généralement comme des variables ou des tampons. En cas de débordement de mémoire tampon, l’objectif principal est de modifier, en quelque sorte, l’adresse de retour et de prendre le contrôle du flux d’exécution du programme en contrôlant le registre de liaison (LR, Link Register). Une fois que nous avons le contrôle de l’adresse de retour, nous pouvons prendre le contrôle de l’ensemble du programme et lui faire exécuter les actions souhaitées, qui sont généralement du code malicieux.

Nous avons codé précédemment un programme vulnérable qui est un cas d’école basique, comme indiqué dans le code ci-dessous, ayant une fonction nommée vulnerable. Cette fonction sera vulnérable aux attaques de débordement de mémoire tampon, car elle utilise la fonction strcpy pour copier la mémoire tampon dans la mémoire tampon de variable locale, qui a une taille de 10 octets pour cet exemple. Donc, si nous entrons une chaîne de longueur supérieure à 10 octets et que nous la copions dans un tampon, il sera alors possible de provoquer un débordement de tampon et d’écrire dans les emplacements de mémoire adjacents. Notre objectif ici sera de remplacer (ou plutôt de réécrire) l’adresse de retour de la fonction vulnérable.

Avant de démarrer, nous devons désactiver quelques protections, que nous pourrons réactiver ensuite. Nous ouvrons le terminal et tapons les commandes suivantes :

echo 0>/proc/sys/kernel/randomize_va_space 

Commençons...

Return to Libc (Ret2Libc)

Nous allons faire une petite présentation de Ret2Libc, car il n’est pas toujours possible d’exploiter ou de corrompre la mémoire en utilisant cette technique (nous développerons un peu plus en détail dans la suite). Return to Libc (Ret2Libc) est une technique utilisée pour contourner la protection de pile (stack) non exécutable. La bibliothèque Libc est mappée dans l’espace mémoire de la plupart des programmes. Comme nous l’avons vu pour les buffer overflows, une fois que nous pouvons corrompre et contrôler la pile, il est possible de faire pointer l’adresse de retour sur une fonction de la bibliothèque (ou librairie) Libc. Pour utiliser cette technique, nous utilisons généralement la fonction system() de la bibliothèque Libc, qui prend comme argument "/bin / sh", pour obtenir un shell qui permettra ensuite d’exécuter des actions malveillantes sur le système cible.

Comme nous l’avons mentionné en début de section, la technique Return to Libc n’est généralement pas possible sur ARM, car dans l’architecture x86, les arguments d’une fonction sont uniquement transmis à la pile, tandis que dans le cas des processeurs ARM, les arguments de la fonction doivent être placés dans des registres, ce qui nous pose problème, car nous ne pouvons écraser la pile qu’en utilisant la technique de débordement de tampon et nous n’avons aucun contrôle sur les valeurs du registre. Alors afin de placer les arguments dans les registres, nous devons adopter une approche différente. Nous devons en premier lieu revenir à un emplacement de mémoire qui contient le code pour charger les registres. Une fois que nous avons chargé les registres avec les arguments requis, nous devons prendre le contrôle de notre programme sur la fonction Libc souhaitée, qui est system() dans notre cas. Une de ces approches et récentes techniques a été discutée lors de la conférence DEFCON à Las Vegas en 2012 par un chercheur en sécurité afin de contourner ce problème. Cette technique a été nommée Ret2ZP (Return to zero protection). Le chercheur nommé Itzhak (Zuk) Avraham  utilise...

Conclusion

Dans ce chapitre, nous avons vu les techniques basiques des attaques de type buffer overflow ; il y en a bien sûr d’autres. L’essentiel ici est d’avoir les bases et les connaissances nécessaires pour pouvoir progresser dans ce domaine pour les utilisateurs qui le souhaitent. Comme nous savons que les composants internes d’Android sont majoritairement codés en C, nous pouvons très bien imaginer que nous puissions trouver une faille de ce type dans un futur proche. Les failles de sécurité surviennent généralement lorsque le développeur d’application n’applique pas les règles fondamentales du développement de code sécurisé, ce qui permet à un attaquant d’exploiter les faiblesses du code applicatif et ainsi de prendre le contrôle du flux d’exécution d’un programme et d’effectuer des actions malveillantes.