UP | HOME

Personal Safe

crackmes.one (LubimPiskoty)


Le programme est écrit en ASM, on trouve le nom du fichier (safe.asm) lorsque l'on fait un strings dessus. Lorsque je l'ouvre avec Ghidra, cela explique pourquoi je me retrouve avec des fonctions syscall dans le code décompilé. Il est possible de déchiffrer un à un tous ces appels (un Wikibook dessus) mais Ghidra a un script pour ça (ResolveX86orX64LinuxSyscallsScript.java). Après son exécution, tous les appels systèmes sont correctement convertis dans le décompilateur.

En lisant rapidement le code décompilé, je comprends que le décompilateur ne va pas mettre utile ici. Il arrive à trouver des appels à OpenSSL alors qu'il n'en est rien. Je me concentre sur l'ASM. La seule partie à retenir du décompilateur est l'appel système à read. Le mot de passe fait 16 caractères et est stocké à l'adresse 0x00402038.

read(0,&password,0x10);

La fonction verify est ensuite appelée plusieurs fois dans l'ASM. Je vais voir ce qu'elle fait avant de me pencher sur les différentes opérations entre chaque appel.

          MOV        RAX,password
          CALL       _verify
          MOV        R12,R15
          MOV        RAX,DAT_00402040
          CALL       _verify
          CMP        R12,R15
          JNZ        _jmpwrong
          MOV        RAX,DAT_0040203c
          CALL       _verify
          MOV        R14,R15
          MOV        RAX,DAT_00402044
          CALL       _verify
          CMP        R14,R15
          JNZ        _jmpwrong
  ​

Elle est très courte. Je vous mets le code avec des commentaires qui explique ce qui se passe dedans en détail. Si vous avez la flemme de le lire : le registre EBX est utilisé comme un compteur (mis à 0 puis incrémenté et comparé en sorti de boucle). Il s'arrête à 4. Le byte se trouvant dans l'adresse mémoire de RAX va être ajouter dans un compteur à chaque tour de boucle. L'adresse de RAX va être ensuite incrémenté, pour se placer sur le byte suivant. Le compteur final est ensuite ajouté dans un compteur global.

        ;; sauvegarde de tbs
        PUSH       tbs
        ;; réinitialisation de R15, tbs et mise à zéro de EBX
        XOR        tbs,tbs
        XOR        R15,R15
        MOV        EBX,0x0

_verifyLoop
        ;; incrémentation de RBX, dont EBX est une partie.
        INC        RBX
        ;; récupération du byte se trouvant dans l'adresse mémoire de
        ;; RAX
        MOV        tbs,byte ptr [RAX]
        INC        RAX
        ;; on l'ajoute dans R15
        ADD        R15,tbs
        CMP        RBX,0x4
        JNZ        _verifyLoop
        ;; on ajoute notre total dans R13
        ADD        R13,R15
        POP        tbs
        RET

En se repenchant sur les appels de la fonction, l'adresse du mot de passe est mis dans le registre RAX, qui est ensuite utilisé dans verify pour pouvoir récupérer les bytes du mot de passe. Entre chaque appel, le registre change d'adresse. Il passe de l'index 0 de password à l'index 8 puis 4 et enfin 12.

Découpons les appels différents et ce qui se passe :

  1. les 4 premiers bytes de password sont additionés et le résultat est stocké dans R12
  2. les bytes 9 à 12 sont additionnés et le résultat est comparé avec la somme précédente
  3. idem que 1. avec les bytes 5 à 8 dont le résultat est stocké dans R14
  4. idem que 2. avec les bytes 13 à 16 et 3. à la place de 1.

À la fin, nous avons :

Il est donc possible de découper le mot de passe en 4 blocs de 4 bytes dont le premier et le troisième doivent être égaux ainsi que le deuxième et quatrième : XXXXYYYYXXXXYYYY.

Attaquons le dernier bloc de vérification.

            CMP        R13,0x42e
            JNZ        _jmpwrong
            ADD        R12,0xb
            CMP        R12,R15
            JNZ        _jmpwrong
            CALL       _correct
    ​

En reprenant la liste des registres, il faut avoir un mot de passe dont la somme vaut au final 0x42e et dont la somme des premiers bytes est légèrement inférieur à celle des derniers. Les opérations peuvent être résumées en une équation qui se résout rapidement.

\begin{equation} \begin{cases} 2x + 2y = \texttt{0x42e}\\ x + \texttt{0xb} = y \end{cases} \begin{cases} y = 273\\ x = 262 \end{cases} \end{equation}

En divisant par 4 \(x\) et \(y\), on peut retrouver des nombres à virgules "ronds" (0.25 et 0.5). Après quelques calculs, je trouve des nombres qui sont contenus dans la table ASCII. Je fini par écire un mdp qui ressemble à AAACDDDEAAACDDDE. Il est sûrement possible d'écire un autre mot de passe qui respecte les règles précédentes mais celui-là est le plus simple.

Author: rick

Email: rick@gnous.eu

Created: 2024-10-06 dim. 16:49

Validate