Rusty_rev
Flag: HACKDAY{D0N7_637_rU57Y}
Challenge
Solution
Ce challenge est un premier programme, appelé loader, qui charge en mémoire un second puis l'exécute.
Reverse du loader
Découvrir l'existence du loader
Ici, j'utilise Ghidra pour décompiler le binaire. Dans la partie SymbolTree, on voit que la fonction memfd_create est utilisée. Un petit tour sur la doc pour voir qu'elle permet de créer un fichier anonyme en mémoire et retourne son file descriptor.

Si l'on regarde où est utilisée cette fonction, on découvre un appel à la fonction write est fait juste après sur le fichier créé :

Extraction du vrai programme
On va lancer le binaire avec GDB et break à ce niveau-là pour voir ce qui est écrit :
Une fois arrivé au breakpoint, on voit que ce que l'on écrit dans le fichier anonyme commence par ELF. C'est là que l'on comprend que c'est un loader qui charge notre vrai programme en mémoire. On va donc dump celui-ci, pour ça on utilise l'adresse affichée dans le paramètre buf et la taille affichée dans nbytes.
La commande dump utilise une adresse de début et une adresse de fin, il faut donc additionner la taille à l'adresse de début pour obtenir l'adresse de fin, 0x7ffff7c71010 + 0x5f9c0 = 0x7ffff7cd09d0 :

Reverse du vrai programme
Trouver la fonction principale
Maintenant, je passe sur IDA pour le binaire qu'on vient d'extraire. La décompilation est plus explicite et visuelle pour le WriteUp.
En lançant le programme, on nous affiche la phrase Please input your password. On commence donc par chercher la string password pour savoir où nous sommes dans le programme.

En regardant les refs à celle-ci, on arrive sur la fonction sub_8434 :

Comprendre la fonction
En regardant un peu à quoi correspondent les refs utilisées dans la fonction, on peut voir qu'en fonction de v55, on place GG WP, Wrong password, Reboot computer ou l'ASCII Art AmongUs en mémoire.
Il y a également une ligne intéressante : on compare v79 à 23 et ensuite, on fait tout un tas de comparaisons avant d'écrire GG WP.

Aparté concernant v55, il s'agit du résultat d'une fonction aléatoire, on peut le voir facilement :

Pour attendre le if qui nous intéresse (celui en vert dans le screen précédent) il ne faut pas que v55 soit égal à 1, 2, 3, ou 4. Donc, à chaque fois qu'on analysera dynamiquement le programme, on va le forcer à 5.
Rebase du program
Toujours concernant l'analyse dynamique, on va devoir rebase notre IDA pour avoir les mêmes adresses que sur notre GDB. Pour ça, on commence par utiliser vmmap dans gdb et on regarde le start du programme : 0x00555555554000.

Maintenant, on va setup ça dans IDA : Edit > Segments > Rebase program...

Parfait, on a les mêmes adresses dans les deux, ce sera plus simple à suivre. Notre fonction sub_8434 est maintenant à l'adresse sub_55555555C434.
Analyse dynamique
Comme j'ai dit tout à l'heure, on va fixe notre variable v55. Pour ça, on va break juste après l'appel à la fonction rand et changer la valeur. Le résultat est dans EAX, donc on change EAX (logique).

Taille du mot de passe
Maintenant, on va break sur la comparaison avec 23, c'est à l'adresse 0x55555555CE1F

En lançant le programme avec le mot de passe "test", on voit que c'est rsp+40 qui est comparé à 23 (0x17). Et la valeur dans rsp+40 est 4 :
On en déduit facilement que c'est la longueur de notre mot de passe, il faut donc qu'il fasse 23 caractères.
Comparaison du mot de passe
En continuant sur la comparaison qui nous intéresse, on voit que :
rbxest comparé avec0x5555555A1050(valeur :A5E7F33F8DE1F5)rbx+7est comparé avec0x5555555A1040(valeur :0FCA1B12650FE105220FCF1436E83C3A)

De plus, avec notre mot de passe test, RBX contient une valeur sur 4 octets. Avec d'autres mots de passe et en breakant au même endroit, on se rend compte que RBX contient notre mot de passe "chiffré".
Le chiffrement est fait caractère par caractère et la position du caractère compte également. On peut le voir assez facilement en faisant différentes tentatives :
Conclusion : il faut que le résultat de notre mot de passe chiffré donne les deux valeurs qu'on a vues juste avant, c'est-à-dire : A5E7F33F8DE1F50FCA1B12650FE105220FCF1436E83C3A.
Automatisation
Ce qu'on va faire, c'est tester tous les caractères à toutes les positions possibles et récupérer leur valeur chiffrée. On pourra ainsi comparer avec le résultat attendu pour voir les correspondances. En python, ça donne ce script :
On a un script Python GDB qui permet de lancer le programme et récupérer le résultat du chiffrement dans output.txt, maintenant il nous faut un script pour tester tous les caractères :
Le résultat :
Mis à jour
Ce contenu vous a-t-il été utile ?