Skip to content

Instantly share code, notes, and snippets.

@ky28059
Created August 31, 2024 22:53
Show Gist options
  • Save ky28059/467d31ff2f536b79f517da4f85229be4 to your computer and use it in GitHub Desktop.
Save ky28059/467d31ff2f536b79f517da4f85229be4 to your computer and use it in GitHub Desktop.

CyberSpace CTF 2024 — SKK?

What have you done to my flag?

We're given an encoded image of the flag

enc

and an encoder script that looks like this:

import numpy as np
import cv2
import random
from datetime import datetime

img = cv2.imread('flag.png')
size_x, size_y = img.shape[:2]

enc_negpos = np.zeros_like(img)

random.seed(datetime.now().timestamp())

for i in range(size_x):
    for j in range(size_y):
        for rgb in range(3):
            negpos = random.random()
            if negpos < 0.5:
                enc_negpos[i, j, rgb] = img[i, j, rgb]
            else:
                enc_negpos[i, j, rgb] = img[i, j, rgb] ^ 255


enc_shuffle = enc_negpos.copy()

for i in range(size_x):
    for j in range(size_y):
        shuffle = random.randint(1, 6)
        if shuffle == 1:
            enc_shuffle[i, j] = enc_negpos[i, j]
        elif shuffle == 2:
            enc_shuffle[i, j] = enc_negpos[i, j][[0, 2, 1]]
        elif shuffle == 3:
            enc_shuffle[i, j] = enc_negpos[i, j][[1, 0, 2]]
        elif shuffle == 4:
            enc_shuffle[i, j] = enc_negpos[i, j][[1, 2, 0]]
        elif shuffle == 5:
            enc_shuffle[i, j] = enc_negpos[i, j][[2, 0, 1]]
        else:
            enc_shuffle[i, j] = enc_negpos[i, j][[2, 1, 0]]


cv2.imwrite('enc.png', enc_shuffle)

At first glance, it looks like the image encoding is done in two steps:

  1. Randomly XOR random channels of each pixel in the image with 255.
  2. Randomly swap channels for each pixel.

With no further information, there doesn't seem to be a mathematical way to perfectly reverse this encoding. However, we can conjecture that most pixels in the original image are white (e.g. (255, 255, 255)). Running these pixels through the series of XORs and swaps should create permutations of channels that are either 0 or 255, and indeed we see the majority of border pixels follow this pattern:

image

Then, we should filter out all of these background pixels and examine only the gray band in the middle. Here's a Python script that does just that:

import cv2
import numpy as np

img = cv2.imread('./enc.png')

res = np.zeros_like(img)

for i in range(len(img)):
    for j in range(len(img[i])):
        if all(30 < x < 225 for x in img[i, j]):
            res[i, j] = img[i, j] - 128
        else:
            res[i, j] = [255, 255, 255]

cv2.namedWindow('res', cv2.WINDOW_NORMAL)
cv2.imshow('res', res)
cv2.waitKey()

cv2.imshow('res', cv2.cvtColor(res, cv2.COLOR_BGR2GRAY))
cv2.waitKey()

res

CSCTF{why_SKK_image_encryption_sooo.weak?}

(or a simpler solution using inRange:

import cv2

img = cv2.imread('./enc.png')
mask = cv2.inRange(img, (20, 20, 20), (235, 235, 235))

cv2.namedWindow('res', cv2.WINDOW_NORMAL)
cv2.imshow('res', mask)
cv2.waitKey()

res2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment