
Exploit LPE (preuve de concept) pour la faille use-after-free du Binder Android qui utilise le spraying iovec et l'écrasement d'addr_limit afin d'obtenir une lecture/écriture arbitraire du noyau.
Un exploit Proof-of-Concept / Élévation de privilèges locale (LPE) réécrit ciblant CVE-2019-2215, une vulnérabilité Use-After-Free dans le pilote Android Binder.
J'ai fourni la compilation du noyau vulnérable dans le dossier vulnerable_kernel_builds. Créez un émulateur Android 10 AOSP et exécutez le noyau avec celui-ci.
emulator -show-kernel -no-window -no-snapshot -wipe-data -avd research -kernel bzImage
Pour les détails techniques du bug, vous pouvez en apprendre davantage sur cet incroyable blog : https://projectzero.google/2019/11/bad-binder-android-in-wild-exploit.html
La structure task_struct possède un membre important addr_limit de type mm_segment_t. addr_limit stocke l'adresse la plus haute valide de l'espace utilisateur. addr_limit fait partie de struct thread_info ou de struct thread_struct selon l'architecture cible. Comme nous avons affaire à un système x86_64 bits, addr_limit est défini dans struct thread_struct.

Si nous pouvons écraser ce addr_limit avec 0xFFFFFFFFFFFFFFFF, nous serons en mesure de lire et d'écrire dans n'importe quelle partie de la mémoire de l'espace noyau. Pour une meilleure compatibilité de l'exploit sur x86_64 et arm64, il est préférable de définir addr_limit à 0xFFFFFFFFFFFFFFFE.
struct iovec est utilisée pour Vectored I/O, également connu sous le nom de Scatter/Gather I/O. L'un des principaux problèmes de struct iovec est qu'elles ont une durée de vie courte. Elles sont allouées par les appels système lorsqu'ils travaillent avec les tampons et immédiatement libérées lorsqu'ils retournent en mode utilisateur.
Nous voulons que la structure iovec reste dans le noyau lorsque nous déclenchons l'opération unlink et écrasons le pointeur iov_base avec l'adresse de binder_thread->wait.head pour obtenir une lecture et une écriture ciblées. Une solution consiste à utiliser des appels système comme readv, writev sur un descripteur de fichier pipe car ils peuvent se bloquer si le pipe est plein ou vide. pipe est un canal de données unidirectionnel qui peut être utilisé pour la communication interprocessus. La fonctionnalité de blocage de pipe nous donne une fenêtre de temps significative pour corrompre la structure iovec dans l'espace noyau.
De la même manière, nous pouvons utiliser l'appel système recvmsg pour bloquer en passant MSG_WAITALL comme paramètre de drapeau.
Comme la taille de la structure binder_thread est de 408 octets, elle finira dans le cache kmalloc-512.

nous aurons besoin d'empiler 25 iovec structures pour réallouer le bloc mémoire pendant. 408 / 16 = 25.5

Comme nous pouvons le voir sur l'image ci-dessus, iovecStack[10].iov_len et iovecStack[11].iov_base seront écrasés.

Nous voudrions donc traiter iovecStack[10], bloquer l'appel système writev, puis déclencher l'opération unlink. Cela garantira que lorsque iovecStack[11].iov_base sera écrasé, nous reprendrons l'appel système writev. Enfin, exfiltrer le contenu du bloc binder_thread vers l'espace utilisateur et lire le pointeur task_struct depuis celui-ci.

Pour obtenir une write ciblée, nous allons utiliser l'appel système recvmsg pour bloquer en passant MSG_WAITALL comme paramètre de drapeau. L'appel système recvmsg peut se bloquer tout comme l'appel système writev.

Comme la taille de mm_segment_t est de 0x8 octets, nous voudrons l'écraser avec 0xFFFFFFFFFFFFFFFE car c'est l'adresse la plus haute valide de l'espace noyau et elle ne fera pas planter le processus si un défaut de page se produit sur un système arm64.

