GCC CTF - GCC Chat 1&2

On nous fournit un fichier : GCChat.apk

Analyse de l’apk

En le lançant dans visual studio/adb, on se retrouve sur un login form :

"Main"

Au début je ne connaissais pas hermes. (Bytecode JS pour des apps React) Je savais que l’apk utilisais une form react pour son pannel, mais je ne trouvais ni comment était stockés les logins ou la logique de l’app. J’ai fini par regarder quelque lib native mais rien de concluant.

En regardant le MainApplication, je décide de regarder ce que Hermes sur internet et bingo , du bytecode JS à reverse et qui fournit la logique de l’app React, c’est pile ce qu’il me manquait.

Le bytecode JS est stocké dans assets/index.android.bundle. On va utiliser le tool https://github.com/P1sec/hermes-dec

hbc-disassembler index.android.bundle hcdec/out.asm.patched

Je n’utilise pas le decompiler car le pseudo-code généré est trop confus, et je trouve la sortie déssasembleur beacoup plus propre que cette sortie la.

Je fais un CTRL-F pour chercher Wrong username or password, alert qui est crée lors d’un mauvais login.
Je finis par tomber sur cette fonction :

hbc-disassembler index.android.bundle hcdec/out.asm.patched

"Main"

On récupère donc l’username : GCC_Staff ainsi qu’un hash qui semble être un sha256 du mot de passe : 011efea0cac926232add733f86e3d9f6ab53c237be9e40f9e6a2e255ba33abc3
On remarque un peu plus haut des fonctions tel que “convert_sha” donc on se doute du format du mot de passe stocké ici.

Heuresement ce SHA256 est très facile à reverse, à cause de la faible du mot de passe initial :

"Main"

On a donc le mot de passe : P4ssw0rd

On peut se connecter sur le login form, et on obtient le 1er flag GCC CHAT 1/2:

"Main"

On notera l’immense flemme de remettre l’apk du chall 1, le button d’envoie de donnée est desactivé.

chall 2

L’apk est le même, mais il faut envoyer le message “Gimme that flag you silly boy!” dans le chat.
Il est actuellement impossible de l’envoyer, le bouton étant desactivé…

Pour cela, même principe que tout a l’heure, on va chercher le bytecode hermes du bouton et tenter de le patcher…

Note : Il n’y a pas de checksum sur le bytecode, il faut patcher l’header JS si on augmente la taille du bytecode mais si on remplace les octets par des instructions de taille simillaire, l’app marchera niquel.

Note 2 : Il existe le tool hbctool pour désassembler du bytecoder Hermes, le patcher et le recompiler. Malheuresement le tool ne supporte pas Hermes 96. Cependant l’autheur avait un fork de ce projet afin de supporter Hermes 96.

Analyse de la fonction Button

Dans la fonction GCC_Chat() on a à la fin :

==> 000001ab: <GetById>: <Reg8: 6, Reg8: 5, UInt8: 13, string_id: 4086>  # String: 'Button' (Identifier)
==> 000001b1: <NewObject>: <Reg8: 5>
==> 000001b3: <TryGetById>: <Reg8: 11, Reg8: 8, UInt8: 14, string_id: 4029>  # String: 'enabled' (Identifier)
==> 000001b9: <LoadConstString>: <Reg8: 10, string_id: 2104>  # String: 'Not enabled !' (String)
==> 000001bd: <JmpFalse>: <Addr8: 7, Reg8: 11>  # Address: 000001c4
==> 000001c0: <LoadConstString>: <Reg8: 10, string_id: 446>  # String: 'Send' (String)
==> 000001c4: <PutNewOwnById>: <Reg8: 5, Reg8: 10, string_id: 5315>  # String: 'title' (Identifier)
==> 000001c9: <CreateClosure>: <Reg8: 9, Reg8: 9, function_id: 6439>  # Function: [#6439 onSendMessage of 169 bytes]: 1 params @ offset 0x0011bc33
==> 000001ce: <PutNewOwnByIdShort>: <Reg8: 5, Reg8: 9, string_id: 203>  # String: 'onPress' (Identifier)
==> 000001d2: <TryGetById>: <Reg8: 8, Reg8: 8, UInt8: 14, string_id: 4029>  # String: 'enabled' (Identifier)
==> 000001d8: <Not>: <Reg8: 8, Reg8: 8>
==> 000001db: <PutNewOwnByIdShort>: <Reg8: 5, Reg8: 8, string_id: 133>  # String: 'disabled' (Identifier)
==> 000001df: <Call3>: <Reg8: 5, Reg8: 7, Reg8: 3, Reg8: 6, Reg8: 5>
==> 000001e5: <PutOwnByIndex>: <Reg8: 4, Reg8: 5, UInt8: 2>
==> 000001e9: <PutNewOwnByIdShort>: <Reg8: 0, Reg8: 4, string_id: 104>  # String: 'children' (Identifier)
==> 000001ed: <Call3>: <Reg8: 0, Reg8: 2, Reg8: 3, Reg8: 1, Reg8: 0>
==> 000001f3: <Ret>: <Reg8: 0>

Il est tentant de remplacer le string ‘Not Enabled’ par ‘Enabled’ mais malheuresement cela ne marchera pas (Génance/20 lors de la réalisation) Cependant on remarque Ligne 0x1d2 que le bouton est activé par défaut (avec la propriété enabled mais que la ligne suivant inverse la propriété, puis save l’état du bouton.)

==> 000001d2: <TryGetById>: <Reg8: 8, Reg8: 8, UInt8: 14, string_id: 4029>  # String: 'enabled' (Identifier)
==> 000001d8: <Not>: <Reg8: 8, Reg8: 8>
==> 000001db: <PutNewOwnByIdShort>: <Reg8: 5, Reg8: 8, string_id: 133>  # String: 'disabled' (Identifier)

On a donc r8 = 1 (activated) puis not r8 => r8=0 puis finalement button.activated = r8
On veut donc enlever ce Not r8 par quelque chose d’autre.

J’ai tenté de remplacer le registre r8 par r0 mais l’application crash.
Comme je n’ai pas envie de patcher le header JS pour augmenter/baisser la taille, je vais chercher une instruction sur 3bytes.

Ainsi on remaruqe dans la fonction GCC_CHAT l’instruction :
==> 00000010: <LoadParam>: <Reg8: 0, UInt8: 1>

C’est parfait c’est sur 3octets ! On ouvre le index.android.bundle dans ghex, on jump sur addr(GCC_CHAT) + insn Load => 0x0011b9d8 + 0x10 => on copie les 3bytes
puis on jump sur addr(GCC_CHAT) + insn NOT => 0x0011b9d8 + 0x1D8 => on remplace les 3octets par les octets du load…

On sauvegarde le index.android.bundle et ensuite on rebuild l’apk. On peux utiliser apktool pour ça, très efficace. Je conseille l’extension APKLAB sur visual studio qui permet d’inclure jadx, la recompilation, l’ouverture auto des apk, la signature, …

Une fois l’apk reconstruit avec APKtool (utiliser appt2), on peut l’installer

>adb install -r c:\Users\titip\Desktop\ctf\gcctf\gccchat2\test\GCC_Chat\dist\GCC_Chat\dist\GCC_Chat.apk

Ce qui donne :

"Main"

et donc au final, envoyant Gimme that flag you silly boy!, on obtient le 2ème flag :

"Main"

Chall très sympathique, c’était également fesable avec frida surement, mais j’ai préféré patcher le bytecode pour aller plus rapidement.