TamuCTF 2023 - Gamer Redux
Table of Contents
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:
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.