Introduction
ShookyPass est un challenge de HackTheBox de type reversing. Cette catégorie regroupe des challenges où l'objectif est de comprendre le fonctionnement interne d'un programme, d'un binaire ou d'un algorithme sans avoir accès au code source original.
Résolution
1/ Récupérons le fichier du challenge. Il s'agit d'un fichier nommé pass.
2/ Grâce à l'utilitaire file, nous pouvons en apprendre un peu plus sur ce fichier:
# file pass
pass: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=3008217772cc2426c643d69b80a96c715490dd91, for GNU/Linux 4.4.0, not strippedNous apprenons ici qu'il s'agit d'un exécutable 64 bits PIE (ASLR actif). Le binaire n'est pas strippé ce qui pourra être utile par la suite.
Dans le détail, les informations sont les suivantes:
- ELF: Signifiant Executable and Linkable Format, nous apprenons ici que nous avons affaire à un format de fichier standard pour les exécutables et bibliothèques sur Linux.
- 64-bit LSB: Indique que l'architecture cible est en 64 bits (x86-64) et utilise l'ordre d'octets Little-Endian (LSB).
- pie executable: Signifiant Position-Independent Executable, cela indique que le binaire utilise l'ASLR du système d'exploitation. Concrètement ? A chaque exécution, il sera chargé d'un emplacement mémoire aléatoire différent. Disons que c'est une protection supplémentaire contre l'exploitation trop facile de débordement de mémoire.
- x86-64: Précise l'architecture du jeu d'instructions (processeurs 64 bits Intel/AMD).
- dynamically linked: Indique que le programme utilise des bibliothèques partagées externes (comme `libc`) qui seront chargées lors de l'exécution.
- interpreter /lib64/ld-linux-x86-64.so.2: Le chemin vers le chargeur dynamique, responsable du chargement des bibliothèques partagées nécessaires.
- for GNU/Linux 4.4.0: Contexte sur l'environnement et la version du noyau Linux pour lesquels le binaire a été compilé.
- not stripped: Information cruciale en reversing: cela signifie que le binaire contient encore les symboles (noms de fonctions et de variables), ce qui facilite grandement l'analyse dans un désassembleur.
3/ Pourquoi ne pas exécuter ce programme et voir ce qu'il a a nous offrir ?
# ./pass
Welcome to the SPOOKIEST party of the year.
Before we let you in, you'll need to give us the password:
You're not a real ghost; clear off!Bon... Le programme nous demande un mot de passe, et bien sûr, il râle parce que nous ne le connaissons pas. A ce stade, nous pouvons imaginer en terme un mécanisme de ce type:
if le_mot_de_passe_utilisateur == "le bon mot de passe"
alors => on donne le flag :)
sinon => on râle sur l'utilisateurReste donc à trouver ce fameux "le bon mot de passe".
4/ Trouver la chaîne de caractère attendue
Puisque nous sommes dans un challenge très simple, je vous propose non pas de décompiler le programme, mais simplement voir s'il ne reste pas des chaînes de caractères en clair dans l'exécutable. Cela confirmerait le fonctionnement que nous espérons de l'étape 3, à savoir une simple comparaison avec une chaîne en claire. J'utilise pour cela l'utilitaire strings qui va extraire ces chaînes du binaire.
# strings pass
-- STRIPED --
Welcome to the
[1;3mSPOOKIEST
[0m party of the year.
Before we let you in, you'll need to give us the password:
s3cr3t_p455_f0r_gh05t5_4nd_gh0ul5
Welcome inside!
You're not a real ghost; clear off!
-- STRIPED --Hum... Évidemment, les choses sont maintenant claires. On reconnaît le texte obtenu lors de l'exécution du programme à l'étape 3 et selon la logique attendue nous espérons que le mot de passe demandé est s3cr3t_p455_f0r_gh05t5_4nd_gh0ul5 .
5/ Réexécuter le programme en passant le mot de passe
Et nous obtenons bien le flag au format classique: HTB{...} .
Pour aller plus loin...
strings, mais pas le flag. Cela signifie que ce n'est pas une chaîne de caractère directement imprimée dans le code source, mais plutôt "fabriqué" par une sorte d'algorithme quelconque.Analysons un peu ce binaire avec le logiciel Ghidra. La décompilation nous donne ce "pseudo-code" suivant:
undefined8 main(void)
{
int iVar1;
char *pcVar2;
long in_FS_OFFSET;
uint local_c4;
char local_b8 [32];
char local_98 [136];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
-- STRIPED: init 32 bytes buffer --
puts("Welcome to the \x1b[1;3mSPOOKIEST\x1b[0m party of the year.");
printf("Before we let you in, you\'ll need to give us the password: ");
fgets(local_98,0x80,stdin);
pcVar2 = strchr(local_98,10);
if (pcVar2 != (char *)0x0) {
*pcVar2 = '\0';
}
iVar1 = strcmp(local_98,"s3cr3t_p455_f0r_gh05t5_4nd_gh0ul5");
if (iVar1 == 0) {
puts("Welcome inside!");
for (local_c4 = 0; local_c4 < 26; local_c4 = local_c4 + 1) {
local_b8[(int)local_c4] = (char)*(undefined4 *)(&parts + (long)(int)local_c4 * 4);
}
puts(local_b8);
}
else {
puts("You\'re not a real ghost; clear off!");
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}Il est très satisfaisant de voir que l'on retrouve exactement ce que l'on avait prédit: une simple comparaison strcmp avec la chaîne de caractère en dure. Puis, une petite procédure qui construit le flag et l'affiche à l'ecran:
for (local_c4 = 0; local_c4 < 26; local_c4 = local_c4 + 1) {
local_b8[(int)local_c4] = (char)*(undefined4 *)(&parts + (long)(int)local_c4 * 4);
}
puts(local_b8);On remarque toute la place pré-allouée en début de programme pour local_b8 dans laquelle le flag va être construit. Celui-ci est crée en récupérant 26 caractères au sein d'une structure nommée parts déclarée en constante dans le programme. local_c4 est un compteur de boucle. Pour chacun des 26 caractères, nous récupérons 1 octet tous les 4 octets dans le tableau parts et l'on considère cet octet comme char c'est à dire qu'on lit cette valeur comme une lettre ascii.
Pas simple à décrire avec des mots, alors voici ce que j'imagine être le code source d'origine:
int parts[] = {
0x41, 0x42, 0x43, 0x44, // etc.
};
char str[27];
for (int i = 0; i < 26; i++) {
str[i] = (char)parts[i];
}
str[26] = '\0';
puts(str);A droite, vous voyez d'ailleurs une vue du contenu mémoire de parts selon notre désassembleur. Nous y reconnaissons bien notre flag.

Ajouter un commentaire