snakeCTF logo

Colourful Gabibbos

CRYPTO

1 file available


Description

I managed to get my hands on two secret images from the Gabibbos, but they're encrypted. Can you help me decrypt them?

The first one was straightforward, it turned out to be a photo of a well-known member, but the second one is a bit more difficult.

Solution

The encryption is done by creating a map for each colour in the image and then encrypting the image by applying the map to each pixel. Using the provided plaintext-ciphertext pair, the map used to encrypt the images can be recovered.

Since the map is generated using the random python module, the internal state of the random generator is recoverable and so is the map used to encrypt the flag and recover the original image.

#!/usr/bin/env python3

import os
import sys

from PIL import Image
import randcrack


def reconstruct_map():
    og = Image.open(os.path.join(dir, 'og.png'))
    en = Image.open(os.path.join(dir, 'og_enc.png'))
    randoms = []
    pixels = list(og.getdata())
    k = {}
    for i, p in enumerate(pixels):
        if p in k:
            continue
        k[p] = en.getpixel((i % og.width, i // og.width))

    pixels = list(k.items())
    pixels.sort(key=lambda x: x[0])

    for _, t in pixels:
        r = t[0] | t[1] << 8 | t[2] << 16 | t[3] << 24
        randoms.append(r)
    return randoms


def find_key(r):
    k = {}
    p = 0
    while p < 256:
        x = r.predict_getrandbits(8)
        while x in k:
            x = r.predict_getrandbits(8)
        k[x] = p
        p += 1
    return k


def decrypt_image(image, r):
    k = find_key(r)
    n = Image.new(image.mode, image.size, 0)
    for x in range(image.size[0]):
        for y in range(image.size[1]):
            n.putpixel((x, y), k[image.getpixel((x, y))])

    return n


def check_image(im):
    for i in range(256):
        if im.getpixel((i, 49)) != i:
            return False
    return True


def main():
    randoms = reconstruct_map()

    r = randcrack.RandCrack()
    for n in randoms[-624:]:
        r.submit(n)

    n = decrypt_image(Image.open(os.path.join(dir, 'flag_enc.png')), r)
    n.save(os.path.join(dir, 'flag.png'))


if __name__ == '__main__':
    dir = sys.argv[1]
    main()