Rapport de reverse engineering sur la série de crackmes Crack My Owl.
Résumé des résultats
| Cible | Mot de passe |
|---|---|
./00 | mistigri |
./01 | ecret |
./02 | 43ys |
./03 | Very/Easy |
./04 | hiddenPhrase |
./05 | ROBINET |
./06 | 1950 |
./07 | 2849 |
./08 | g00d3n0ugh |
./09 | 9876543210 |
./owl | 3333333333333333333333333333333333333 |
./scorpion | FAIL! |
Méthodologie
Outils utilisés : gdb-gef, objdump, strings, grep, hexdump, readelf.
Protocole
- exécution du programme afin d’observer son comportement ;
- appel à
strings <exe>afin d’observer les chaînes interprétées dans la section de données ; - utilisation de
gdbafin d’afficher les fonctions (info functions) :- s’il y a des
strcmp, on positionne des points d’arrêt (breakpoint) afin d’analyser les registres et/ou la mémoire ; et subséquemment, d’en déduire le mot de passe ; - sinon, on évalue le flot d’exécution du programme ;
- s’il y a des
- on effectue des hypothèses, testées en modifiant manuellement la valeur des registres si besoin.
Résolutions
00 — Crackme 00
Contexte. Binaire simple : comparaison directe via strcmp.
Méthodologie.
(gdb) b strcmp
(gdb) b main
(gdb) rConstat / analyse. Comparaison directe contre une chaîne stockée en dur.
Résultat. Mot de passe : mistigri.
01 — Crackme 01
Contexte. Binaire contenant une chaîne en dur, accessible via la table de fonctions.
Méthodologie.
(gdb) info functions
# repérer fonction cible et ses constantes
(gdb) b <fonction_identifiée>
(gdb) rConstat / analyse. La chaîne est écrite en dur dans le binaire avant l’entrée dans la fonction de vérification.
Résultat. Mot de passe : ecret.
02 — Crackme 02
Contexte. Vérification via strcmp ; utile d’inspecter les registres passés en argument.
Méthodologie.
(gdb) b strcmp
(gdb) r
(gdb) print $rdi # pointeur vers la chaîne attendueConstat / analyse. rdi pointe vers la chaîne attendue — la lecture directe en mémoire permet la reconstruction.
Résultat. Mot de passe : 43ys.
03 — Crackme 03
Contexte. Vérification par comparaison entre registres (eax vs edx). L’analyse montre une transformation (décalage) appliquée aux caractères d’entrée.
Méthodologie.
# breakpoint autour de la routine de comparaison
(gdb) b *<addr_cmp>
(gdb) r
# observer eax/edx et les valeurs des caractères en boucleConstat / analyse. Chaque caractère d’entrée est comparé avec la cible après l’application d’un chiffrement de César. Inverser ce décalage permet de retrouver la chaîne correcte.
Résultat. Mot de passe : Very/Easy.
04 — Crackme 04
Contexte. Comparaisons itératives caractère par caractère ; registre al utilisé pour les comparaisons successives.
Méthodologie.
(gdb) info functions
(gdb) b strcmp
(gdb) rEn particulier, itérer et lire al à chaque comparaison impose a fortiori de relancer le programme à chaque fois avec la valeur adéquate du caractère décodé pour accéder à l’itération suivante.
Constat / analyse. En itérant les valeurs de al à chaque comparaison, on reconstitue la chaîne caractère par caractère.
Résultat. Mot de passe : hiddenPhrase.
05 — Crackme 05
Contexte. Vérification impliquant des opérations bitwise et arithmétiques sur des registres (dil, sil).
Méthodologie.
(gdb) b *<addr_op>
(gdb) r
# step, observer dil/sil, noter les transformations (sub, shl, etc.)Constat / analyse. Séquence observée : sub dil puis shl (shift left). Les valeurs observées de dil sont indépendantes de l’entrée utilisateur : la condition finale s’attend à un résultat précis égal à 0x80.
Résultat. Mot de passe : ROBINET.
06 — Crackme 06
Contexte. La vérification compare le résultat d’une addition d’arguments à une constante (0x79e).
Méthodologie.
(gdb) b atoi
# éviter la terminaison immédiate du programme
(gdb) b *main+<offset_ret>
(gdb) r
# examiner les valeurs passées à atoi et l'additionConstat / analyse. On identifie l’addition arg1 + arg2 comparée à 0x79e (hexadécimal). La résolution de l’équation donne la valeur attendue.
Résultat. Mot de passe : 1950 (0x79e = 1950).
07 — Crackme 07
Contexte. Comparaison entre 0x0b2b et l’entrée.
Méthodologie.
(gdb) b *<addr_cmp>
(gdb) r
# suivre les deux comparaisons successivesConstat / analyse. Le flot effectue une comparaison ; la résolution de la contrainte donne la chaîne attendue.
Résultat. Mot de passe : 2849.
08 — Crackme 08
Contexte. Pattern mémoire observé ; lecture sur rdi + rcx - 0x4. Pattern répétitif tous les deux caractères.
Méthodologie.
- Analyse statique :
objdump -d crackme08 > dump.txt
# repérer .text et adresses pertinentes- Débogage dynamique :
(gdb) b *0x<addr_text>
(gdb) r
# observer accès mémoire (x/s, x/b) sur les offsets rdi+rcx-0x4Constat / analyse. Accès mémoire répétitif révélant un motif sur deux caractères — reconstitution progressive de la chaîne cible.
Résultat. Mot de passe : g00d3n0ugh.
09 — Crackme 09
Contexte. Vérification directe, simple comparateur.
Méthodologie.
(gdb) b strcmp
(gdb) r
# observer la chaîne attendueConstat / analyse. Comparaison explicite.
Résultat. Mot de passe : 9876543210.
Scorpion (binaire corrompu)
Format ELF corrompu
Le binaire reste exécutable mais n’est pas déboguable sans correction des headers.
Étapes pour rétablir / analyser.
- Diagnostic :
readelf -l -a scorpion-
Observations :
shnum = 7erroné (en effet, la taille des headers de section est quant à elle nulle, ce qui est incohérent avec la valeur deshnum).
-
Corrections appliquées :
- réécriture des variables
shnum,shentsize,shstrndxpour restaurer la cohérence ELF en l’absence effective de headers de section. En pratique, celles-ci ont été mises à 0.
- réécriture des variables
-
Analyse après correction :
- identification des routines équivalentes à
strlen, génération d’une transformation sur l’entrée (endianness inversion, shifts) ; - présence de syscalls non déterministes (arguments dépendants du flot d’exécution).
- identification des routines équivalentes à
Restauration d'un binaire ELF
La réparation du format ELF est délicate et documentée dans le cas présent pour garder la traçabilité. Toujours conserver une copie du binaire original.
Owl
Format ELF corrompu
Le binaire reste exécutable mais n’est pas déboguable sans correction des headers.
Observations.
- On constate dans
imhexqu’il y a trois headers de section, le premier étant complètement nul. - Le header ELF décrit mal les headers de section.
Restauration ELF.
- Suppression du header nul et mise à jour de
shnumà2. - Fix
shentsize = 0x40(64 octets équivalant à la taille d’un header de section) dont la valeur était initialement nulle. - Fix
shstrndx = 1.
Exploitation.
- La portion de code contiguë au point d’entrée se termine par
jmp rbx. rbxcontient une adresse calculée à partir du nombre d’arguments fournis àowl.- Point d’arrêt utile :
jmp rbxpour capturer l’adresse de saut et suivre le flot calculé. - Il faut un seul argument pour que l’instruction
jmpaccède à des instructions valides (sinonsegfault).
- Point d’arrêt utile :
- En déboguant le programme et en explorant l’exhaustivité des branchements conditionnels par forçage de la valeur des registres via
set $<register>, on constate que la chaîne doit être de taille0x25. - On observe qu’une nouvelle chaîne est construite à partir de la chaîne fournie en argument. Cette dernière représente le code ASCII en hexadécimal de la chaîne originale.
- Ultimement, on somme toutes les valeurs numériques de la chaîne originale, puis de celle produite. Ensuite, on multiplie le résultat obtenu pour la chaîne originale par 2 en faisant un shift left 1. Enfin, on compare les résultats : l’égalité permet d’afficher
OKet de terminer le programme normalement.
WARNING
La construction de la chaîne produite implique qu’elle soit deux fois plus longue ; nous avons donc l’intuition d’un point fixe par la transformation appliquée à la chaîne originale. Et en effet, après un parcours de la table ASCII, le caractère
3est un point fixe — car son code ASCII est33.
Résultat. Mot de passe : 3333333333333333333333333333333333333.