OwnAppV2, Proxying and Reversing a Flutter apk | BreizhCTF WU

Table des matières :


Flutter detection

Lorsque l’on décompile l'apk à l’aide de jadx, on voit clairement dans le MANIFEST.MF la présence de libflutter.so, indiquant que l’application utilise flutter.

alt text
Lorsque l’on run l'apk, seul un bouton cliquable est présent.
alt text

reFlutter

reFlutter permet de patcher l’app et d’utiliser une version patchée de la lib Flutter. L’objectif est de pouvoir intercepter le traffique HTTP/HTTPS en utilisant par exemple Burp Suite.

L'IP correspond à votre IP locale.

alt text Il faut par la suite ajouter aux Proxy Settings le port correspondant (8083) et cocher Support invisible proxying.

alt text

Une fois l'apk patchée, il ne manque plus qu’à la signer, pour cela il est possible d’utiliser apk-uber

alt text

Il ne manque plus qu’à installer à nouveau l’app patchée. Lorsque l’on relance l’application et que l’on clique à nouveau, rien ne se passe..
alt text

Penchons nous côté reverse.

B(l)utter

Blutter (https://github.com/worawit/blutter) est un outil permettant de directement reverse le libapp.so (arm64) de l’apk. Ce fichier contient les libs natives compilées, ainsi que le code Flutter et les dependencies nécessaire à l’execution de l’app. Le fichier libapp.so est récupérable après décompilation par le biais de jadx (jadx OwnAppV2.apk -d $PWD/jadx), il se situe dans /resources/lib/.

alt text

On retouve dans /asm/own_app_version_2/ le main.dart décompilé. En cherchant un peu, on tombe rapidement sur un endpoint explicite.
alt text

Si la valeur 42 (0x2A) est atteinte, alors le code charge l’URL https://ownappv2.ctf.bzh/score_from_user_app_android?score=42 dans un registre, la convertit en un objet Uri avec Uri.parse, puis envoie une requête GET à l’URL en utilisant http.get

Puisqu’on à la flemme de cliquer 42 fois, on utilise pyautogui..

import pyautogui
import time

delay = 5

print(f"[+] Place your mouse. Waiting {delay} secondes")

time.sleep(delay)

x, y = pyautogui.position()

number_of_clicks = 42

for _ in range(number_of_clicks):
    pyautogui.click(x, y)

print("Done")

alt text

On récupère directement dans burp la requête, il ne manque plus qu’à ajouter le score (+1 pour battre Kaluche ahah)

alt text

Solve without emulation

Il était aussi possible de récupérer le flag sans même lancer l'apk. Dans le main.dart récupéré après avoir reverse le libapp.so, on avait directement l’endpoint en clair.
alt text

La réponse indique que les webbers ne sont pas trusted.. Il suffisait simplement de modifier le User-Agent par celui d’une application Dart, avec la bonne version. User-Agent: Dart/2.18 (dart:io)

Bonus point: zygisk-reflutter

J’ai trouvé ce module vraiment sympa pour Magisk, permettant d’implémenter reFlutter sur un android physique root. (https://github.com/yohanes/zygisk-reflutter)

alt text alt text

Remerciements

Merci aux organisateurs, ainsi qu’à @Worty pour les challs android ⋆。゚☁︎ 。⋆

Special thanks a notre team ▯▯▯ aka 🪬​🪬​🪬(sympa les char non-ascii ahah)