Cover Image

TamuCTF 2023 - Gamer Redux

Challenge Overview

In this challenge, we were given a file that contains a series of Minecraft commands. Here is an overview of the commands:

summon armor_stand ~2 ~-3 ~2 {NoGravity:1,Invisible:1}
summon armor_stand ~3 ~-3 ~2 {NoGravity:1,Invisible:1}
...

We don’t know what this corresponds to, but since Minecraft is known for creating pixel art, we decided to explore this possibility.

Solution

Step 1: Read the File and Extract Coordinates

First, we read the file and extracted the coordinates of the blocks present in the Minecraft commands. To do this, we used the following code:

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

Step 2: Create a Grid and Display Coordinates

Next, we created a grid to display the extracted coordinates. The code for this is as follows:

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))

Step 3: Generate a QR Code from the Grid

After displaying the grid, we realized it was a QR Code. To generate a QR Code image from the grid, we used the PIL and opencv libraries:

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

Step 4: Read the QR Code and Extract Text

Finally, we used the pyzbar library to read the generated QR Code and extract the text. Here is the corresponding code:

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

Step 5: Execute the Code and Get the Text

Now, we put it all together and execute the code to get the QR Code text:

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)

Complete Code

And here is the complete code:


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)

Result

By executing this code, we were able to extract the QR Code text, which is the answer to this challenge.

$ python3.10 sus.py 
Grid:
                           
 #######  ###  #   ####### 
 #     #    ## # # #     # 
 # ### #  ###  # # # ### # 
 # ### # # ## ###  # ### # 
 # ### # # # # ### # ### # 
 #     #  ##   # # #     # 
 ####### # # # # # ####### 
          #  #  ##         
 ##   ### ## ## ##   ##    
 #### # ## #    # #  ###   
 # ### # ## ## ##  ### ### 
  #  #  ## ##    ##   # ## 
 ##  #######   ##  #  # #  
 # ##    ### # ## #   #    
 #  #### ###   ### # #  ## 
 # ##      # # ##  # ####  
 # ## ##  #  ##  ######### 
         ##    ###   ###   
 ####### # # ## ## # # # # 
 #     # # ##   ##   ##  # 
 # ### #   ########### #   
 # ### #   # ###   ##   ## 
 # ### #   #  ### # #  # # 
 #     # # #    ## ####  # 
 ####### #       #  ###  # 
                           
Image saved to qr_code.png
QR code content: gigem{cr33p3r_4w_m4444n}

Generated QR Code

And we get the following QR code:

QR code
Figure 0x1 – QR code

Flag

The flag is gigem{cr33p3r_4w_m4444n}

Conclusion

In this challenge, we used Python libraries such as re, PIL, opencv, and pyzbar to read a file containing Minecraft commands, extract coordinates, create a grid representing a QR Code, generate an image from this grid, and finally read the QR Code to obtain the text. This approach allowed us to solve the Gamer Redux challenge in the MISC category of TamuCTF 2023.