blue-office

problem

The Blue Office’s ingenious cipher, meticulously crafted for the prestigious CCTF, became an impenetrable enigma that left even the most seasoned cryptanalysts baffled.

#!/usr/bin/enc python3

import binascii
from secret import seed, flag

def gen_seed(s):
	i, j, k = 0, len(s), 0
	while i < j:
		k = k + ord(s[i])
		i += 1
	i = 0
	while i < j:
		if (i % 2) != 0:
			k = k - (ord(s[i]) * (j - i + 1))            
		else:
			k = k + (ord(s[i]) * (j - i + 1))
	
		k = k % 2147483647
		i += 1

	k = (k * j) % 2147483647
	return k

def reseed(s):
	return s * 214013 + 2531011

def encrypt(s, msg):
	assert s <= 2**32
	c, d = 0, s
	enc, l = b'', len(msg)
	while c < l:
		d = reseed(d)
		enc += (msg[c] ^ ((d >> 16) & 0xff)).to_bytes(1, 'big')
		c += 1
	return enc

enc = encrypt(seed, flag)
print(f'enc = {binascii.hexlify(enc)}')

output.txt

enc = b'b0cb631639f8a5ab20ff7385926383f89a71bbc4ed2d57142e05f39d434fce'

solution

here, we can see that a secret integer between 0 and 2**32 inclusive is used as the seed, and it is reseeded and then XORed by the original string to get the final string

the key element here is that the only bits that matter are the 1 << 16 to 1 << 23 bits because of the bit mask of 0b0000_0000_1111_1111_0000_0000_0000_0000 or 0x00ff0000.

so, we can just brute force this from 0 to 2**24

#!/usr/bin/enc python3

import binascii
from secret import seed, flag


def gen_seed(s):
    i, j, k = 0, len(s), 0
    while i < j:
        k = k + ord(s[i])
        i += 1
    # k is currently the ascii sum of the string
    i = 0
    while i < j:
        print(j - i + 1)
        if (i % 2) != 0:
            k = k - (ord(s[i]) * (j - i + 1))
        else:
            k = k + (ord(s[i]) * (j - i + 1))

        k = k % 2147483647
        i += 1

    k = (k * j) % 2147483647
    return k


def reseed(s):
    return s * 214013 + 2531011


def encrypt(s, msg):
    assert s <= 2 ** 32
    index, d = 0, s
    enc, length = b'', len(msg)
    #  C, C, T, F, {
    # f3,f9,
    print(d)
    while index < length:
        d = reseed(d)
        enc += (msg[index] ^ ((d >> 16) & 0xff)).to_bytes(1, 'big')
        index += 1
    return enc


def decrypt(s, enc):
    assert s <= 2 ** 32
    index, d = 0, s
    msg, length = b'', len(enc)
    while index < length:
        d = reseed(d)
        msg += (enc[index] ^ ((d >> 16) & 0xff)).to_bytes(1, 'big')
        index += 1
    return msg


def main():
    enc2 = encrypt(seed, flag)
    print(f'enc = {binascii.hexlify(enc2)}')
    ltb = binascii.unhexlify('b0cb631639f8a5ab20ff7385926383f89a71bbc4ed2d57142e05f39d434fce')
    for tryseed in range(2**24):
        if tryseed % 2**20 == 0:
            print(tryseed//(2**24/100))
        oted = decrypt(tryseed, ltb)
        if oted.startswith(b"CCTF"):
            print(oted, tryseed)
            quit()


if __name__ == '__main__':
    main()

this takes some time but does print out the flag CCTF{__B4ck_0r!F1c3__C1pHeR_!!} and seed 10364460