Analyse du contournement de KTRR

Magazine
Marque
MISC
Numéro
102
|
Mois de parution
mars 2019
|
Domaines


Résumé

En mars 2018, le chercheur Luca Todesco (@qwertyoruiop) a publié le code de son exploit qui contourne le mécanisme de protection d'intégrité du noyau iOS sur l'iPhone 7. Ce contournement affecte les versions d'iOS 10 et 10.1. Bien qu'il ne permette pas de modifier directement le code du noyau, il rend possible le changement des valeurs du segment de données constantes, avec les conséquences que nous expliquerons.


Body

 

Introduction

Apple profite souvent de la sortie des nouveaux modèles d'iPhone pour renforcer la sécurité de son système. Avec l'arrivée de l'iPhone 7, un nouveau mécanisme de protection d'intégrité du noyau a été ajouté. Appelé KTRR (vraisemblablement Kernel Text Region Range), il succède à l'ancien mécanisme Watchtower (aussi appelé KPP) présent sur les iPhones 5S jusqu'au 6S sous iOS 9 et supérieur. Le rôle d'un tel mécanisme est d'empêcher la modification de données dans les segments du noyau que l'on souhaite protéger. C'est une mesure préventive, qui permet de restreindre le champ d'action d'un attaquant qui est capable d'aller lire et écrire dans la mémoire du noyau, après l'avoir exploité. Dans le cas d'un jailbreak, après avoir obtenu la capacité d'aller lire et écrire dans la mémoire du noyau, la voie la plus rapide pour parvenir à ses fins est d'aller y modifier code et données. Avec un mécanisme comme KTRR, cela n'est plus possible. Afin de comprendre la manière dont fonctionne l'exploit, nous expliquons dans un premier temps le fonctionnement de KTRR. Nous verrons le déroulement de l'exploit, avant de nous intéresser aux parties marquantes.

1. Prérequis

Pour comprendre les explications qui suivent, il est fortement recommandé au lecteur d'être familier avec l'architecture ARMv8-A [1] et plus particulièrement avec le mécanisme de traduction d'adresses [2]. Les liens vers la documentation correspondante ainsi que le code [3] de l'exploit sont disponibles à la fin de l'article.

2. Principe de fonctionnement de KTRR

Deux nouveaux éléments font leur apparition pour mettre en place le mécanisme. Le premier est une MMIO (Memory Mapped I/O) nommée RoRgn (Read-only Region) qui empêche l'accès en écriture à une zone de la mémoire physique. Les registres de cette MMIO sont situés dans la structure AMCC (Apple's Memory Cache Controller) comme le montre le code du noyau [4] :

//#if defined(KERNEL_INTEGRITY_KTRR)

#define rMCCGEN        (*(volatile uint32_t *) (amcc_base + 0x780))

#define rRORGNBASEADDR (*(volatile uint32_t *) (amcc_base + 0x7e4))

#define rRORGNENDADDR  (*(volatile uint32_t *) (amcc_base + 0x7e8))

#define rRORGNLOCK     (*(volatile uint32_t *) (amcc_base + 0x7ec))

//#endif

Une fois que les adresses de base et de fin sont initialisées, les registres sont verrouillés en plaçant 1 dans rRORGNLOCK. Le second élément apparu est le groupe des registres KTRR : ARM64_REG_KTRR_LOWER_EL1, ARM64_REG_KTRR_UPPER_EL1 et ARM64_REG_KTRR_LOCK_EL1. Ils sont présents pour limiter l'ensemble des adresses exécutables en EL1 (niveau de privilège du noyau). De la même manière que pour RoRgn, en mettant 1 dans le registre ARM64_REG_KTRR_LOCK_EL1, les deux autres registres sont verrouillés. Après le verrouillage des registres RoRgn et KTRR, l'agencement des segments mémoire du noyau est le suivant :

 

Mapping

Segment

Protection

Accès possible

r--/r--

__PRELINK_TEXT

RoRgn + KTRR

r--/r--

r-x/r-x

__PLK_TEXT_EXEC

RoRgn + KTRR

r-x/r-x

r--/r--

__PLK_DATA_CONST

RoRgn + KTRR

r--/r--

r-x/r-x

__TEXT

RoRgn + KTRR

r-x/r-x

rw-/rw-

__DATA_CONST

RoRgn + KTRR

r--/r--

r-x/r-x

__TEXT_EXEC

RoRgn + KTRR

r-x/r-x

rw-/rw-

__LAST

RoRgn

r--/r--

rw-/rw-

__KLD

 

rw-/rw-

rw-/rw-

__DATA

 

rw-/rw-

rw-/rw-

__BOOTDATA

 

rw-/rw-

r--/r--

__LINKEDIT

 

r--/r--

rw-/rw-

__PRELINK_DATA

 

rw-/rw-

rw-/rw-

__PRELINK_INFO

 

rw-/rw-

Il faudrait mettre en gras (ou le style prévus) le texte de la première rangée du tableau (Mapping, Segment, Protection et Accès possible).

Le segment __LAST est particulier : il inclut la section __pinst (vraisemblablement protected ou privileged instructions) qui contient les instructions permettant de changer la valeur de certains registres système, comme TTBR1_EL1 et SCTLR_EL1. Ce segment n'étant pas compris dans l'intervalle défini par les registres KTRR, on ne peut pas y accéder en exécution lorsque la MMU est active (car il faut être dans l’intervalle défini par KTRR pour être exécutable). Ainsi, le noyau définit la valeur de ces registres avant l'activation de la MMU.

3. Description de la vulnérabilité

Lors de la sortie de veille du CPU, une structure accessible en écriture depuis le noyau (elle ne réside pas dans un segment protégé par RoRgn) permet de détourner le flot d'exécution. De plus, l'intervalle défini par les registres KTRR à ce moment-là n'est pas correct, car il inclut __LAST.pinst. Il est alors possible de créer un mapping alternatif pour les segments de données accessibles en lecture seule. L'accès en écriture à ce segment peut faciliter le jailbreak et altérer le fonctionnement du système iOS, chose qu'Apple ne souhaite en aucun cas permettre.

4. Déroulement de l'exploit

Pour mieux saisir le contexte d'exécution de l'exploit ainsi que son fonctionnement, il est possible de se référer à la Figure 1 qui donne une vision plus globale. Il est important de noter qu'un premier exploit visant le noyau est nécessaire (première étape), avant de réaliser celui de KTRR.

 

deroulement

 

Figure 1 : Contexte et séquence de l’exploit

4.1 Phase préparatoire

Avant d'exécuter la charge pour contourner KTRR, plusieurs opérations sont réalisées depuis l'espace utilisateur pour permettre à l'exploit de correctement fonctionner. On peut notamment citer :

  • l'allocation de pages de mémoire pour y placer les gadgets, ainsi que les données utilisées lors de l'exploit ;
  • la préparation de la table de traduction TTBR0_EL1 (modification et ajouts de quelques entrées dans la table de niveau 3) qui sera utilisée par le noyau lors du reset et plus particulièrement, lors de la phase d’exploitation ;
  • la création d'une fausse table de traduction (celle pointée par TTBR1_EL1) visant à remplacer l'originale.

4.2 Lancement de l'exploit

Si l’on s’intéresse au code exécuté lors de la sortie de veille du CPU, on remarque que la fonction common_start utilise la structure _const_boot_args pour convertir l'adresse physique de la fonction arm_init_tramp en adresse virtuelle avant de s'y rendre :

// Load VA of the trampoline

adrp    x0, arm_init_tramp@page

add     x0, x0, arm_init_tramp@pageoff

add     x0, x0, x22 ; x0 += _const_boot_args.BA_VIRT_BASE

sub     x0, x0, x23 ; x0 -= _const_boot_args.BA_PHYS_BASE

// Branch to the trampoline

br      x0

Cette structure à la particularité d’être présente dans un segment accessible en écriture. Dès lors que l’on est en possession d’une primitive d'écriture dans la mémoire du noyau (cf. étape 1 de la Figure 1), il sera possible d’y modifier ses éléments. Si l'on regarde le code ci-dessus, cela signifie que l'on contrôle les registres x22 et x23. Les opérandes du calcul d'obtention de x0 étant connus, la modification du registre x22 ou x23 permettra de rediriger le flot d'exécution du noyau.

C’est exactement de cette manière que le lancement procède :

swritewhere = loadstruct + 8;

swritewhat = (phyzb+gadget0_off)-(G(TTBRMAGIC_BX0)+slide-gVirtBase);

/*...*/

WriteAnywhere64(swritewhere, swritewhat);

Le code ci-dessus est une partie du code exécuté depuis l'espace utilisateur. Après la phase préparatoire, cette écriture permet d'amorcer l'exploit. La fonction WriteAnywhere64 est la primitive d'écriture dans la mémoire du noyau. La variable loadstruct contient l'adresse de la structure _const_boot_args. À son offset 8 se trouve BA_VIRT_BASE (la base virtuelle du noyau).

L'écriture dans loadstruct + 8 avec la fonction WriteAnywhere64 va remplacer _const_boot_args.BA_VIRT_BASE par une valeur contrôlée. Dans notre cas, le registre x0 contiendra phyzb+gadget0_off. La MMU étant active, la traduction s'effectuera sous le contrôle de l'attaquant (car c'est TTBR0_EL1 qui sera utilisé) et dirigera le branchement vers le premier gadget. Il ne reste plus que la sortie de mise en veille (le reset) de l'appareil survient pour que l'exploit se lance.

Il faut cependant faire attention, car il y a un autre endroit où _const_boot_args.BA_VIRT_BASE est utilisé :

    // Set up the exception vectors

    adrp    x0, EXT(ExceptionVectorsBase)@page

    add     x0, x0, EXT(ExceptionVectorsBase)@pageoff

    add     x0, x0, x22 ; x0 += _const_boot_args.BA_VIRT_BASE

    sub     x0, x0, x23 ; x0 -= _const_boot_args.BA_PHYS_BASE

    MSR_VBAR_EL1_X0

La valeur calculée de VBAR_EL1 sera fausse, il faudra donc penser à changer la valeur erronée de VBAR_EL1 par la valeur d'origine.

4.3 Exploitation

Toute la phase d'exploitation se fait en JOP et ROP (Jump-Oriented Programming et Return-Oriented Programming), car au moment où le flot d'exécution est redirigé, KTRR est actif. Ainsi, seul le code du noyau est exécutable. Le point le plus important pour l'exploit est la réalisation d'une copie de la table de traduction d'origine du noyau, et de faire en sorte que la copie qui est modifiable remplace l'originale. On peut ainsi re-mapper en lecture et écriture les segments de données comme __DATA_CONST. Après cela, l'exploit rend la main au noyau iOS pour que le reset reprenne le cours normal de son exécution. Le Figure 2 montre en rouge la partie correspondant à l'exploitation.

 

exploit

 

Figure 2 : Exécution de l’exploit lors du reset

5. Quelques points techniques d'intérêt

5.1 Re-mapping des instructions protégées

L'opération suivante a lieu lors de la phase préparatoire. Même avec le mauvais intervalle des registres KTRR, les instructions du segment __LAST ne sont pas encore exécutables, car elles ne sont accessibles qu'en lecture seule (voir le tableau sur le mapping des segments). Pour invoquer l'instruction capable de changer le registre TTBR1_EL1, il faut donc re-mapper la page en lecture et exécution (rappelons que l'écriture n'est pas possible, car le segment __LAST est inclus dans l'intervalle défini par RoRgn). Cela est réalisé avec la table de traduction de TTBR0_EL1, qui est modifiable. En effet, un branchement vers une adresse physique translatée par TTBR0_EL1 nous amènera à l'instruction souhaitée.

5.2 Détails sur l'exécution de l'exploit

Le premier enchaînement de gadgets à être exécuté va servir à allouer de la place sur la pile, pour y copier une chaîne de ROP avec le gadget suivant :

    LDR             X8, [X8,#0x10] ; memmove + 4 // pour contrôler l'adresse de retour

    ADD             X0, SP, #8

    BLR             X8 ; memmove((SP + 8), chain + 8, ((0x1F * 80) - 8))

Une fois cette chaîne copiée, le flot d'exécution sera redirigé vers celle-ci comme le montre l'épilogue de la fonction memmove :

    LDP             X29, X30, [SP],#0x10

    RET

À partir de là, plusieurs opérations seront effectuées par la chaîne de ROP. Elle commence tout d’abord par restaurer VBAR_EL1, car le noyau s’est basé sur la valeur contenue dans _const_boot_args.BA_VIRT_BASE pour calculer l’adresse virtuelle. Après cela, la fonction __pinst_set_ttbr1 (présente dans le segment _LAST) est appelée pour changer la valeur du registre TTBR1_EL1. Ces deux tâches achevées, il faut s’occuper de rendre la main au noyau. Pour que la reprise puisse se faire normalement, le registre LR doit pointer vers la bonne fonction d’initialisation (arm_init_cpu ou arm_init_idle_cpu). En effet, lors du reset, suivant le type de veille dont on sort, la gestion sera déléguée à l’une des deux fonctions. Pour faire ce choix, la structure cpu_data est utilisée. On y trouve à l'offset 0x130 le CPU_RESET_HANDLER qui indique la fonction devant être lancée par arm_init_tramp une fois l’exploit terminé.

6 Patch

À partir d'iOS 10.2, l'intervalle attribué aux registres KTRR lors du reset CPU est identique à celui du démarrage et donc correct :

// program and lock down KTRR

// subtract one page from rorgn_end to make pinst instructions NX

 

msr     ARM64_REG_KTRR_LOWER_EL1, x17

sub     x19, x19, #(1 << (ARM_PTE_SHIFT-12)), lsl #12

msr     ARM64_REG_KTRR_UPPER_EL1, x19

mov     x17, #1

msr     ARM64_REG_KTRR_LOCK_EL1, x17

De plus, la structure qui permettait de rediriger le flot d'exécution n'est plus accessible en écriture, car elle réside dans le segment __DATA_CONST.

Conclusion

Nous avons vu comment la protection mise en place par KTRR a pu être partiellement contournée. Un tel exploit montre qu'il est devenu assez difficile de contourner les nouveaux mécanismes de protection, cela malgré les erreurs d'implémentation. Nul doute qu'avec les nouvelles sécurités apparues sur les derniers iPhones, les exploits publics se feront de plus en plus rares.

Remerciements

Je souhaite remercier Fred Raynal pour ses relectures et les conseils prodigués. Je remercie également Jean-Baptiste Bédrune, Joffrey Guilbon et Cédric Tessier pour leurs précieux retours, ainsi que Luca Todesco pour avoir publié un exploit des plus intéressants.

Références

[1] Manuel de référence de l’architecture ARMv8-A : https://static.docs.arm.com/ddi0487/da/DDI0487D_a_armv8_arm.pdf

[2] Traduction d’adresses sous ARMv8-A : https://static.docs.arm.com/100940/0100/armv8_a_address%20translation_100940_0100_en.pdf

[3] Code de l’exploit : http://yalu.qwertyoruiop.com/y7.txt

[4] Définition de RoRgn dans le noyau XNU : https://opensource.apple.com/source/xnu/xnu-4570.1.46/pexpert/pexpert/arm64/AMCC.h.auto.html

 

Sur le même sujet

L’intégration du « Privacy by Design » et de la SSI dans la gestion de projets en mode V ou Agile

Magazine
Marque
MISC
Numéro
106
|
Mois de parution
novembre 2019
|
Domaines
Résumé

L’analyse de l’actualité ne cesse de nous alerter sur la très faible prise en compte de la sécurité native dans un grand nombre de projets et plus particulièrement sur la sous-estimation de l’intégration des exigences de protection de la vie privée.Les articles 25 du RGPD « Protection des données dès la conception et protection des données par défaut » et 32 « Sécurité du traitement », formalisent l’obligation pour le responsable du traitement de prendre en compte les exigences juridiques et techniques pendant toutes les phases des projets de la conception jusqu’à la fin de vie du système cible.Nous nous attacherons à identifier les principaux acteurs concernés et leurs modes de concertation dans les gestions de projets en V ou Agile.Nous chercherons à souligner les points d’attention et d’amélioration dans les deux méthodes.

F-Droid - Votre catalogue d’applications libres et open source

Magazine
Marque
Linux Pratique
Numéro
116
|
Mois de parution
novembre 2019
|
Domaines
Résumé

Si vous utilisez un terminal Android, vous avez sans doute pour habitude de lancer par défaut le Play Store de Google pour trouver de nouvelles applications à installer sur votre appareil. Saviez-vous cependant qu’il existait un autre catalogue dont la particularité est de recenser toutes les applications mobiles libres et open source développées pour la plateforme Android ? Il se nomme F-Droid et pourra être installé rapidement sur votre mobile/tablette. 

Richelieu : solution du challenge de la DGSE

Magazine
Marque
MISC
Numéro
106
|
Mois de parution
novembre 2019
|
Domaines
Résumé

En mai 2019, la DGSE (Direction Générale de la Sécurité Extérieure) a organisé pour la première fois un challenge de sécurité informatique ouvert au public, qu’elle a baptisé Challenge Richelieu. Je vous invite à écouter la bande originale du Bureau des Légendes en fond sonore. Imprégnez-vous de cette atmosphère pleine de tension où les secrets sont rois, puis regardons ensemble ce que nous réservait ce challenge.

Auditer la sécurité d'une application iOS

Magazine
Marque
MISC
Numéro
106
|
Mois de parution
novembre 2019
|
Domaines
Résumé

Auditer la sécurité d'une application iOS n'est toujours pas une tâche aisée. Force est de constater que la plupart des auditeurs, amateurs de bug bounty ou autres curieux préfèrent travailler sur les applications Android malgré les récentes protections ajoutées au système d'exploitation de Google. Nous allons malgré tout essayer de présenter une méthodologie qui rend possible l'analyse orientée sécurité d'une application iOS, même sans jailbreak. Un bref rappel sera effectué pour ensuite introduire quelques outils et documentations apparues ces derniers mois.

Sondes de détection : performances, évaluations et biais

Magazine
Marque
MISC
Numéro
106
|
Mois de parution
novembre 2019
|
Domaines
Résumé

En avril 2019, l’ANSSI a qualifié les premières sondes pour assurer la supervision de sécurité de réseaux. Les opérateurs d’importance vitale (OIV), les opérateurs de services essentiels (OSE) et, d’une manière générale, les organismes opérant des fonctions sensibles disposent ainsi de produits français de confiance : Cybels Sensor de Thales et Trackwatch Full Edition de Gatewatcher.La méthodologie d’évaluation des sondes n’est, hélas, pas publique. Les ingénieurs sécurité et réseau devant intégrer ces sondes ne disposent donc pas de guides pour effectuer la recette de leur efficacité en production. Cet article propose un retour d’expérience sur l’évaluation des sondes, notamment sous l’angle de la performance. Cet aspect est, en effet, particulièrement significatif puisque le taux de détection d’une sonde diminue si elle est submergée, quand bien même elle serait équipée des meilleurs signatures et moteurs d’analyse.

Par le même auteur

Analyse du contournement de KTRR

Magazine
Marque
MISC
Numéro
102
|
Mois de parution
mars 2019
|
Domaines
Résumé

En mars 2018, le chercheur Luca Todesco (@qwertyoruiop) a publié le code de son exploit qui contourne le mécanisme de protection d'intégrité du noyau iOS sur l'iPhone 7. Ce contournement affecte les versions d'iOS 10 et 10.1. Bien qu'il ne permette pas de modifier directement le code du noyau, il rend possible le changement des valeurs du segment de données constantes, avec les conséquences que nous expliquerons.