Solution de timruff pour Sloubi

intro reverse

24 mars 2026

On nous fourni un fichier : sloubi.

Tous d’abords il faut comprendre à quel fichier on a à faire.

$ file sloubi 
sloubi: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a1a4da4d1b6c8e7eae63c938e73b432edd273d68, for GNU/Linux 3.2.0, not stripped
$ chmod +x ./sloubi 
$ ./sloubi
ceci est une phrase
Nope.

On fait un file est on remarque que c’est un fichier ELF en 64 bit pour Linux.
On fait un chmod +x pour rendre exécutable le fichier sloubi (attention à ne faire que cela sur des binaires de confiance !).
On exécute le programme sloubi et on saisit une phrase pour voir le comportement, le programme affiche seulement “Nope.”.
On va décompiler le programme avec Ghidra.

undefined8 main(void)

{
  int iVar1;
  char *pcVar2;
  undefined8 uVar3;
  long in_FS_OFFSET;
  size_t local_78;
  undefined8 local_70;
  char local_68 [48];
  char local_38 [32];
  undefined1 uStack_18;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  pcVar2 = fgets(local_68,0x28,stdin);
  if (pcVar2 == (char *)0x0) {
    puts("Nope.");
    uVar3 = 1;
  }
  else {
    local_78 = strlen(local_68);
    if ((local_78 != 0) && (local_68[local_78 - 1] == '\n')) {
      local_68[local_78 - 1] = '\0';
      local_78 = local_78 - 1;
    }
    if (local_78 == 0x20) {
      for (local_70 = 0; local_70 < 0x20; local_70 = local_70 + 1) {
        local_38[(local_70 * 0x11 + 0x33) % 0x20] = local_68[local_70];
      }
      uStack_18 = 0;
      iVar1 = strcmp(local_38,"4B}mCuCNJmeVhvCzQusFHS7{2gCBCrQW");
      if (iVar1 == 0) {
        puts("Congrats! You can use this flag to validate the challenge.");
      }
      else {
        puts("Nope.");
      }
      uVar3 = 0;
    }
    else {
      puts("Nope.");
      uVar3 = 1;
    }
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return uVar3;
}

On a certaines choses importantes.

pcVar2 = fgets(local_68,0x28,stdin);

On voit que notre saisie se fait dans la variable “local_68” et qu’elle peut avoir au maximum une longueur de 40 (0x28) caractères au maximum.

local_78 = strlen(local_68);
    if ((local_78 != 0) && (local_68[local_78 - 1] == '\n')) {
      local_68[local_78 - 1] = '\0';
      local_78 = local_78 - 1;
    }

Ici on stocke la taille la chaîne “local_68” dans local_78.
Si la taille est différent de zéro, et que l’avant caractère de local_68 est un retour chariot alors on supprime le retour chariot et on le remplace par un caractère fin de chaîne.

if (local_78 == 0x20) {
      for (local_70 = 0; local_70 < 0x20; local_70 = local_70 + 1) {
        local_38[(local_70 * 0x11 + 0x33) % 0x20] = local_68[local_70];
      }
      uStack_18 = 0;
      iVar1 = strcmp(local_38,"4B}mCuCNJmeVhvCzQusFHS7{2gCBCrQW");
      if (iVar1 == 0) {
        puts("Congrats! You can use this flag to validate the challenge.");
      }

Il y a une vérification de la taille de la chaîne saisie est doit faire 32 (0x20) caractères.
Après il y a une boucle for qu’il fait plusieurs choses :

(indice_Saisie*17+51)mod 32 <=tableauSaisie[indice_chaineSaisie]) 

solution

On fait l’opération inverse :

scrambled = "4B}mCuCNJmeVhvCzQusFHS7{2gCBCrQW"
size = 32

# On reconstruit local_68 (original) en utilisant la même formule de permutation
original = [''] * size
for i in range(size):
    j = (i * 17 + 51) % size   # indice dans scrambled pour le caractère original à la position i
    original[i] = scrambled[j]

flag = ''.join(original)
print(flag)

On retrouve le flag :

$ python3 ./sloubi.py 
FCSC{XXX}