Hey, check this QR code ASAP! It's highly sensitive so I scrambled it but you shouldn't have a hard time reconstructing - just make sure to update the a_order to our shared PIN. The b_order is the reverse of that 😉
Solution
Players receive a jumbled up QR code, and the script used to generate it.
They simply need to make an opposing script to reconstruct it. Since they don't know the PIN, they can try all possible combinations (24) and see which produce valid QRs. The whole process can be automated to a single script.
solve.py
from PIL import Image, ImageDrawfrom itertools import permutationsimport subprocess# Load the rearranged QR code imagerearranged_image = Image.open("obscured.png")# Assuming the QR code is a square, get the dimensionswidth, height = rearranged_image.sizehalf_width, half_height = width //2, height //2# Define the bounding boxes for the four main squares in natural ordersquares ={"1": (0,0, half_width, half_height),# Top-left"2": (half_width,0, width, half_height),# Top-right"3": (0, half_height, half_width, height),# Bottom-left"4": (half_width, half_height, width, height) # Bottom-right}# Function to split a square into two trianglesdefsplit_square_into_triangles(img,box): x0, y0, x1, y1 = box# Define the points for the triangles a_triangle_points = [(x0, y0), (x1, y0), (x0, y1)] # Top-left triangle b_triangle_points = [(x1, y1), (x1, y0), (x0, y1)] # Bottom-right triangle# Crop and mask each triangledefcrop_triangle(points): mask = Image.new("L", img.size, 0) draw = ImageDraw.Draw(mask) draw.polygon(points, fill=255) triangle_img = Image.new("RGBA", img.size) triangle_img.paste(img, (0, 0), mask)return triangle_img.crop((x0, y0, x1, y1))returncrop_triangle(a_triangle_points),crop_triangle(b_triangle_points)# Split each square into two triangles and store themtriangle_images ={}for key, box in squares.items(): triangle_images[f"{key}a"], triangle_images[f"{key}b"]=split_square_into_triangles( rearranged_image, box)# Define the positions for the 4 quadrantsoriginal_positions = [ (0,0),# Top-left (half_width,0),# Top-right (0, half_height),# Bottom-left (half_width, half_height) # Bottom-right]# Brute-force through all permutations of a_orderfor a_order inpermutations(["1", "2", "3", "4"]):# Set b_order as the reverse of a_order b_order = a_order[::-1]# Create a new blank image for each permutation original_image = Image.new("RGBA", rearranged_image.size)# Assemble the image based on a_order and b_orderfor i inrange(4): a_triangle = triangle_images[f"{a_order[i]}a"] b_triangle = triangle_images[f"{b_order[i]}b"]# Combine triangles in a new square combined_square = Image.new("RGBA", (half_width, half_height)) combined_square.paste(a_triangle, (0, 0)) combined_square.paste(b_triangle, (0, 0), b_triangle)# Paste the combined square into the original image original_image.paste(combined_square, original_positions[i])# Save the reconstructed image original_image.save("temp_original.png")# Run zbarimg on the saved image to try decoding it result = subprocess.run( ["zbarimg", "temp_original.png"], capture_output=True, text=True) output = result.stdout.strip()# Check if zbarimg returned a valid QR code outputif output:print(f"Decoded QR code with a_order {a_order} and b_order {b_order}:")print(output)break# Stop after finding the first valid QR codeelse:print("No readable QR code found in any permutation.")
Run it and get the flag!
pythonsolve.pyDecodedQRcodewitha_order ('2', '4','1','3') and b_order ('3','1','4','2'):QR-Code:INTIGRITI{7h475_h0w_y0u_r3c0n57ruc7_qr_c0d3}