4 min read

[CTF GEMA] Free Flag

CTF GEMA Groupe 2025

Niveau de Difficulté : Hard

Catégorie du Challenge : Forensic

Description :

I think it’s too sensitive so they hide it so much like that First we install the file and we got a picture of a red flag as the following

Steps to Solve

Local Image

La première étape dans tout défi de forensique consiste à vérifier s'il y a des éléments suspects en utilisant la commande strings, ainsi que des fichiers cachés avec la commande binwalk, comme suit :

Local Image

Booyah, nous avons trouvé un mot encodé en BASE64... nous allons maintenant le décoder.

Local Image

Nous avons ce message (1000x570 S)... Je pense que c’est la taille de quelque chose, mais continuons à chercher... EDIT (((LE NOM DU FICHIER EST FREEFLAG.jpg))).

Local Image

Ici, nous avons utilisé la commande binwalk, et nous avons découvert qu'il y avait des fichiers cachés dedans. Extrayons-les avec cette commande :

binwalk -e --dd=.* FREEFLAG.jpg

Maintenant, nous avons ces fichiers à l'intérieur.

Local Image
Local Image

Et un fichier zip contient (flag.txt), mais il est protégé par un mot de passe et aucun mot de passe dans les listes de mots ne fonctionne pour ce fichier, donc nous devons trouver un mot de passe.

Local Image

Après une recherche approfondie sans indice et sans rien de vraiment utile, nous devons penser aux 7 images qui portent la mention (___ false). Pourquoi existent-elles en premier lieu ?


Nous avons découvert que les images représentent un séparateur entre les différentes parties du mot de passe comme suit... Nous savons tous que les fichiers PNG commencent par (IHDR) et se terminent par (IEND)...


Donc, lorsque nous regardons la fin de la première image en utilisant la commande strings et que nous observons ce qui se trouve entre la première et la deuxième image, nous découvrons qu'il y a un mot entre chaque image et la suivante.

Local Image

Lorsque nous les rassemblons, nous obtenons le mot de passe. Le mot de passe est : WTBVTjQxTDNEVEgzUDRTUW==, et il est évidemment encodé en BASE64. Nous l'avons donc décodé en utilisant CyberChef.

Local Image

Maintenant, nous avons le mot de passe original qui est : Y0UN4IL3DTH3P4SS, et maintenant, enfin, nous pouvons extraire le (flag.txt)...

Local Image

Après l'extraction, nous avons trouvé beaucoup et beaucoup de zéros et de uns, chaque chiffre étant sur une nouvelle ligne...


Nous pouvons connaître le nombre de zéros et de uns en observant la taille du fichier, car comme nous le savons tous, le zéro représente un bit et le un aussi.

Local Image

Nous avons obtenu la taille du fichier, qui est de 1 139 999 bits.
Mais nous ne pouvons pas dire qu'il y a un million et 140 000 zéros et uns dans ce fichier, car chaque chiffre est sur une nouvelle ligne et nous savons que le caractère ( ) est considéré comme un bit. Nous divisons donc la taille du fichier par 2, ce qui nous donne 570 001 bits (570 000 zéros et uns).
Mais c'est le nombre que nous avons trouvé au début, 1000x570 bits. Maintenant, nous savons ce que cela signifie, c'est la taille des zéros et des uns sur chaque ligne.
Donc, en utilisant Python, nous allons organiser le fichier de manière à ce qu'il y ait 570 colonnes par ligne {570 zéros et uns sur chaque ligne pour 1000 lignes}. Et maintenant, nous obtenons cela.

width, height = 1000, 570

file_path = "flag.txt"
with open(file_path, "r") as f:
    data = f.read().split()

assert len(data) == width * height, "La taille des données ne correspond pas à la résolution indiquée."

pbm_path = "flag.pbm"
with open(pbm_path, "w") as f:
    f.write(f"P1\n{width} {height}\n")
    for i in range(height):
        f.write(" ".join(data[i * width:(i + 1) * width]) + "\n")

pbm_path
from PIL import Image
import numpy as np

pbm_path = "flag.pbm"
with open(pbm_path, "r") as f:
    lines = f.readlines()

assert lines[0].strip() == "P1", "Format PBM non reconnu."
width, height = map(int, lines[1].split())
data = np.array([int(pixel) for line in lines[2:] for pixel in line.split()]).reshape((height, width))

png_path = "flag.png"
img = Image.fromarray((data * 255).astype(np.uint8))
img.save(png_path)

png_path

Le flag est : FLAG{Y0UG0TMY1DEA}.


Flag

FLAG{Y0UG0TMY1DEA}