Cover Image

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.