TamuCTF 2023 - Gamer Redux
Énoncé
Dans ce challenge, on nous a donné un fichier qui contient une série de commandes Minecraft. Voici un aperçu des commandes :
summon armor_stand ~2 ~-3 ~2 {NoGravity:1,Invisible:1}
summon armor_stand ~3 ~-3 ~2 {NoGravity:1,Invisible:1}
...
Nous ne savons pas à quoi cela correspond, mais puisque Minecraft est connu pour fabriquer des pixel art, nous avons décidé d’explorer cette possibilité.
Solution
Étape 1 : Lire le fichier et extraire les coordonnées
Tout d’abord, nous avons lu le fichier et extrait les coordonnées des blocs présents dans les commandes Minecraft. Pour ce faire, nous avons utilisé le code suivant :
import re
def read_minecraft_command(file_name):
with open(file_name, 'r') as file:
command = file.read()
return command
def extract_coordinates(command):
pattern = r"summon armor_stand ~(\d+) ~-3 ~(\d+)"
matches = re.findall(pattern, command)
coordinates = [(int(x), int(y)) for x, y in matches]
return coordinates
Étape 2 : Créer une grille et afficher les coordonnées
Ensuite, nous avons créé une grille pour afficher les coordonnées extraites. Le code pour cela est le suivant :
def create_grid(coordinates, width, height):
grid = [[' ' for _ in range(width)] for _ in range(height)]
for x, y in coordinates:
try:
grid[y-1][x-1] = '#'
except IndexError:
print(f"Index out of range: ({x-1}, {y-1})")
return grid
def display_grid(grid):
for row in grid:
print(''.join(row))
Étape 3 : Générer un QR Code à partir de la grille
Après avoir affiché la grille, nous avons réalisé qu’il s’agissait d’un QR Code. Pour générer une image QR Code à partir de la grille, nous avons utilisé les bibliothèques PIL
et opencv
:
from PIL import Image, ImageDraw
import cv2
def generate_qr_image(grid, cell_size=10, margin=10):
width = len(grid[0]) * cell_size + margin * 2
height = len(grid) * cell_size + margin * 2
image = Image.new('1', (width, height), 1)
draw = ImageDraw.Draw(image)
for y, row in enumerate(grid):
for x, cell in enumerate(row):
if cell == '#':
draw.rectangle(
[x * cell_size + margin, y * cell_size + margin, (x + 1) * cell_size + margin, (y + 1) * cell_size + margin],
fill=0,
)
return image
Étape 4 : Lire le QR Code et extraire le texte
Finalement, nous avons utilisé la bibliothèque pyzbar
pour lire le QR Code généré et extraire le texte. Voici le code correspondant :
Voici le code correspondant :
from pyzbar.pyzbar import decode
def read_qr_code(image_path):
image = cv2.imread(image_path)
decoded_objects = decode(image)
if decoded_objects:
return decoded_objects[0].data.decode('utf-8')
else:
return None
Étape 5 : Exécuter le code et obtenir le texte
Maintenant, nous mettons le tout ensemble et exécutons le code pour obtenir le texte du QR Code :
file_name = "sus.txt"
command = read_minecraft_command(file_name)
coordinates = extract_coordinates(command)
width, height = 27, 27
grid = create_grid(coordinates, width, height)
print("Grid:")
display_grid(grid)
qr_image = generate_qr_image(grid)
qr_image.save('qr_code.png')
print("Image saved to qr_code.png")
qr_text = read_qr_code('qr_code.png')
print('QR code content:', qr_text)
Et voici le code complet:
import re
import cv2
from pyzbar.pyzbar import decode
from collections import defaultdict
from PIL import Image, ImageDraw
def read_qr_code(image_path):
image = cv2.imread(image_path)
decoded_objects = decode(image)
if decoded_objects:
return decoded_objects[0].data.decode('utf-8')
else:
return None
def generate_qr_image(grid, cell_size=10, margin=10):
width = len(grid[0]) * cell_size + margin * 2
height = len(grid) * cell_size + margin * 2
image = Image.new('1', (width, height), 1)
draw = ImageDraw.Draw(image)
for y, row in enumerate(grid):
for x, cell in enumerate(row):
if cell == '#':
draw.rectangle(
[x * cell_size + margin, y * cell_size + margin, (x + 1) * cell_size + margin, (y + 1) * cell_size + margin],
fill=0,
)
return image
def read_minecraft_command(file_name):
with open(file_name, 'r') as file:
command = file.read()
return command
def extract_coordinates(command):
pattern = r"summon armor_stand ~(\d+) ~-3 ~(\d+)"
matches = re.findall(pattern, command)
coordinates = [(int(x), int(y)) for x, y in matches]
return coordinates
def create_grid(coordinates, width, height):
grid = [[' ' for _ in range(width)] for _ in range(height)]
for x, y in coordinates:
try:
grid[y-1][x-1] = '#'
except IndexError:
print(f"Index out of range: ({x-1}, {y-1})")
return grid
def display_grid(grid):
for row in grid:
print(''.join(row))
file_name = "sus.txt"
command = read_minecraft_command(file_name)
coordinates = extract_coordinates(command)
width, height = 27, 27
grid = create_grid(coordinates, width, height)
print("Grid:")
display_grid(grid)
qr_image = generate_qr_image(grid)
qr_image.save('qr_code.png')
print("Image saved to qr_code.png")
qr_text = read_qr_code('qr_code.png')
print('QR code content:', qr_text)
En exécutant ce code, nous avons pu extraire le texte du QR Code, qui est la réponse à ce challenge.
$ python3.10 sus.py
Grid:
####### ### # #######
# # ## # # # #
# ### # ### # # # ### #
# ### # # ## ### # ### #
# ### # # # # ### # ### #
# # ## # # # #
####### # # # # # #######
# # ##
## ### ## ## ## ##
#### # ## # # # ###
# ### # ## ## ## ### ###
# # ## ## ## # ##
## ####### ## # # #
# ## ### # ## # #
# #### ### ### # # ##
# ## # # ## # ####
# ## ## # ## #########
## ### ###
####### # # ## ## # # # #
# # # ## ## ## #
# ### # ########### #
# ### # # ### ## ##
# ### # # ### # # # #
# # # # ## #### #
####### # # ### #
Image saved to qr_code.png
QR code content: gigem{cr33p3r_4w_m4444n}
Et on obtient le QR code suivant:
Le flag est gigem{cr33p3r_4w_m4444n}
Conclusion
Dans ce challenge, nous avons utilisé des bibliothèques Python telles que re
, PIL
, opencv
, et pyzbar
pour lire un fichier contenant des commandes Minecraft, extraire les coordonnées, créer une grille représentant un QR Code, générer une image à partir de cette grille, et finalement lire le QR Code pour obtenir le texte. Cette approche nous a permis de résoudre le challenge Gamer Rédux dans la catégorie MISC du TamuCTF 2023.