Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.
ndhquals2015:crackme_prime [2015/04/05 13:50] Spl3en |
ndhquals2015:crackme_prime [2017/04/09 15:33] (Version actuelle) |
||
---|---|---|---|
Ligne 16: | Ligne 16: | ||
- | L'exécution du programme donne : | + | C'est un ELF x86 32-bit, l'exécution du programme donne : |
spl3en@box:~$ ./crackme | spl3en@box:~$ ./crackme | ||
Ligne 24: | Ligne 24: | ||
- | On désassemble donc l'exécutable pour comprendre quel est le format attendu. \ | + | On va donc désassembler l'exécutable afin de comprendre quel est le format attendu. |
- | Globalement, voici le flot d'exécution du programme : | + | |
+ | Globalement, voici le flot d'exécution du ''main'' du programme : | ||
{{http://i.cubeupload.com/Gv0LvV.png}} | {{http://i.cubeupload.com/Gv0LvV.png}} | ||
Ligne 32: | Ligne 33: | ||
On remarque qu'il y a un block assez long qui mène au "good boy" du challenge, qui est gardé par 3 conditions : | On remarque qu'il y a un block assez long qui mène au "good boy" du challenge, qui est gardé par 3 conditions : | ||
- | - Dans la première condition, le programme vérifie si un argument a été donné au programme. \ | + | - Dans la première condition, le programme vérifie si un argument a été donné au programme en vérifiant ''argc'' |
- | - Dans la deuxième condition, il vérifie que la taille du premier argument fait 29 caractères. \ | + | |
- | - Dans la troisième condition, il vérifie que le premier argument ne contient pas le caractère '0'. \ | + | |
- | Dans le cas où un mauvais argument est rentré, rien est affiché. | + | - Dans la deuxième condition, il vérifie que la taille du premier argument fait 29 caractères en faisant un ''strlen (argv[1])'' |
+ | - Dans la troisième condition, il vérifie que le premier argument ne contient pas le caractère '0' en faisant un ''strchr (argv[1], '0')'' | ||
- | Le long block au milieu s'occupe de lire le premier argument, en le décomposant en 6 sous parties de 4 caractères, en sautant un caractères à chaque fois entre chaque. \ | + | Dans le cas où un mauvais argument est rentré, rien est affiché. |
- | Notre serial est donc de la forme : xxxx-xxxx-xxxx-xxxx-xxxx-xxxx | ||
- | |||
- | spl3en@box:~$ strlen xxxx-xxxx-xxxx-xxxx-xxxx-xxxx | ||
- | 29 | ||
+ | Sachant cela, on doit maintenant comprendre ce que fait le gros block du milieu. | ||
Voici une décompilation d'HexRays afin d'éviter la lecture de l'assembleur : | Voici une décompilation d'HexRays afin d'éviter la lecture de l'assembleur : | ||
Ligne 51: | Ligne 48: | ||
- | On remarque l'utilisation d'une fonction "c1" que l'on a pas encore analysé. | + | On en déduit que le long block au milieu s'occupe de lire le premier argument du programme, en le décomposant en 6 sous parties de 4 caractères, en sautant un caractères à chaque fois entre chaque. |
+ | |||
+ | Notre serial est donc de la forme : ''xxxx-xxxx-xxxx-xxxx-xxxx-xxxx'' | ||
+ | |||
+ | On regarde la taille de ce format pour vérifie que l'on ne s'est pas trompé : | ||
+ | |||
+ | spl3en@box:~$ strlen xxxx-xxxx-xxxx-xxxx-xxxx-xxxx | ||
+ | 29 | ||
+ | |||
+ | Chaque sous partie de 4 caractère passe dans ''strtol'' ayant pour argument 16, ce qui nous indique que ces sous parties doivent être de l'hexadécimal. | ||
+ | |||
+ | En fin de block, on remarque l'utilisation d'une fonction ''c1'' que l'on n'a pas encore analysé. | ||
- | Elle prend en entrée un mot de 16 bits récupéré depuis notre argument. | + | Elle prend en entrée un mot de 16 bits, récupéré depuis les sous parties de 4 caractères hexa. |
Un rapide aperçu du flow graph de cette fonction nous indique qu'elle est complexe : | Un rapide aperçu du flow graph de cette fonction nous indique qu'elle est complexe : | ||
{{http://puu.sh/h2ekZ/8c485f4fd4.png}} | {{http://puu.sh/h2ekZ/8c485f4fd4.png}} | ||
- | Elle appelle des fonctions telles que "aes_init", "aes_decrypt", "EVP_aes_256_cbc", laissant supposer que de l'AES-256 CBC est utilisé. | ||
Plutôt que d'analyser cette fonction, nous allons nous intéresser à sa sortie : 0, 1, ou -1 en cas d'erreur. | Plutôt que d'analyser cette fonction, nous allons nous intéresser à sa sortie : 0, 1, ou -1 en cas d'erreur. | ||
Ligne 70: | Ligne 77: | ||
Il ne nous reste plus qu'à trouver les valeurs du serial pour que ces conditions soit satisfaites. | Il ne nous reste plus qu'à trouver les valeurs du serial pour que ces conditions soit satisfaites. | ||
- | Sachant que c1 prend en entrée 16 bits, un bruteforce de 2^16 (65536) entrées est réalisable pour connaître l'ensemble des relations entrées <-> sorties de cette fonction, ce qui nous permettra d'éviter de l'analyser. | + | Sachant que ''c1'' prend en entrée 16 bits, un bruteforce de 2^16 (soit 65536) entrées est réalisable pour connaître l'ensemble des relations entrées <-> sorties de cette fonction. |
+ | De cette manière, nous allons éviter de l'analyser. | ||
Le script GDB suivant va nous permettre de récupérer les 65536 sorties de la fonction c1 : | Le script GDB suivant va nous permettre de récupérer les 65536 sorties de la fonction c1 : | ||
Ligne 104: | Ligne 112: | ||
[...] | [...] | ||
- | On récupére tout ça, et on le compacte dans un tableau C-style. | + | On récupére tout ça, et on le compacte dans un tableau de int C-style. |
- | On écrit un bout de code en C qui boucle à partir de la valeur 0x1111 (le caractère '0' est filtré) jusqu'à 0xFFFF, et qui teste si les deux équations renvoie 1. | + | On écrit un bout de code en C qui boucle à partir de la valeur 0x1111 (le caractère '0' étant filtré) jusqu'à 0xFFFF, et qui teste si les deux équations renvoie 1. |