La team derrière le jailbreak Electra vient de publier la version initiale permettant d’installer Cydia, pour ainsi appliquer des tweaks et thèmes. Ce post n’est pas un tutoriel pour installer et utiliser cet outil, mais une analyse du jailbreak à proprement parler.
Electra
Electra était initialement un jailbreak pour les versions de iOS 11 et de iOS 11.1.x. Cette première version utilisait l’exploit async_wake de Ian Beer pour appeler la fonction task_for_pid(0)
.
Electra permet d’installer Cydia, un serveur SSH et d’injecter du code dans des applications et processus à l’aide de Substitute.
Contrairement à la version actuelle, Electra pour iOS 11.1.2 est disponible sur Github sous licence open source. A l’aide du code source de la première version de Electra et d’un désassembleur on va analyser cet outil.
Electra est un jailbreak que l’on appelle KPPLess. En effet, pour supporter tous les appareils sous iOS 11.x il n’est pas possible d’utiliser le contournement de KPP utilisé dans Yalu et DoubleHelix car les appareils récents comme l’iPhone 7, 8 et X ont une nouvelle protection hardware que j’ai détaillé ici
Exploitation
Les deux exploits utilisés permettent la même chose : appeler la fonction task_for_pid()
avec le PID 0, qui correspond au noyau. Par défaut ceci n’est bien évidemment pas possible. Ces exploits vont ainsi permettre de lire et d’écrire dans la mémoire du noyau.
multi_path
Le premier exploit MPTCP a été détaillé pour la première fois par Eloi Vanderbeken. Le bug exploité est un Heap overflow, dû à la mauvaise vérification de limites dans le protocole MPTCP.
Le protocole MPTCP (Multi Path TCP) permet d’utiliser une même connexion TCP au travers de plusieurs interfaces réseau. Dans le cas de l’iPhone, il est utilisé par Siri pour communiquer en même temps par les données en cellulaires et Wifi.
Le protocole a été rendu accessible aux développeurs avec iOS 11 et nécessite un compte dev pour utiliser les sockets MPTCP et déclencher l’exploit.
Le premier exploit disponible a été implémenté par John Åkerblom, puis un second exploit a été publié par Ian Beer. Les exploits sont similaires, mise à part que celui de Ian Beer fait un overflow de 3 octets, alors que celui John Åkerblom n’overflow que 2 octets et les remplace par d’autres objets différents.
Ian a détaillé plus précisément sont exploit ici.
empty_list
Empty_list a été publié plus tard par Ian Beer. Le bug en question est qu’il manque une simple vérification. En effet si la taille de la mémoire allouée du buffer est inférieure à la taille minimum requise, le programme va passer les différentes vérifications et continuer son chemin. Cela va déclencher un Heap overflow.
Voici le PoC de Ian Beer en une vingtaine de lignes :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/attr.h>
int main() {
int fd = open("/", O_RDONLY);
if (fd == -1) {
perror("unable to open fs root\n");
return 0;
}
struct attrlist al = {0};
al.bitmapcount = ATTR_BIT_MAP_COUNT;
al.volattr = 0xfff;
al.commonattr = ATTR_CMN_RETURNED_ATTRS;
size_t attrBufSize = 16;
void* attrBuf = malloc(attrBufSize);
int options = 0;
int err = fgetattrlist(fd, &al, attrBuf, attrBufSize, options);
printf("err: %d\n", err);
return 0;
}
L’exploit lui nous permet d’exécuter le task port pour le processus 0, qui correspond au noyau. On peut ainsi lire et écrire dans la mémoire du noyau à l’aide des fonctions implémentées dans kmem.h
.
Je ne vais pas m’attarder sur l’exploit puisqu’il a déjà été documenté et compréhensible si vous avez un peu d’expérience avec XNU et iOS.
Le seul problème avec cet exploit est qu’il pas stable, et pour l’exécuter correctement il faut au moins 5 ou 6 essais. D’après Ian Beer il a 30% de réussite contrairement à multi_path qui est à 80-90%.
Post exploitation
UID : 0
La première étape classique dans une escalade de privilèges est d’être root. Et contrairement à MacOS, ce n’est que le début. En effet être root sur un iPhone ou iPad est très limité.
D’après le code suivant, voici comment on obtient les autorisations du noyau pour avoir l’UID 0 et empêchant la fonction setuid(0)
de crash à l’aide de la macro KCALL
qui permet d’appeler des fonctions depuis le noyau.
1
2
3
4
5
6
7
8
9
10
11
// Properly copy the kernel's credentials so setuid(0) doesn't crash
uint64_t kern_ucred = 0;
KCALL(find_copyout(), kern_proc+0x100, &kern_ucred, sizeof(kern_ucred), 0, 0, 0, 0);
uint64_t self_ucred = 0;
KCALL(find_copyout(), our_proc+0x100, &self_ucred, sizeof(self_ucred), 0, 0, 0, 0);
KCALL(find_bcopy(), kern_ucred + 0x78, self_ucred + 0x78, sizeof(uint64_t), 0, 0, 0, 0);
KCALL(find_bzero(), self_ucred + 0x18, 12, 0, 0, 0, 0, 0);
setuid(0);
Puis à l’aide de setuid()
on configure le processus avec le PID 0, pour être root.
AMFID
Amfid est le démon qui interface avec la kext AMFI qui s’occupe de gérer tout ce qui est relatif aux signatures de code et l’autorisation d’éxecution de code signé sur iOS. En me basant sur le code et les commentaires, voici comment fonction le contournement :
- Calculer les CD Hashes pour les ressources qui sont ciblées
- Créer un trust chain (chain de confiance) personnalisée
- Insérer la trust chain dans la trust chain déjà existante en stockant uniquement les 20 premiers octets de chaque hash
RootFS R/W
Avec iOS 11.3, Apple a implémenté une nouvelle mitigation dans la kext APFS d’iOS, qui empêche de remonter la racine (/
) en R/W. C’est un problème car cela empêche donc d’installer Cydia ou tout autre fichier système.
This is not a JB because we didn't remount the rootfs to R/W and install Cydia. The problem is Apple added a new mitigation on iOS 11.3 (or 11.2?) in APFS KEXT and we need to bypass it. https://t.co/hUVZKe3I6V
— Min(Spark) Zheng (@SparkZheng) 7 mai 2018
Min Spark et Xiaolong Bai ont publié un premier writeup sur comment contourner le principal problème, mais sans persistance. C’est à dire qu’il faut réinstaller tous les composants après un redémarrage.
Electra a ensuite été publié avec un bypass d’APFS fonctionnel et persistent considéré comme 0-day (bien que ça pas rapport avec la sécurité d’iOS). Ce jailbreak embarque deux fonctions permettant de remonter les fichiers systèmes en écriture :
- remountRootAsRW_old() : 11.2.x
- remountRootAsRW() : 11.3.x
Dans notre cas on va s’occuper de la partie 11.3.x.
D’après le pseudocode généré par IDA Pro, voici comment fonctionne cette fonction : L’outil effectue une vérification pour voir si le dossier /var/rootfsmnt
existe, puis le supprime si la condition est vraie. Il créé ensuite le répertoire qui est vérifié plus tôt. Et (si je comprends bien) défini les identifications du noyau temporairement pour monter les rootfs.
1
2
3
4
5
6
7
if ((unsigned int)file_exists("/var/rootfsmnt"))
rmdir("/var/rootfsmnt")
mkdir("/var/rootfsmnt", 0x1EDu);
chown("/var/rootfsmnt", 0, 0);
printf("Temporarily setting kern ucred\n");
wk64(v12 + (unsigned int)offsetof_p_ucred, v11);
Ensuite on la fonction tente de monter la partition /dev/disk0s1s1
dans /var/rootfsmnt
. Dès que celle-ci est montée l’app renomme “/var/rootfsmnt” en orig-fs
.
Le disque monté dans /var/rootmntfs
est démonté et on restaure les indentifications par défaut.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if ((unsigned int)mountDevAsRWAtPath("/dev/disk0s1s1", "/var/rootfsmnt") ){
printf("Error mounting root at %s\n", "/var/rootfsmnt");
}
else {
printf("Disabling the APFS snapshot mitigations\n");
snapshot = find_system_snapshot();
if (snapshot && !do_rename("/var/rootfsmnt", snapshot, "orig-fs")){
status = 0;
unmount("/var/rootfsmnt", 0);
rmdir("/var/rootfsmnt");
}
}
printf("Restoring our ucred\n");
wk64(v12 + (unsigned int)offsetof_p_ucred, v10);
vnode_put(disk0s1s1_vnode);
Si tout s’est bien passé, l’appareil va redémarrer.
Ensuite une vérification est effectuée pour savoir si les rootfs sont bien montées en R/W avec un simple test en créant un fichier à la racine.
Installation de Cydia
Depuis sa sortie, l’installation de Cydia n’a pas changée. Pour fonctionner Cydia a besoin de plusieurs composants comme APT ou dpkg. Pour cela, Saurik propose le gestionnaire de paquets et ses dépendances dans une archive nommée bootstrap.tar. Pour l’installer c’est tout simple, il suffit d’extraire les composants de l’archive dans /
.
Le code ci-dessus montre Electra qui configure le fichier /etc/hosts
et certains repos pour Cydia.
Ensuite le fichier .cydia_no_stash
est créé à la racine pour empêcher Cydia de déplacer les répertoires dans la partition système vers la partition mobile. Ce n’est plus utile depuis que les deux partitions partagent le même stockage.
jailbreakd
Electra étant un jailbreak KPPless, il n’est pas capable de patcher directement le kernel. C’est pour cela que jailbreakd
est utilisé. jailbreakd
est un démon qui d’après la doc et le code va appliquer les patches de setuid
pour les processus, les plateformes et le transfert de certaines fonctionnalités.
On peut interfacer avec ce démon à l’aide d’une bibliothèque dynamique libjailbreak.dylib
. Ce démon et la lib sont principalement utilisés par Cydia pour patcher setuid(0)
.
Detection de jailbreak
Electra est aussi muni d’un système de détéction de jailbreak tiers. En effet, les exploits étant open source, tout le monde peut developper des outils basé sur ces exploits. Pour empecher toute forme de disfonctionnement, Electra va tenter de supprimer les potentiels jailbreak qui modifient les fichiers systèmes. La fonction removePotentialJailbreak()
est tout simple : elle supprime des fichiers et repertoires créée par ces alternatives.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int removePotentialJailbreak()
{
rmdir("/jb");
rmdir("/Applications/Filza.app");
unlink("/etc/dropbear");
rmdir("/var/LIB");
rmdir("/var/ulb");
rmdir("/var/bin");
rmdir("/var/sbin");
unlink("/var/profile");
unlink("/var/motd");
unlink("/var/dropbear");
rmdir("/var/containers/Bundle/tweaksupport");
rmdir("/var/containers/Bundle/iosbinpack64");
rmdir("/var/containers/Bundle/dylibs");
unlink("/var/log/testbin.log");
unlink("/var/log/jailbreakd-stdout.log");
return unlink("/var/log/jailbreakd-stderr.log");
}
En plus de cette fonction, les devs ont implémenté deux autres fonctions pour détecter et supprimer LiberiOS, le jailbreak de Jonathan Levin. Non pas par concurence, mais plutôt pour éviter tout conflit lors de l’installation de certains fichiers comme les executables installé dans /usr/local/bin/
.
On a fait le tour du jailbreak Electra pour iOS 11.3.1. La team a décidé de ne pas publier les versions avec symboles histoire d’obfusquer un minimum leur code. Mais diaphora est là pour m’aider dans mon périple héhé.
EDIT : Seule la version VFS est deployée sans symboles de debug etc…
Bref j’ai surement fais des erreurs, et il me reste encore pleins de choses à comprendre, donc si vous avez des retours ou des questions à poser n’hésitez pas !
Si vous avez besoin d’infos contactez-moi sur twitter: @matteyeux
Github : https://github.com/matteyeux
Sources :
https://github.com/coolstar/electra
http://newosxbook.com/QiLin/qilin.pdf
https://github.com/Siguza/v0rtex
https://bugs.chromium.org/p/project-zero/issues/detail?id=1564
https://github.com/potmdehex/multipath_kfree
https://bugs.chromium.org/p/project-zero/issues/detail?id=1558