CTF Writeups
WebsiteDiscordSocials..
  • CryptoCat's CTF writeups
  • 2025
    • Tsuku
      • Web
        • len_len
        • Flash
        • YAMLwaf
    • CTF@CIT
      • Web
        • Breaking Authentication
        • Commit & Order: Version Control Unit
        • How I Parsed your JSON
        • Mr. Chatbot
        • Keeping Up with the Credentials
  • 2024
    • CryptoCat
      • Summar-AI-ze
    • Intigriti
      • Warmup
        • BabyFlow
        • In Plain Sight
        • IrrORversible
        • Layers
        • Rigged Slot Machine 1
      • Game
        • Bug Squash 1
        • Bug Squash 2
      • Rev
        • Secure Bank
      • Web
        • Biocorp
        • Cat Club
        • Pizza Paradise
        • SafeNotes 2.0
      • Misc
        • Quick Recovery
        • Triage Bot 2
      • Pwn
        • Floormat Sale
        • Retro2Win
        • Rigged Slot Machine 2
        • UAP
      • Crypto
        • Schrodinger's Pad
      • Mobile
        • Cold Storage
      • OSINT
        • No Comment
        • Trackdown
        • Trackdown 2
      • Forensics
        • CTF Mind Tricks
        • Hoarded Flag
        • Password Management
    • CSAW
      • Web
        • Playing on the Backcourts
        • Log Me In
        • Lost Pyramid
        • BucketWars
    • CyberSpace
      • Web
        • Feature Unlocked
    • UIU
      • Web
        • Fare Evasion
        • Log Action
    • Wani
      • Web
        • Bad Worker
        • PoW
        • One Day One Letter
    • Akasec
      • Web
    • HTB Cyber Apocalypse
      • Web
        • Flag Command
        • TimeKORP
        • KORP Terminal
        • Labyrinth Linguist
        • Locktalk
        • SerialFlow
        • Testimonial
  • 2023
    • Intigriti
      • Gamepwn
        • Dark Secrets
      • Misc
        • Triage Bot
      • OSINT
        • Photographs
      • Pwn
        • Floormat Store
      • Web
        • Bug Report Repo
        • My Music
    • Imaginary
      • Web
        • Blank
        • IDORiot
        • Inspection
        • Login
        • Perfect Picture
        • Roks
    • Google
      • Pwn
        • Write-Flag-Where
    • Cyber Apocalypse
      • AI
        • Last Hope
        • Mysterious Learning
      • Crypto
        • Perfect Synchronization
      • Pwn
        • Getting Started
        • Labyrinth
        • Pandora's Box
        • Void
      • Rev
        • Cave System
        • Hunting License
        • Needle in a Haystack
        • Shattered Tablet
        • She Sells Sea Shells
    • Sekai
      • Rev
        • Azusawa's Gacha World
    • Amateurs
      • Web
        • Sanity
        • Waiting an Eternity
    • NahamCon
      • Web
        • Hidden Figures
        • Marmalade 5
        • Obligatory
        • Star Wars
        • Stickers
    • Angstrom
      • Pwn
        • Leek
  • 2022
    • Imaginary (iCTF)
      • Crypto
        • ASE
      • Pwn
        • Links 1
        • Links 2
        • Links 3
        • Open Doors
    • SEETF
      • Pwn
        • 4mats
        • Easy Overflow
      • Rev
        • BabyReeee
      • Web
        • Super-Secure-Requests-Forwarder
    • HTB Cyber Apocalypse
      • Pwn
        • Hellbound
    • Angstrom
      • Pwn
        • Really Obnoxious Problem
        • Wah
        • Whats My Name
        • Where Am I
      • Web
        • Crumbs
        • Xtra Salty Sardines
    • NahamCon
      • Pwn
        • Baby Steps
      • Web
        • Flaskmetal Alchemist
        • Hacker Ts
        • Two for One
    • Pico
      • Forensics
        • Side Channel
      • Pwn
        • Buffer Overflow 1
        • Buffer Overflow 2
        • Buffer Overflow 3
        • Flag Leak
        • Function Overwrite
        • ROPfu
        • RPS
        • Stack Cache
        • Wine
        • X-Sixty-What
      • Rev
        • Wizardlike
      • Web
        • Noted
    • Space Heroes
      • Pwn
        • Vader
      • Web
        • Flag in Space
    • Intigriti
      • Pwn
        • Bird
        • Cake
        • Easy Register
        • Search Engine
    • Dice
      • Pwn
        • Interview Opportunity
  • 2021
    • Pico
      • Pwn
        • Unsubscriptions Are Free
    • Crusaders of Rust (COR)
      • Crypto
        • Fibinary
      • Pwn
        • Chainblock
    • HTB Cyber Santa
      • Crypto
        • Meet Me Halfway
        • Xmas Spirit
      • Pwn
        • Minimelfistic
        • Mr. Snowy
        • Naughty List
        • Sleigh
      • Rev
        • Infiltration
        • Intercept
    • K3rn3l
      • Crypto
        • Badseed
        • Twizzty Buzzinezz
    • HTB x Synack RedTeamFive
      • Misc
        • Context
        • Hotel
      • Pwn
        • Air Supplies
        • Injection Shot
        • Library
        • Recruitment
      • Rev
        • Knock Knock
        • Split
    • KillerQueen
      • Pwn
        • A Kind of Magic
        • Tweety Birb
        • Zoom2Win
    • HacktivityCon
      • Pwn
        • Retcheck
        • The Library
        • Yabo
      • Web
        • Availability
    • CSAW
      • Pwn
        • Alien Math
        • Password Checker
      • Rev
        • Checker
    • HackyHolidays
      • Crypto
        • Cute Invoice
        • Mineslazer
      • Forensics
        • Injection Traffic
        • Power Snacks
      • Pwn
        • Deleted Flag
        • Engine Control
      • Web
        • Skylark
    • HTB Cyber Apocalypse
      • Crypto
        • Phasestream
      • Misc
        • Alien Camp
        • Build Yourself In
      • Pwn
        • Controller
        • System Drop
      • Web
        • Blitzprop
        • E-Tree
        • Wild Goose Hunt
    • Angstrom
      • Pwn
        • Sanity Checks
        • Secure Login
        • Sticky Stacks
        • Tranquil
      • Rev
        • Free Flags
        • Jailbreak
      • Web
        • Jar
Powered by GitBook
On this page
  • Challenge Description
  • Solution
  • gen.py
  • solve.py
  1. 2024
  2. Intigriti
  3. Misc

Quick Recovery

Writeup for Quick Recovery (Misc) - 1337UP LIVE CTF (2024) 💜

PreviousMiscNextTriage Bot 2

Last updated 4 months ago

Challenge Description

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.

gen.py

from PIL import Image, ImageDraw
from itertools import permutations
import subprocess

qr_code_image = Image.open("qr_code.png")
width, height = qr_code_image.size
half_width, half_height = width // 2, height // 2

squares = {
    "1": (0, 0, half_width, half_height),
    "2": (half_width, 0, width, half_height),
    "3": (0, half_height, half_width, height),
    "4": (half_width, half_height, width, height)
}


def split_square_into_triangles(img, box):
    x0, y0, x1, y1 = box
    a_triangle_points = [(x0, y0), (x1, y0), (x0, y1)]
    b_triangle_points = [(x1, y1), (x1, y0), (x0, y1)]

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

    return crop_triangle(a_triangle_points), crop_triangle(b_triangle_points)


triangle_images = {}
for key, box in squares.items():
    triangle_images[f"{key}a"], triangle_images[f"{key}b"] = split_square_into_triangles(
        qr_code_image, box)

a_order = ["1", "2", "3", "4"]  # UPDATE ME
b_order = ["1", "2", "3", "4"]  # UPDATE ME

final_positions = [
    (0, 0),
    (half_width, 0),
    (0, half_height),
    (half_width, half_height)
]

reconstructed_image = Image.new("RGBA", qr_code_image.size)

for i in range(4):
    a_triangle = triangle_images[f"{a_order[i]}a"]
    b_triangle = triangle_images[f"{b_order[i]}b"]
    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)
    reconstructed_image.paste(combined_square, final_positions[i])

reconstructed_image.save("obscured.png")
print("Reconstructed QR code saved as 'obscured.png'")

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, ImageDraw
from itertools import permutations
import subprocess

# Load the rearranged QR code image
rearranged_image = Image.open("obscured.png")

# Assuming the QR code is a square, get the dimensions
width, height = rearranged_image.size
half_width, half_height = width // 2, height // 2

# Define the bounding boxes for the four main squares in natural order
squares = {
    "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 triangles
def split_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 triangle
    def crop_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))

    return crop_triangle(a_triangle_points), crop_triangle(b_triangle_points)

# Split each square into two triangles and store them
triangle_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 quadrants
original_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_order
for a_order in permutations(["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_order
    for i in range(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 output
    if 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 code
else:
    print("No readable QR code found in any permutation.")

Run it and get the flag!

python solve.py
Decoded QR code with a_order ('2', '4', '1', '3') and b_order ('3', '1', '4', '2'):
QR-Code:INTIGRITI{7h475_h0w_y0u_r3c0n57ruc7_qr_c0d3}

Flag: INTIGRITI{7h475_h0w_y0u_r3c0n57ruc7_qr_c0d3}