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
.
-
main
-
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. -
difficult_part
(1)Pour simplifier la compréhension, on retype la variable sur la stack par
char[16]
(carfgets
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
. -
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
. -
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
. -
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
. -
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}
.