Solution de erdnaxe pour Guessy

intro reverse linux x86/x64

11 novembre 2023

Analyse préliminaire

On commence par analyser le type du fichier :

$ file guessy
guessy: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped

guessy est donc un fichier exécutable ELF x86-64 non strippé. Il contient donc des symboles de débogage tel que le nom des fonctions.

On tente d’afficher les chaînes de caractères du binaire :

$ strings guessy
/lib64/ld-linux-x86-64.so.2
libc.so.6
[...]
Can you guess the LAST character of the flag ?
Really ? I ask you for a single character and you give me this ?
Oh no, you were so close !
[...] Rien d'intéressant

Le flag ne semble pas être en clair dans le programme, mais il semble y avoir des interactions. On lance le programme et on tente notre chance :

$ ./guessy
Give me the flag:
FCSC{
Ok so I see we have an understanding. Let's begin the difficult part now.
Now you can try to guess the next eight characters of the flag.
aaaaaaaa
Wrong guess.

Bon, il est temps de passer à de l’analyse statique.

Analyse statique

On analyse le binaire dans Ghidra et on essaie de comprendre le flot d’exécution à partir de la fonction main.

  1. main

  2. validate

    if ((((*param_1 == 'F') && (param_1[1] == 'C')) && (param_1[2] == 'S')) && (param_1[3] == 'C')) {
    if (param_1[4] == '{') {
    

    La solution est donc FCSC{, on pouvait le deviner avec le format du flag.

  3. difficult_part (1)

    Pour simplifier la compréhension, on retype la variable sur la stack par char[16] (car fgets de longueur 16).

    if (((((local_18[0] == 'e') && (local_18[1] == '7')) && (local_18[2] == '5')) &&
        ((local_18[3] == '5' && (local_18[4] == '2')))) &&
       ((local_18[5] == 'c' && ((local_18[6] == 'f' && (local_18[7] == '6')))))) {
    

    La solution est donc e7552cf6.

  4. difficult_part (2)

    if ((((((int)local_18[0] & 0x7fffffffU) == L'4') &&
         (((int)local_18[1] & 0x7fffffffU) == L'c')) &&
        (((int)local_18[2] & 0x7fffffffU) == L'e')) &&
       (((((int)local_18[3] & 0x7fffffffU) == L'2' && (((int)local_18[4] & 0x7fffffffU) == L'e')
         ) && ((((int)local_18[5] & 0x7fffffffU) == L'5' &&
               ((((int)local_18[6] & 0x7fffffffU) == L'a' &&
                (((int)local_18[7] & 0x7fffffffU) == L'd')))))))) {
    

    La solution est donc 4ce2e5ad.

  5. difficult_part (3)

    if ((((((int)local_18[0] & 0x1fffffffU) == L'0') &&
         (((int)local_18[1] & 0x1fffffffU) == L'b')) &&
        (((int)local_18[2] & 0x1fffffffU) == L'b')) &&
       (((((int)local_18[3] & 0x1fffffffU) == L'0' &&
         (((int)local_18[4] & 0x1fffffffU) == L'9')) &&
        ((((int)local_18[5] & 0x1fffffffU) == L'5' &&
         ((((int)local_18[6] & 0x1fffffffU) == L'4' &&
          (((int)local_18[7] & 0x1fffffffU) == L'f')))))))) {
    

    La solution est donc 0bb0954f.

  6. difficult_part (4)

    if ((((((byte)(local_28[0] ^ local_18[0]) == 1) &&
          ((byte)(local_28[1] ^ local_18[1]) == 'T')) &&
         ((byte)(local_28[2] ^ local_18[2]) == 'U')) &&
        (((byte)(local_28[3] ^ local_18[3]) == 'Q' &&
         ((byte)(local_28[4] ^ local_18[4]) == 9)))) &&
       (((byte)(local_28[5] ^ local_18[5]) == 7 &&
        (((byte)(local_28[6] ^ local_18[6]) == 'W' && (local_18[7] == local_28[7])))))) {
    

    C’est un XOR avec les 8 caractères précédents.

    >>> a = b"\x01TUQ\x09\x07W\x00"; b = b"0bb0954f"
    >>> bytes([aa^bb for aa, bb in zip(a, b)])
    b'167a02cf'
    

    La solution est donc 167a02cf.

  7. most_difficult_part, troll trivial : }.

On relance le programme :

$ ./guessy
Give me the flag:
FCSC{
Ok so I see we have an understanding. Let's begin the difficult part now.
Now you can try to guess the next eight characters of the flag.
e7552cf6
Well done, you can try to guess the next eight characters but it won't be so easy.
4ce2e5ad
I see you've got some skills in reversing, but can you guess the next eight ?
0bb0954f
I must say that I'm impressed but it's not over. Will you be able to guess the next eight characters ?
167a02cf
Alright, now let's go to the most difficult part of this challenge.
Can you guess the LAST character of the flag ?
}
Congratulations, you've guessed the flag !

Le flag est donc FCSC{e7552cf64ce2e5ad0bb0954f167a02cf}.