{WRITE-UP Reverse Java} CTF ESNA 2019

Android APK Reverse Engineering (Java)

Pour ce challenge vous aurez besoin de :
– un IDE Java (ici Netbeans avec JDK 11)
– un décompilateur/désassembleur de fichier APK (aapt, jadx)
– une connaissance basique du Java et des fichiers APK

Présentation :

Ce challenge était présent lors du CTF de l’ESNA en mars 2019.
Je n’ai pas la description du challenge ni la consigne, mais de toute manière très peu d’informations étaient données. La description devait être quelque chose du genre « cette application Android utilise un système de login futuriste ». Un fichier APK était mis à disposition.

Il faut tout d’abord savoir qu’un fichier APK est simplement une archive zip contenant l’ensemble du code compilé, les ressources et des métadonnées.

Il est donc possible d’ouvrir le fichier avec WinRaR ou 7-Zip.

Contenu de l’APK

Cependant il n’y a que très peu d’intérêt à ouvrir un fichier APK de la sorte, puisque les ressources ainsi que le code sont compilés respectivement dans les fichiers « resources.arsc » et « classes.dex ».

Il faut savoir que le langage Java n’est pas un langage natif, le code n’est donc pas transformé en assembleur comme ça serait le cas pour le C ou le C++. Le code source est tout d’abord transformé en Bytecode, une sorte de langage intermédiaire qui est lu et traduit par la machine virtuelle Java (JVM). C’est d’ailleurs ce qui permet au Java d’être multi-plateforme.
Ce bytecode peut donc être décompilé très facilement par n’importe qui (s’il ne bénéficie d’aucune protection), des outils en ligne ou disponibles en téléchargement existent pour le faire. Un bon programmeur pourrait reverse ce code par lui même, cependant il n’y a aucun intérêt à le faire puisque des outils le font très bien et très rapidement.

Une différence notable existe avec le code Java pour Android toutefois.

En effet, Android utilise une machine virtuelle différente pour interpréter le bytecode, son nom est Dalvik (cliquez pour en savoir plus).
Sa décompilation nécessite donc des outils spécialisés, et par chance, il en existe plusieurs, JADX étant le plus connu. Son avantage est qu’il peut aussi « décompiler » les ressources, car sous Android les ressources sont toujours contenues dans des dossiers prédéfinis selon une norme, et il en va de même pour les Strings (chaînes de caractère), qui sont dans des fichiers XML. Pas besoin d’expliquer en quoi c’est nécessaire je pense !

Une fois tout cela expliqué, je pense qu’il est temps d’entrer dans le vif du sujet !

Décompilation des sources et des ressources :

Si vous voulez le faire manuellement, vous pouvez vous rendre sur la page de JADX et à vous l’aventure.

Nous allons faire plus rapide ici, un site peut nous le faire très rapidement sans problème, il suffit d’uploader le fichier APK et après quelques secondes, le site nous propose de télécharger un fichier ZIP qui contient les sources et les ressources décompilées.

Rendez-vous sur ce site : http://www.javadecompilers.com/apk

Oubliez l’interface un peu… dégueu, vous serez bientôt ravi de ses fonctionnalités

Uploadez le fichier, puis cliquez sur « Upload and Decompile ».

Attendez 30s environ…
Fait !

Vous pouvez maintenant télécharger le résultat avec le bouton « Save ».

Le contenu du zip

Voilà qui est mieux, les dossiers « resources » et « sources » contiennent tout ce dont on a besoin pour réaliser ce challenge !

Recherche des strings dans les ressources :

Cela peut paraître assez trivial mais il s’agit pourtant d’une des premières choses à faire histoire de vérifier.
Dans notre cas il se pourrait que le flag s’y trouve, bien que cela soit assez improbable.

Les APK ont tous la même structure, ce qui veut dire que les strings d’une application seront toujours dans le même fichier au même endroit. Une aubaine.
Voici le fichier avec son chemin : /resources/res/values/strings.xml

Voici une partie du contenu :

Chaque string est identifiée par un nom, et contient une valeur (la chaine en question)

Personnellement je cherche toujours des mots comme « pass », « flag » et des choses du genre.
Le fichier étant assez court (49 lignes), on peut se permettre de tout regarder manuellement.

Tiens ! Que voit-on à la fin du fichier ?

Le flag ? Non.

Il aurait été trop simple que cela soit le flag, mais cependant il faut conserver cette information, elle nous servira…

Analyse du code source :

Le code se situe ici : /sources/areizen/easy/login
Sous Android, l’équivalent de la classe principale (main) est nommée : MainActivity.java

Le code la classe MainActivity

On remarque beaucoup de lignes qui font références à des bibliothèques Android.
On remarque en haut dans les champs globaux un tableau de type « byte » nommé hardcodedCipheredPassword.
Les données hexadécimale qu’il contient sont le mot de passe mais comme sont nom l’indique, il est chiffré.
On voit une fonction très intéressante vers la fin, il s’agit très certainement de la fonction de chiffrement/déchiffrement du mot de passe.

En regardant un peu son fonctionnement, on voit que chaque lettre est XORée avec le modulo de la longueur de chaque lettre du mot de passe.

Reste à déterminer quels sont les deux paramètres, mais je vous le donne en mille : que pourrait nécessiter une fonction de chiffrement/déchiffrement ?

Le texte chiffré ou à chiffrer et la clé de chiffrement.

Et rappelez-vous, on a un mot de passe chiffré et une clé qu’on a trouvé dans les ressources…
Ca s’annonce intéressant, et facile en plus.

Plus qu’à ouvrir un IDE Java pour utiliser cette fonction.

Reverse engineering :

J’utilise Netbeans, mais vous pouvez utiliser n’importe quel IDE. Ou bien même juste en ligne de commande en tapant « javac » si vous êtes de la génération digitale (troll).

Tout d’abord créer un projet, ou un fichier .java (selon vos IDE) qui sera une Main Class, peu importe le nom. Voici la mienne :

J’ai innové oui

Maintenant récupérez la ligne « public static byte[] hardcodedCipheredPassword = new byte[]{(byte) 32……..« , c’est le mot de passe chiffré.
Et récupérez la fonction « ultraFuturisticCipher(byte[],
byte[])
« .

Insérez les dans la classe que vous venez de créer :

Si vous connaissez Java, vous savez où les choses vont

Ensuite ajoutez un champ en dessous du mot de passe, qui contiendra la clé récupérée dans les ressources :

Changez la signature de la fonction ultraFuturisticCipher en ajoutant le mot clé « static ». Ceci permet de l’appeler depuis la fonction main qui est statique.

Maintenant il suffit d’ajouter deux lignes dans la fonction main.

La première appelle la fonction de déchiffrement avec comme paramètres le flag chiffré et la clé (string en byte), et la valeur du résultat est stockée dans une string.
La seconde affiche tout simple le flag.

Voici le code :

Plus qu’à lancer le code et la magie opère !

Enfin ce flag !

Flag : ESNA{futurIsNow}

Merci d’avoir suivi !

Vertebro