Objectif
Exploiter la vulnérabilité de débordement de tampon et réaliser une exécution de code à distance (RCE).
Outils
- Jadx – https://github.com/skylot/jadx
- ADB – https://developer.android.com/tools/adb?hl=fr
- Ghidra – https://github.com/NationalSecurityAgency/ghidra
- Templates Frida – https://github.com/rsenet/Frida-Templates/
- DeployMyFridaEnv – https://github.com/rsenet/DeployMyFridaEnv
Installation
Pour commencer, nous allons installer l’application sur un périphérique virtuel (bien que cela fonctionne également avec les périphériques physiques), le tout grâce à ADB :
$ adb install com.mobilehackinglab.notekeeper.apk
Performing Streamed Install
Success
L’application est installée avec succès et se lance parfaitement :

Commençons l’analyse !
Code source de l’application
L’objectif de l’exercice ne laisse pas vraiment de place au doute ! Ainsi, au sein du fichier MainActivity, il est possible d’identifier plusieurs éléments particulièrement intéressants :
- Ligne 28 : méthode Java native appelée parse, qui prend une chaîne en entrée et renvoie une chaîne. Son code est implémenté en C/C++ via le JNI.

- Ligne 126 : chargement de la librairie native notekeeper.so afin de rendre ses fonctions accessibles
- Ligne 109 : Appel de la méthode native parse sur l’argument title_ (le titre de la note)

Le code Java/Kotlin s’exécutant dans la JVM/ART, l’exploitation d’un Buffer Overflow n’est pas directement possible. La seule alternative est d’avoir ce type de vulnérabilité au sein de la librairie native développée en C/C++.
Reverse de la librairie
Grâce à Ghidra, il est possible d’ouvrir la librairie notekeeper.so et y trouver la fonction JNI parse (ou Java_com_mobilehackinglab_notekeeper_MainActivity_parse de son nom complet) :

Les deux premiers paramètres de ce type de fonctions sont toujours les mêmes :
- param_1 correspond à un pointeur vers la structure JNI
- param_2 fait référence à l’objet Java (this)
Le troisième paramètre quant à lui correspond donc à l’argument title_ passé en argument de la fonction. Grâce à Ghidra, il est alors possible de modifier le nom des variables pour simplifier la compréhension du code :

En lignes 8 et 9, on retrouve les deux variables locale à la fonction parse() char local_2a4[100] et char acStack_240[500] :
- local_2a4 est un buffer de 100 octets destiné à recevoir le titre parsé
- acStack_240 est un buffer de 500 octets initialisé avec une commande système (ligne 22)
Par la suite, local_48 contient un pointeur vers les caractères du titre (ligne 20), et local_4c la longueur de la chaîne (ligne 21). En ligne 22, le buffer acStack_240 est rempli avec une commande système par défaut.
La copie du contenu de l’entrée (le titre) est copiée dans local_2a4 (lignes 27 à 29). En effet, cette ligne est problématique car aucune limite de taille n’est mise en place. Ainsi, si la longueur du titre est supérieure à 100 caractères, alors, la copie va déborder de local_2a4 et va écraser acStack_240.
Normalement, system() exécute « Log \ »Note added at $(date)\ » » (ligne 31).
Mais si local_2a4 a débordé, cette chaîne peut être corrompue et contenir du code arbitraire.
Exploitation
La vulnérabilité étant mise en avant, il ne reste plus qu’à sortir Frida pour faire déborder local_2a4 avec plus de 100 caractères et ainsi exécuter du code arbitraire grâce à la commande system() :

Ainsi, lors de l’appel à la fonction parse au sein de la classe MainActivity, Frida va remplacer automatiquement la chaîne de caractères envoyée à la fonction parse() par 100 caractères A suivi de la commande id > /data/data/com.mobilehackinglab.notekeeper/orhus.txt;. Cette dernière sera alors exécutée par la fonction system() :


