Vous avez enfin atteint le niveau professionnel et, il faut faire vos preuves lors de cette première compétition. Le début de votre enchaînement gymnastique se déroule bien jusqu'à ce que votre plus grande peur se réalise. Une mauvaise réception en amenant une autre, vous ne contrôlez plus votre élan et tentez de vous rattraper tant bien que mal. Malheureusement, vous finissez par atterrir un peu trop loin en dehors des tapis.
Voici la dernière vision que vous avez avant de heurter le sol, est-ce que quelque chose peut encore vous aider?
Version simple
On pouvait utiliser l'outil
Version puriste
Pour ceux comme moi qui n'ont pas trouvé l'outil au-dessus...
On peut remarquer des choses assez atypiques sur l'image :
L'image semble se répéter et des lignes verticales régulières se voient sur toute la largeur
Des "artéfacts" de plus en plus présents vers la droite de l'image
On peut confirmer l'hypothèse des blocs qui se répètent avec un script Python pour regarder les valeurs des pixels. Ici, je regarde la ligne de pixels tout en haut de l'image puisqu'il ne semble par y avoir d'"artéfacts" dessus.
from PIL import Image
img = Image.open('chall_stega.png')
previous_x = 0
# pixel de référence
pixel = img.getpixel((previous_x, 0))
for x in range(previous_x+1, img.width):
# Affiche la position des pixels identique et l'offset à partir du précédent
if pixel == img.getpixel((x, 0)):
print(f'{x:>4} {x-previous_x}')
previous_x = x
# 172 172
# 343 171
# 514 171
# 685 171
# 856 171
# 1027 171
On voit clairement que notre pixel 0, 0 se répète plusieurs fois de manière régulière sur la ligne. On a un premier bloc de 172 pixels de large puis des blocs de 171.
Comme les blocs paraissent tous plus ou moins les mêmes, mais avec quelques différences, on peut s'amuser à les comparer. Pour ça, on va calculer la distance entre deux pixels situés au même endroit dans chacun.
Commençons par comparer le premier et le second bloc
from math import sqrt
from PIL import Image
img = Image.open('chall_stega.png')
def distance(p1: list[int], p2: list[int]) -> int:
# Les deux points sont bien de même dimension
assert len(p1) == len(p2)
# Calcul de distance classique
return round(sqrt(sum([(p1[i]-p2[i])**2 for i in range(len(p1))])))
def compare_blocs(bloc1: Image.Image, bloc2: Image.Image) -> Image.Image:
width = min(bloc1.width, bloc2.width)
height = min(bloc1.height, bloc2.height)
# 'L' créé une image en nuance de gris (grayscale)
# Donc 1 seul canal de couleur dans lequel on mettra la distance
diff = Image.new('L', (width, height), 'black')
# Pour tous les pixels
for y in range(height):
for x in range(width):
p1 = bloc1.getpixel((x, y))
p2 = bloc2.getpixel((x, y))
diff.putpixel((x, y), distance(p1, p2))
return diff
# Le premier bloc fait 172 de large
premier_bloc = img.copy().crop((0, 0, 172, img.height))
# Le second commence à 172 et fait 171
second_bloc = img.copy().crop((172, 0, 172 + 171, img.height))
diff = compare_blocs(premier_bloc, second_bloc)
diff.show()
On obtient l'image :
Tiens, tiens, tiens. Il semblerait qu'on a trouvé un début de flag. On peut en déduire que le premier bloc est était copié puis utilisé pour cacher le flag dedans.
Mais alors, est-ce que les blocs suivants utilisent tous le premier bloc ? Non, ils utilisent le bloc précédent.
On peut le voir en testant... ou en réfléchissant avec les premiers détails observés : les blocs vers la droite paraissent avoir de plus en plus d'"artéfacts" à l'intérieur, autrement dit ces répétitions apparaissent surement quand on manipule le bloc précédent et donc, les artéfacts se cumulent au fil des prochains blocs.