Hackvent 2024: Day 4

Hackvent 202430

[HV24.04] Missing QR

Oh my Santa, the same elf who once messed up the color table did it again. But this time he seems to have been interrupted while painting the Christmas ball. Maybe you can help Santa finish his job?

Analyze the image and get the flag.
Flag format: HV24{}
sha256sum of the GIF: 7a5c0093b7f6d10f4cd55986a58bf1420ab90d09013b96d5692fcd7399f3fd6d

This challenge was written Dr. Nick. Maybe I was wrong all along and the Dr. is referring to a doctorate in GIFs.
Hackvent 2024 - Day 4 - Image

Solution

We are given an image of a QR code and told that the elves messed up the colour table and were interrupted while painting it

First, we write a Python script to output the index, RGB color, HEX color, and binary representation for each entry in the GIF global colour palette.

# Hackvent 2024 - Day 4
# Mo Beigi
#
# Print out colour palette

from PIL import Image

def analyze_palette_usage(image_path):
    """
    Outputs all palette colors and then lists used and unused colors in hex, RGB, and binary formats.

    Parameters:
    - image_path: Path to the input image.
    """
    img = Image.open(image_path)

    if img.mode != "P":
        print("The image is not palette-based.")
        return

    # Get the palette
    palette = img.getpalette()
    num_colors = len(palette) // 3

    # Load the pixel data
    pixels = img.load()
    width, height = img.size

    # Track used palette indices
    used_indices = set()

    for y in range(height):
        for x in range(width):
            used_indices.add(pixels[x, y])

    # Separate used and unused palettes
    all_indices = set(range(num_colors))
    unused_indices = all_indices - used_indices

    def rgb_to_binary(r, g, b):
        """Convert RGB values to a single binary string."""
        return f"{r:08b}{g:08b}{b:08b}"  # 8 bits for each channel

    # Output all palette colors
    print("\nAll Palette Colors:")
    for i in range(num_colors):
        r, g, b = palette[i * 3:i * 3 + 3]
        hex_color = f"#{r:02x}{g:02x}{b:02x}"
        binary_string = rgb_to_binary(r, g, b)
        print(f"Index {i}: RGB({r}, {g}, {b}), HEX({hex_color}), BIN({binary_string})")

# Example usage
image_path = "ba6004dd-2610-4443-a5c8-b12a9b655554.gif"
analyze_palette_usage(image_path)

This outputs the following data:

Index 0: RGB(0, 1, 0), HEX(#000100), BIN(000000000000000100000000)
Index 1: RGB(46, 4, 7), HEX(#2e0407), BIN(001011100000010000000111)
Index 2: RGB(83, 6, 12), HEX(#53060c), BIN(010100110000011000001100)
Index 3: RGB(47, 21, 22), HEX(#2f1516), BIN(001011110001010100010110)
Index 4: RGB(109, 10, 20), HEX(#6d0a14), BIN(011011010000101000010100)
Index 5: RGB(83, 18, 23), HEX(#531217), BIN(010100110001001000010111)
Index 6: RGB(57, 28, 35), HEX(#391c23), BIN(001110010001110000100011)
Index 7: RGB(59, 34, 28), HEX(#3b221c), BIN(001110110010001000011100)
Index 8: RGB(60, 35, 37), HEX(#3c2325), BIN(001111000010001100100101)
Index 9: RGB(80, 29, 37), HEX(#501d25), BIN(010100000001110100100101)
Index 10: RGB(132, 14, 22), HEX(#840e16), BIN(100001000000111000010110)
Index 11: RGB(116, 19, 28), HEX(#74131c), BIN(011101000001001100011100)
Index 12: RGB(132, 18, 28), HEX(#84121c), BIN(100001000001001000011100)
Index 13: RGB(76, 38, 27), HEX(#4c261b), BIN(010011000010011000011011)
Index 14: RGB(121, 26, 35), HEX(#791a23), BIN(011110010001101000100011)
Index 15: RGB(82, 40, 47), HEX(#52282f), BIN(010100100010100000101111)
Index 16: RGB(144, 25, 37), HEX(#901925), BIN(100100000001100100100101)
Index 17: RGB(130, 29, 39), HEX(#821d27), BIN(100000100001110100100111)
Index 18: RGB(133, 30, 36), HEX(#851e24), BIN(100001010001111000100100)
Index 19: RGB(96, 43, 49), HEX(#602b31), BIN(011000000010101100110001)
Index 20: RGB(121, 36, 45), HEX(#79242d), BIN(011110010010010000101101)
Index 21: RGB(98, 46, 57), HEX(#622e39), BIN(011000100010111000111001)
Index 22: RGB(101, 47, 57), HEX(#652f39), BIN(011001010010111100111001)
Index 23: RGB(173, 26, 42), HEX(#ad1a2a), BIN(101011010001101000101010)
Index 24: RGB(90, 53, 55), HEX(#5a3537), BIN(010110100011010100110111)
Index 25: RGB(143, 37, 47), HEX(#8f252f), BIN(100011110010010100101111)
Index 26: RGB(136, 44, 53), HEX(#882c35), BIN(100010000010110000110101)
Index 27: RGB(113, 50, 56), HEX(#713238), BIN(011100010011001000111000)
Index 28: RGB(90, 59, 69), HEX(#5a3b45), BIN(010110100011101101000101)
Index 29: RGB(196, 28, 44), HEX(#c41c2c), BIN(110001000001110000101100)
Index 30: RGB(99, 58, 68), HEX(#633a44), BIN(011000110011101001000100)
Index 31: RGB(172, 36, 50), HEX(#ac2432), BIN(101011000010010000110010)
Index 32: RGB(107, 56, 62), HEX(#6b383e), BIN(011010110011100000111110)
Index 33: RGB(99, 61, 70), HEX(#633d46), BIN(011000110011110101000110)
Index 34: RGB(116, 57, 68), HEX(#743944), BIN(011101000011100101000100)
Index 35: RGB(114, 59, 66), HEX(#723b42), BIN(011100100011101101000010)
Index 36: RGB(142, 50, 59), HEX(#8e323b), BIN(100011100011001000111011)
Index 37: RGB(93, 67, 72), HEX(#5d4348), BIN(010111010100001101001000)
Index 38: RGB(121, 65, 60), HEX(#79413c), BIN(011110010100000100111100)
Index 39: RGB(207, 38, 54), HEX(#cf2636), BIN(110011110010011000110110)
Index 40: RGB(170, 51, 60), HEX(#aa333c), BIN(101010100011001100111100)
Index 41: RGB(142, 58, 66), HEX(#8e3a42), BIN(100011100011101001000010)
Index 42: RGB(139, 60, 70), HEX(#8b3c46), BIN(100010110011110001000110)
Index 43: RGB(109, 70, 80), HEX(#6d4650), BIN(011011010100011001010000)
Index 44: RGB(110, 71, 78), HEX(#6e474e), BIN(011011100100011101001110)
Index 45: RGB(119, 71, 50), HEX(#774732), BIN(011101110100011100110010)
Index 46: RGB(106, 74, 50), HEX(#6a4a32), BIN(011010100100101000110010)
Index 47: RGB(112, 70, 79), HEX(#70464f), BIN(011100000100011001001111)
Index 48: RGB(113, 71, 82), HEX(#714752), BIN(011100010100011101010010)
Index 49: RGB(198, 49, 63), HEX(#c6313f), BIN(110001100011000100111111)
Index 50: RGB(173, 57, 69), HEX(#ad3945), BIN(101011010011100101000101)
Index 51: RGB(226, 40, 61), HEX(#e2283d), BIN(111000100010100000111101)
Index 52: RGB(216, 47, 64), HEX(#d82f40), BIN(110110000010111101000000)
Index 53: RGB(145, 70, 79), HEX(#91464f), BIN(100100010100011001001111)
Index 54: RGB(140, 75, 52), HEX(#8c4b34), BIN(100011000100101100110100)
Index 55: RGB(233, 46, 65), HEX(#e92e41), BIN(111010010010111001000001)
Index 56: RGB(208, 54, 70), HEX(#d03646), BIN(110100000011011001000110)
Index 57: RGB(120, 82, 90), HEX(#78525a), BIN(011110000101001001011010)
Index 58: RGB(114, 84, 92), HEX(#72545c), BIN(011100100101010001011100)
Index 59: RGB(121, 85, 92), HEX(#79555c), BIN(011110010101010101011100)
Index 60: RGB(116, 87, 84), HEX(#745754), BIN(011101000101011101010100)
Index 61: RGB(174, 70, 81), HEX(#ae4651), BIN(101011100100011001010001)
Index 62: RGB(243, 53, 71), HEX(#f33547), BIN(111100110011010101000111)
Index 63: RGB(142, 84, 54), HEX(#8e5436), BIN(100011100101010000110110)
Index 64: RGB(116, 90, 100), HEX(#745a64), BIN(011101000101101001100100)
Index 65: RGB(146, 84, 88), HEX(#925458), BIN(100100100101010001011000)
Index 66: RGB(207, 70, 83), HEX(#cf4653), BIN(110011110100011001010011)
Index 67: RGB(123, 96, 87), HEX(#7b6057), BIN(011110110110000001010111)
Index 68: RGB(147, 91, 98), HEX(#935b62), BIN(100100110101101101100010)
Index 69: RGB(174, 82, 90), HEX(#ae525a), BIN(101011100101001001011010)
Index 70: RGB(161, 92, 59), HEX(#a15c3b), BIN(101000010101110000111011)
Index 71: RGB(120, 102, 109), HEX(#78666d), BIN(011110000110011001101101)
Index 72: RGB(171, 90, 99), HEX(#ab5a63), BIN(101010110101101001100011)
Index 73: RGB(138, 103, 82), HEX(#8a6752), BIN(100010100110011101010010)
Index 74: RGB(156, 97, 94), HEX(#9c615e), BIN(100111000110000101011110)
Index 75: RGB(203, 83, 93), HEX(#cb535d), BIN(110010110101001101011101)
Index 76: RGB(174, 90, 98), HEX(#ae5a62), BIN(101011100101101001100010)
Index 77: RGB(143, 102, 110), HEX(#8f666e), BIN(100011110110011001101110)
Index 78: RGB(145, 103, 109), HEX(#91676d), BIN(100100010110011101101101)
Index 79: RGB(146, 105, 111), HEX(#92696f), BIN(100100100110100101101111)
Index 80: RGB(152, 104, 106), HEX(#98686a), BIN(100110000110100001101010)
Index 81: RGB(149, 104, 112), HEX(#956870), BIN(100101010110100001110000)
Index 82: RGB(206, 89, 99), HEX(#ce5963), BIN(110011100101100101100011)
Index 83: RGB(159, 105, 103), HEX(#9f6967), BIN(100111110110100101100111)
Index 84: RGB(171, 104, 60), HEX(#ab683c), BIN(101010110110100000111100)
Index 85: RGB(169, 105, 72), HEX(#a96948), BIN(101010010110100101001000)
Index 86: RGB(172, 100, 108), HEX(#ac646c), BIN(101011000110010001101100)
Index 87: RGB(160, 107, 107), HEX(#a06b6b), BIN(101000000110101101101011)
Index 88: RGB(171, 105, 111), HEX(#ab696f), BIN(101010110110100101101111)
Index 89: RGB(177, 103, 110), HEX(#b1676e), BIN(101100010110011101101110)
Index 90: RGB(150, 114, 89), HEX(#967259), BIN(100101100111001001011001)
Index 91: RGB(149, 117, 112), HEX(#957570), BIN(100101010111010101110000)
Index 92: RGB(193, 107, 114), HEX(#c16b72), BIN(110000010110101101110010)
Index 93: RGB(185, 115, 60), HEX(#b9733c), BIN(101110010111001100111100)
Index 94: RGB(145, 122, 132), HEX(#917a84), BIN(100100010111101010000100)
Index 95: RGB(173, 118, 84), HEX(#ad7654), BIN(101011010111011001010100)
Index 96: RGB(173, 119, 115), HEX(#ad7773), BIN(101011010111011101110011)
Index 97: RGB(164, 122, 118), HEX(#a47a76), BIN(101001000111101001110110)
Index 98: RGB(158, 124, 130), HEX(#9e7c82), BIN(100111100111110010000010)
Index 99: RGB(169, 123, 111), HEX(#a97b6f), BIN(101010010111101101101111)
Index 100: RGB(197, 114, 122), HEX(#c5727a), BIN(110001010111001001111010)
Index 101: RGB(166, 124, 129), HEX(#a67c81), BIN(101001100111110010000001)
Index 102: RGB(126, 134, 145), HEX(#7e8691), BIN(011111101000011010010001)
Index 103: RGB(146, 132, 118), HEX(#928476), BIN(100100101000010001110110)
Index 104: RGB(194, 122, 76), HEX(#c27a4c), BIN(110000100111101001001100)
Index 105: RGB(171, 125, 130), HEX(#ab7d82), BIN(101010110111110110000010)
Index 106: RGB(177, 125, 131), HEX(#b17d83), BIN(101100010111110110000011)
Index 107: RGB(176, 125, 133), HEX(#b07d85), BIN(101100000111110110000101)
Index 108: RGB(177, 130, 92), HEX(#b1825c), BIN(101100011000001001011100)
Index 109: RGB(145, 137, 144), HEX(#918990), BIN(100100011000100110010000)
Index 110: RGB(177, 133, 106), HEX(#b1856a), BIN(101100011000010101101010)
Index 111: RGB(201, 122, 130), HEX(#c97a82), BIN(110010010111101010000010)
Index 112: RGB(172, 134, 118), HEX(#ac8676), BIN(101011001000011001110110)
Index 113: RGB(177, 135, 113), HEX(#b18771), BIN(101100011000011101110001)
Index 114: RGB(170, 135, 136), HEX(#aa8788), BIN(101010101000011110001000)
Index 115: RGB(172, 136, 118), HEX(#ac8876), BIN(101011001000100001110110)
Index 116: RGB(178, 136, 110), HEX(#b2886e), BIN(101100101000100001101110)
Index 117: RGB(177, 136, 118), HEX(#b18876), BIN(101100011000100001110110)
Index 118: RGB(173, 136, 135), HEX(#ad8887), BIN(101011011000100010000111)
Index 119: RGB(178, 136, 115), HEX(#b28873), BIN(101100101000100001110011)
Index 120: RGB(204, 132, 79), HEX(#cc844f), BIN(110011001000010001001111)
Index 121: RGB(172, 137, 136), HEX(#ac8988), BIN(101011001000100110001000)
Index 122: RGB(178, 134, 140), HEX(#b2868c), BIN(101100101000011010001100)
Index 123: RGB(170, 139, 141), HEX(#aa8b8d), BIN(101010101000101110001101)
Index 124: RGB(174, 139, 141), HEX(#ae8b8d), BIN(101011101000101110001101)
Index 125: RGB(145, 148, 154), HEX(#91949a), BIN(100100011001010010011010)
Index 126: RGB(179, 145, 116), HEX(#b39174), BIN(101100111001000101110100)
Index 127: RGB(194, 138, 145), HEX(#c28a91), BIN(110000101000101010010001)
Index 128: RGB(193, 143, 107), HEX(#c18f6b), BIN(110000011000111101101011)
Index 129: RGB(188, 145, 116), HEX(#bc9174), BIN(101111001001000101110100)
Index 130: RGB(150, 155, 163), HEX(#969ba3), BIN(100101101001101110100011)
Index 131: RGB(187, 146, 123), HEX(#bb927b), BIN(101110111001001001111011)
Index 132: RGB(192, 144, 115), HEX(#c09073), BIN(110000001001000001110011)
Index 133: RGB(174, 149, 151), HEX(#ae9597), BIN(101011101001010110010111)
Index 134: RGB(189, 148, 106), HEX(#bd946a), BIN(101111011001010001101010)
Index 135: RGB(179, 149, 144), HEX(#b39590), BIN(101100111001010110010000)
Index 136: RGB(179, 148, 151), HEX(#b39497), BIN(101100111001010010010111)
Index 137: RGB(181, 148, 146), HEX(#b59492), BIN(101101011001010010010010)
Index 138: RGB(194, 149, 116), HEX(#c29574), BIN(110000101001010101110100)
Index 139: RGB(180, 150, 149), HEX(#b49695), BIN(101101001001011010010101)
Index 140: RGB(169, 154, 164), HEX(#a99aa4), BIN(101010011001101010100100)
Index 141: RGB(183, 153, 149), HEX(#b79995), BIN(101101111001100110010101)
Index 142: RGB(199, 152, 111), HEX(#c7986f), BIN(110001111001100001101111)
Index 143: RGB(181, 154, 161), HEX(#b59aa1), BIN(101101011001101010100001)
Index 144: RGB(195, 150, 153), HEX(#c39699), BIN(110000111001011010011001)
Index 145: RGB(194, 153, 156), HEX(#c2999c), BIN(110000101001100110011100)
Index 146: RGB(173, 162, 164), HEX(#ada2a4), BIN(101011011010001010100100)
Index 147: RGB(153, 169, 174), HEX(#99a9ae), BIN(100110011010100110101110)
Index 148: RGB(197, 156, 161), HEX(#c59ca1), BIN(110001011001110010100001)
Index 149: RGB(170, 166, 156), HEX(#aaa69c), BIN(101010101010011010011100)
Index 150: RGB(208, 159, 118), HEX(#d09f76), BIN(110100001001111101110110)
Index 151: RGB(168, 169, 175), HEX(#a8a9af), BIN(101010001010100110101111)
Index 152: RGB(179, 167, 168), HEX(#b3a7a8), BIN(101100111010011110101000)
Index 153: RGB(172, 168, 168), HEX(#aca8a8), BIN(101011001010100010101000)
Index 154: RGB(186, 164, 166), HEX(#baa4a6), BIN(101110101010010010100110)
Index 155: RGB(172, 168, 175), HEX(#aca8af), BIN(101011001010100010101111)
Index 156: RGB(211, 164, 119), HEX(#d3a477), BIN(110100111010010001110111)
Index 157: RGB(198, 164, 170), HEX(#c6a4aa), BIN(110001101010010010101010)
Index 158: RGB(201, 164, 166), HEX(#c9a4a6), BIN(110010011010010010100110)
Index 159: RGB(157, 176, 184), HEX(#9db0b8), BIN(100111011011000010111000)
Index 160: RGB(200, 166, 171), HEX(#c8a6ab), BIN(110010001010011010101011)
Index 161: RGB(200, 169, 170), HEX(#c8a9aa), BIN(110010001010100110101010)
Index 162: RGB(205, 167, 171), HEX(#cda7ab), BIN(110011011010011110101011)
Index 163: RGB(214, 167, 158), HEX(#d6a79e), BIN(110101101010011110011110)
Index 164: RGB(208, 169, 153), HEX(#d0a999), BIN(110100001010100110011001)
Index 165: RGB(200, 171, 172), HEX(#c8abac), BIN(110010001010101110101100)
Index 166: RGB(213, 169, 144), HEX(#d5a990), BIN(110101011010100110010000)
Index 167: RGB(167, 181, 187), HEX(#a7b5bb), BIN(101001111011010110111011)
Index 168: RGB(215, 171, 137), HEX(#d7ab89), BIN(110101111010101110001001)
Index 169: RGB(224, 172, 125), HEX(#e0ac7d), BIN(111000001010110001111101)
Index 170: RGB(219, 174, 147), HEX(#dbae93), BIN(110110111010111010010011)
Index 171: RGB(221, 176, 124), HEX(#ddb07c), BIN(110111011011000001111100)
Index 172: RGB(198, 178, 182), HEX(#c6b2b6), BIN(110001101011001010110110)
Index 173: RGB(171, 186, 195), HEX(#abbac3), BIN(101010111011101011000011)
Index 174: RGB(226, 176, 126), HEX(#e2b07e), BIN(111000101011000001111110)
Index 175: RGB(209, 178, 181), HEX(#d1b2b5), BIN(110100011011001010110101)
Index 176: RGB(178, 186, 195), HEX(#b2bac3), BIN(101100101011101011000011)
Index 177: RGB(217, 181, 139), HEX(#d9b58b), BIN(110110011011010110001011)
Index 178: RGB(213, 179, 198), HEX(#d5b3c6), BIN(110101011011001111000110)
Index 179: RGB(210, 180, 182), HEX(#d2b4b6), BIN(110100101011010010110110)
Index 180: RGB(186, 188, 189), HEX(#babcbd), BIN(101110101011110010111101)
Index 181: RGB(185, 188, 192), HEX(#b9bcc0), BIN(101110011011110011000000)
Index 182: RGB(188, 189, 197), HEX(#bcbdc5), BIN(101111001011110111000101)
Index 183: RGB(209, 183, 185), HEX(#d1b7b9), BIN(110100011011011110111001)
Index 184: RGB(195, 188, 196), HEX(#c3bcc4), BIN(110000111011110011000100)
Index 185: RGB(212, 184, 201), HEX(#d4b8c9), BIN(110101001011100011001001)
Index 186: RGB(189, 193, 187), HEX(#bdc1bb), BIN(101111011100000110111011)
Index 187: RGB(233, 185, 132), HEX(#e9b984), BIN(111010011011100110000100)
Index 188: RGB(209, 189, 193), HEX(#d1bdc1), BIN(110100011011110111000001)
Index 189: RGB(185, 197, 201), HEX(#b9c5c9), BIN(101110011100010111001001)
Index 190: RGB(213, 188, 192), HEX(#d5bcc0), BIN(110101011011110011000000)
Index 191: RGB(178, 200, 208), HEX(#b2c8d0), BIN(101100101100100011010000)
Index 192: RGB(222, 192, 158), HEX(#dec09e), BIN(110111101100000010011110)
Index 193: RGB(186, 200, 208), HEX(#bac8d0), BIN(101110101100100011010000)
Index 194: RGB(188, 203, 208), HEX(#bccbd0), BIN(101111001100101111010000)
Index 195: RGB(189, 202, 204), HEX(#bdcacc), BIN(101111011100101011001100)
Index 196: RGB(220, 196, 172), HEX(#dcc4ac), BIN(110111001100010010101100)
Index 197: RGB(215, 197, 200), HEX(#d7c5c8), BIN(110101111100010111001000)
Index 198: RGB(196, 203, 208), HEX(#c4cbd0), BIN(110001001100101111010000)
Index 199: RGB(238, 192, 165), HEX(#eec0a5), BIN(111011101100000010100101)
Index 200: RGB(206, 202, 203), HEX(#cecacb), BIN(110011101100101011001011)
Index 201: RGB(246, 198, 141), HEX(#f6c68d), BIN(111101101100011010001101)
Index 202: RGB(188, 210, 219), HEX(#bcd2db), BIN(101111001101001011011011)
Index 203: RGB(196, 212, 219), HEX(#c4d4db), BIN(110001001101010011011011)
Index 204: RGB(196, 212, 220), HEX(#c4d4dc), BIN(110001001101010011011100)
Index 205: RGB(191, 215, 225), HEX(#bfd7e1), BIN(101111111101011111100001)
Index 206: RGB(194, 213, 220), HEX(#c2d5dc), BIN(110000101101010111011100)
Index 207: RGB(251, 200, 162), HEX(#fbc8a2), BIN(111110111100100010100010)
Index 208: RGB(241, 202, 204), HEX(#f1cacc), BIN(111100011100101011001100)
Index 209: RGB(203, 215, 221), HEX(#cbd7dd), BIN(110010111101011111011101)
Index 210: RGB(199, 218, 227), HEX(#c7dae3), BIN(110001111101101011100011)
Index 211: RGB(211, 216, 216), HEX(#d3d8d8), BIN(110100111101100011011000)
Index 212: RGB(208, 219, 224), HEX(#d0dbe0), BIN(110100001101101111100000)
Index 213: RGB(243, 214, 162), HEX(#f3d6a2), BIN(111100111101011010100010)
Index 214: RGB(252, 213, 150), HEX(#fcd596), BIN(111111001101010110010110)
Index 215: RGB(214, 218, 225), HEX(#d6dae1), BIN(110101101101101011100001)
Index 216: RGB(218, 218, 221), HEX(#dadadd), BIN(110110101101101011011101)
Index 217: RGB(247, 214, 166), HEX(#f7d6a6), BIN(111101111101011010100110)
Index 218: RGB(220, 218, 224), HEX(#dcdae0), BIN(110111001101101011100000)
Index 219: RGB(240, 217, 216), HEX(#f0d9d8), BIN(111100001101100111011000)
Index 220: RGB(222, 223, 227), HEX(#dedfe3), BIN(110111101101111111100011)
Index 221: RGB(209, 231, 239), HEX(#d1e7ef), BIN(110100011110011111101111)
Index 222: RGB(221, 228, 233), HEX(#dde4e9), BIN(110111011110010011101001)
Index 223: RGB(253, 230, 173), HEX(#fde6ad), BIN(111111011110011010101101)
Index 224: RGB(224, 235, 243), HEX(#e0ebf3), BIN(111000001110101111110011)
Index 225: RGB(242, 231, 232), HEX(#f2e7e8), BIN(111100101110011111101000)
Index 226: RGB(221, 243, 251), HEX(#ddf3fb), BIN(110111011111001111111011)
Index 227: RGB(246, 236, 241), HEX(#f6ecf1), BIN(111101101110110011110001)
Index 228: RGB(250, 236, 240), HEX(#faecf0), BIN(111110101110110011110000)
Index 229: RGB(255, 245, 184), HEX(#fff5b8), BIN(111111111111010110111000)
Index 230: RGB(254, 255, 208), HEX(#feffd0), BIN(111111101111111111010000)
Index 231: RGB(255, 253, 252), HEX(#fffdfc), BIN(111111111111110111111100)
Index 232: RGB(0, 0, 1), HEX(#000001), BIN(000000000000000000000001)
Index 233: RGB(1, 0, 1), HEX(#010001), BIN(000000010000000000000001)
Index 234: RGB(1, 0, 0), HEX(#010000), BIN(000000010000000000000000)
Index 235: RGB(0, 1, 0), HEX(#000100), BIN(000000000000000100000000)
Index 236: RGB(1, 0, 0), HEX(#010000), BIN(000000010000000000000000)
Index 237: RGB(1, 0, 1), HEX(#010001), BIN(000000010000000000000001)
Index 238: RGB(0, 0, 1), HEX(#000001), BIN(000000000000000000000001)
Index 239: RGB(1, 0, 0), HEX(#010000), BIN(000000010000000000000000)
Index 240: RGB(1, 0, 1), HEX(#010001), BIN(000000010000000000000001)
Index 241: RGB(0, 0, 0), HEX(#000000), BIN(000000000000000000000000)
Index 242: RGB(0, 1, 1), HEX(#000101), BIN(000000000000000100000001)
Index 243: RGB(0, 0, 1), HEX(#000001), BIN(000000000000000000000001)
Index 244: RGB(0, 0, 1), HEX(#000001), BIN(000000000000000000000001)
Index 245: RGB(0, 0, 1), HEX(#000001), BIN(000000000000000000000001)
Index 246: RGB(1, 0, 0), HEX(#010000), BIN(000000010000000000000000)
Index 247: RGB(0, 1, 1), HEX(#000101), BIN(000000000000000100000001)
Index 248: RGB(0, 1, 0), HEX(#000100), BIN(000000000000000100000000)
Index 249: RGB(0, 1, 1), HEX(#000101), BIN(000000000000000100000001)
Index 250: RGB(1, 0, 0), HEX(#010000), BIN(000000010000000000000000)
Index 251: RGB(0, 0, 0), HEX(#000000), BIN(000000000000000000000000)
Index 252: RGB(1, 0, 0), HEX(#010000), BIN(000000010000000000000000)
Index 253: RGB(0, 0, 0), HEX(#000000), BIN(000000000000000000000000)
Index 254: RGB(0, 0, 0), HEX(#000000), BIN(000000000000000000000000)
Index 255: RGB(0, 0, 0), HEX(#000000), BIN(000000000000000000000000)

By observing the partially drawn QR code, we deduce it is a 29x29 QR code. A 29x29 QR code has 841 pixels. In this case, the provided image already has the position markers drawn. Each position market is 7x7 = 49 pixels and there are 3 of them in total. Thus, 49 + 49 + 49 = 147 pixels are already accounted for. Therefore, we assume we need 841 - 147 = 694 pixels to draw the rest of the QR code. As each pixel is likely a 1 or a 0, this means we need 694 pixels worth of data.

The color table reveals some interesting RGB entries. We suspect that data is hidden inside each entry. We write a script that will take the LSB of each RGB channel and treat it as 3 bits of data. As there are 256 entries, this will give us 256*3 = 768 bits of data. This is more than we need but not exactly the amount of bits we require.

# Hackvent 2024 - Day 4
# Mo Beigi
#
# Get LSB from each channel in GIF colour palette.

from PIL import Image

def extract_lsb_from_gif_palette(file_path):
    """
    Extract the least significant bit (LSB) from each RGB channel in the global palette of a GIF.

    Parameters:
    - file_path: Path to the GIF file.

    Returns:
    - str: A binary string representing the LSBs of all RGB channels.
    """
    # Open the GIF file
    img = Image.open(file_path)

    # Ensure the image is palette-based
    if img.mode != "P":
        raise ValueError(f"{file_path} is not palette-based.")

    # Get the global palette
    palette = img.getpalette()

    # Extract LSBs from RGB channels
    lsb_sequence = "".join(
        f"{palette[i] & 1}{palette[i + 1] & 1}{palette[i + 2] & 1}"
        for i in range(0, len(palette), 3)
    )

    return lsb_sequence


# Example usage
file_path = "ba6004dd-2610-4443-a5c8-b12a9b655554.gif"
lsb_binary_string = extract_lsb_from_gif_palette(file_path)
print("Extracted LSB Binary String:")
print(lsb_binary_string)

Running the script provides the following output:

Extracted LSB Binary String:
010001100110100101101100011011000010000001101001011011100011101001111100011111001100011000100000100110010010001110110100010000100100010110000001110011111001010101010101000000000110010001111000000000101101110000101001101010010111000100111011000100011111100110000011111110001110110110100100111000000111100001001000000110111011100110110100000111010000000100101001001010000011011100110001111010011101001011100110101100010001100111101101101010100110101000010011110000000001101000100100001010111010011010110111111001101100000101000101001111110000001100011111100001111110111111100000000000010100000110010001001001001001000111010100100111101100010100010001001100000010011111101101011010111001000110010110001101100010100101001100101000011001001001100011010011100000100000000000

At this point, we want to create a 29x29 QR code as a binary string. We will prefill the position markets (because they are already present in the image), and use our binary string to prefill the rest. Our script looks like this:

# Hackvent 2024 - Day 4
# Mo Beigi
#
# Create binary string representing QR code.

def generate_qr_binary_direct(binary_data):
    """
    Generate a binary string for a 29x29 QR code with top-left, top-right, and bottom-left alignment boxes,
    and directly use binary data for the remaining positions.

    Parameters:
    - binary_data (str): Binary string containing the data to fill the QR code.

    Returns:
    - str: Full binary string of the QR code.
    """
    # QR code dimensions
    size = 29

    # Large alignment box template (7x7, 1 for black, 0 for white)
    alignment_box = [
        "1111111",  # Top row of alignment box
        "1000001",  # Second row
        "1011101",  # Third row
        "1011101",  # Fourth row
        "1011101",  # Fifth row
        "1000001",  # Sixth row
        "1111111",  # Bottom row
    ]

    # Convert alignment box rows to individual bits
    alignment_bits = [list(row) for row in alignment_box]

    # Create an empty 29x29 grid for the QR code
    qr_grid = [["0" for _ in range(size)] for _ in range(size)]

    # Insert alignment boxes into the grid
    def insert_alignment(x, y):
        """Insert a large alignment box into the QR grid."""
        for i in range(7):  # 7x7 alignment box
            for j in range(7):
                qr_grid[y + i][x + j] = alignment_bits[i][j]

    # Top-left alignment box
    insert_alignment(0, 0)

    # Top-right alignment box
    insert_alignment(size - 7, 0)

    # Bottom-left alignment box
    insert_alignment(0, size - 7)

    # Fill the remaining grid directly with binary data
    data_index = 0
    for y in range(size):
        for x in range(size):
            # Skip positions that are part of alignment boxes
            if (0 <= x < 7 and 0 <= y < 7) or \
               (size - 7 <= x < size and 0 <= y < 7) or \
               (0 <= x < 7 and size - 7 <= y < size):
                continue

            # Fill the cell with the next bit from binary data
            if data_index < len(binary_data):
                qr_grid[y][x] = binary_data[data_index]
                data_index += 1

    # Check if any binary data remains unused
    if data_index < len(binary_data):
        print(f"Unused binary data: {binary_data[data_index:]}")

    # Convert the grid into a single binary string
    qr_binary_string = "".join("".join(row) for row in qr_grid)
    return qr_binary_string


# Example binary data
binary_data = ("010001100110100101101100011011000010000001101001011011100011101001111100011111001100011000100000100110010010001110110100010000100100010110000001110011111001010101010101000000000110010001111000000000101101110000101001101010010111000100111011000100011111100110000011111110001110110110100100111000000111100001001000000110111011100110110100000111010000000100101001001010000011011100110001111010011101001011100110101100010001100111101101101010100110101000010011110000000001101000100100001010111010011010110111111001101100000101000101001111110000001100011111100001111110111111100000000000010100000110010001001001001001000111010100100111101100010100010001001100000010011111101101011010111001000110010110001101100010100101001100101000011001001001100011010011100000100000000000"
)

# Generate QR binary string
qr_binary_string = generate_qr_binary_direct(binary_data)

# Output the result
print("Generated QR Binary String with Alignment Boxes:")
print(qr_binary_string)

Running this script gives us:

Unused binary data: 10001101100010100101001100101000011001001001100011010011100000100000000000
Generated QR Binary String with Alignment Boxes:
1111111010001100110100111111110000011011011000110111000001101110100001000000110110111011011101001011011100011101110110111011010011111000111011101100000111100110001100010000011111111100000100110010111111101000111011010001000010010001011000000111001111100101010101010100000000011001000111100000000010110111000010100110101001011100010011101100010001111110011000001111111000111011011010010011100000011110000100100000011011101110011011010000011101000000010010100100101000001101110011000111101001110100101110011010110001000110011110110110101010011010100001001111000000000110100010010000101011101001101011011111100110110000010100010100111111000011111110011000111111000011111100000110111111100000000000011011101010000011001000100100110111010010010001110101001001101110111101100010100010001001000001110000001001111110110111111110110101110010001100101

We use this handy website to convert a binary string to a QR code: https://bahamas10.github.io/binary-to-qrcode/

This is the QR code we currently have:

Hackvent 2024 - Day 4 - First QR

It looks somewhat correct but isn't quite right. If you observe, you will notice that the timing patterns and alignment markers are present but they seem to be shifted to the wrong place. Therefore, we now use some trial and error to drop some bits from the start of our bit string until the timing and alignment pattern are in the correct position.

After some trial and error, we test our script with this binary string:

binary_data = ("01111100011111001100011000100000100110010010001110110100010000100100010110000001110011111001010101010101000000000110010001111000000000101101110000101001101010010111000100111011000100011111100110000011111110001110110110100100111000000111100001001000000110111011100110110100000111010000000100101001001010000011011100110001111010011101001011100110101100010001100111101101101010100110101000010011110000000001101000100100001010111010011010110111111001101100000101000101001111110000001100011111100001111110111111100000000000010100000110010001001001001001000111010100100111101100010100010001001100000010011111101101011010111001000110010110001101100010100101001100101000011001001001100011010011100000100000000000"
)

Which produces this output:

Unused binary data: 0000000000
Generated QR Binary String with Alignment Boxes:
1111111011111000111110111111110000010110001100010001000001101110100100110010010010111011011101011101101000100101110110111010010010001011001011101100000100001110011111010000011111111010101010101010111111100000000110010001111000000000101101110000101001101010010111000100111011000100011111100110000011111110001110110110100100111000000111100001001000000110111011100110110100000111010000000100101001001010000011011100110001111010011101001011100110101100010001100111101101101010100110101000010011110000000001101000100100001010111010011010110111111001101100000101000101001111110000001100011111100001111110111111100000000000010100000110010001001011111110100100100011101010010100000101111011000101000100011011101001100000010011111101110111010101101011100100011001101110101100011011000101001011000001001100101000011001001011111110110001101001110000010

We visualize this QR code, which is scannable and contains the daily flag!

Hackvent 2024 - Day 4 - Solution QR

Flag:

HV24{QR_$tuff_h1dd3n_in_th3_c0lor_t@b1e}

Leave a comment

(required)(will not be published)(required)

Comments

There are no comments yet. Be the first to add one!