Writeup
Last updated
Last updated
Le Challenge est le suivant : Réussir à avoir l'intégralité des personnages sans payer et sans se connecter. L'idée est la suivant un check est fait pour savoir si on est connecté et si on a le droit à un accès premium.
Pour ce faire j'ai connecté premièrement l'iphone à mon pc et lancer iproxy pour faire du ssh over usb :
J'ai ensuite utilisé l'outils : frida-ios-dump ( Frida-ios-dump )
Durant cette phase on va essayer de trouver le maximum d'information qui nous permettrai d'identifier les fonctions responsable du check du login et du check du status premium.
Pour ce faire on peut utiliser plusieurs outils :
DĂ©sassembleur : Cutter / Ghidra
Frida : Frida & Frida-Trace
Objection
GrapeFruit
La première chose que j'ai fait fut de lancé l'analyse du binaire et d'essayer de tracer des méthodes qui pourrait porter le nom login avec frida-trace :
On remarque le nom d'une classe qui est assez explicite : CHCurrentUser. L'hypothèse est la suivante, cette classe serait à l'origine des contrôles de connexion et autre pour l'utilisateur courant.
On peut faire deux choses Ă ce stade :
Aller sur Cutter/Ghidra/GrapeFruit pour identifier cette classe et les méthodes associées
Tracer Ă l'aide de Frida-Trace cette classe et les appels potentiel qui peuvent ĂŞtre fait
En cherchant la classe sur Cutter, on identifie une multitude de méthode assez parlante (le binaire n'a pas été stripped) et on retrouve la méthode loginToken qu'on avez tracé :
On peut faire la mĂŞme chose avec GrapeFruit :
On peut tracer avec frida-trace la classe CHCurrentUser pour voir quelle méthode sont appelé lorsqu'on veut selectionner un personnage ou lorsque les personnages sont affiché ou bien lorsqu'on est à l'acceuil du jeu
Il manque certaines méthode car les appels sont trop long, voici un autre screen quand on navigue sur l'application :
On comprend que la fonction isLoggedOut doit sans doute être responsable d'identifier si l'utilisateur est identifié ou non, tout comme GuestorIsLoggedOut etc.
La méthode isBasic et premiumStatus sont intrigante à première vu elle servirait sans doute à savoir si le compte de l'utilisateur à la version premium ou non de l'application.
Donc notre travail désormais est de connaitre le fonctionnement de ces méthodes à l'aide de leurs noms, de reverse engeenering et de leurs valeurs de retour.
Pour ce faire on va utiliser frida,cutter et objection.
A l'aide de objection on va obtenir les valeur de retour de ces fonctions et essayer de déduire le comportement via cette valeur.
On obtient ceci lorsqu'on clique sur Jouer avec l'ordinateur et sur Plus :
C'est assez parlant les méthodes ont l'air de renvoyé un booléan on va donc confirmé cela avec frida :
On remarque que la méthode isLoggedOut et isBasic renvoie bien un boolean et premiumStatus renvoie un pointeur vers donc un objet mais quoi comme objet on ne sait pas pour le moment.
Pour le savoir, il suffirait de créer un compte et d'inspecter quelle type d'objet s'y trouve comme ceci :
On a créé un compte pour voir vers quoi pointe le pointeur et surtout on peut monitorer les valeurs renvoyé par isLoggedOut et isBasic (sans supprise isLoggedOut renvoie 0 et isBasic 1 puisqu'on a pas de compte premium).
ObjC.chooseSync nous permet de récupérer l'instance de la classe CHCurrentUser et les $ivars nous permettent de récupérer ces attributs. Les $ivars sont aussi disponible à la consultation sur GrapeFruit.
On remarque un attribut du nom de premiumStatus c'est intéressant.
On peut aussi utilisé la méthode premiumStatus pour récupérer le pointeur, (on aurait pu le voir aussi avec objection)
DĂ©sormais il suffit d'inspecter cette adresse :
On a donc un objets ayant les attributs features,premiumStatusType et isa.
Désormais qu'on sait que isLoggedOut renvoie 0 quand on est connecté et renvoie 1 quand on est deconnecté il suffit à l'aide d'objection ou frida de changer sa valeur de retour avec de fake login :
Grâce à cela il nous est desormais possible de choisir les personnages sans avoir la pop-up qui nous demande de créer un compte.
A noté que sa crash quand on revient en arrière donc à REPARER;
On a vu précedent l'appel à des méthode isBasic et premiumStatus qui sont pour moi responsable du statut de notre compte.
Ainsi allons sur cutter explorer ces deux méthodes :
On comprend grâce à ce code que isBasic renvoie bien un boolean. Ensuite il fait appelle à la méthode premiumStatus et stocke sa valeur de retour dans ivar4. Souvenez vous que premiumStatus renvoie un pointeur donc le if veut dit si le pointeur n'est pas null alors stocke dans ivar5 la valeur renvoyer par la méthode premiumStatusType et retourne true si ivar5 vaut 0 ce qui signifie indique que le compte est bien un compte basic sinon c'est un compte premium.
Mais cette méthode nous est inconnu, essayons de trouver la classe qui lui est associé :
La classe qui est assoicié à cette méthode est la classe CHPremiumStatus. Allons voir les ivars de cette classe pour en apprendre plus :
Ainsi la méthode premiumStatusType permet de renvoyer la valeur premiumStatusType et cette valeur contrôle la valeur renvoyé par isBasic. Ainsi l'idée serai de mettre 1 à _premiumStatusType et voir comment réagi l'application.
Il faudrai aussi tracé l'application pour voir à quelle moment cette méthode est appelé et celle-ci est appelé avant qu'on clique sur le bouton:
TOUT CECI EST REALISE AVEC UN COMPTE POUR QUE L APPLICATION NE CRASH PAS MAIS UNE AUTRE SOLUTION ARRIVERA PROCHAINEMENT.DE PLUS LORSQU ON A PAS DE COMPTE L OBJET CHPREMIUMSTATUS N EXISTE PAS DONC IL FAUT LE CREER ET L AJOUTER A NOTRE INSTANCE CHCURRENTUSER ET A SON ATTRIBUT PREMIUMSTATUS :
Ainsi on remarque qu'avant même de cliquer sur le bouton jouer avec l'ordinateur la méthode est appelé ce qui est logique on verifie d'abord si le jour à un compte premium si oui alors j'affiche une vu avec tout les personnage sinon j'affiche une vue avec des personnages limité.
Donc on va modifier la valeur de premiumstatustype Ă 1 pour dire Ă l'application que notre compte est premium.
Ainsi premiumStatus n'est plus a 0 mais Ă 1 et on obtient tous les personnages.
On a :
Solution pour login + premium :
On backtrace isGuestLoggedOut, on voit que la fonction est appelé dans d'autre fonction dont processbot on voit sur cutter , il y a un cbz on se hook
on regarde elle appelle isGuestOrLogged