We found a brand new type of encryption, can you break the secret code? (Wrap with picoCTF{}) apbopjbobpnjpjnmnnnmnlnbamnpnononpnaaaamnlnkapndnkncamnpapncnbannaapncndnlnpna new_caesar.py
So we are given a script new_caesar.py, let's analyse it.
LOWERCASE_OFFSET =ord("a")ALPHABET = string.ascii_lowercase[:16][...]flag ="redacted"key ="b"assertall([k in ALPHABET for k in key])assertlen(key)==1b16 =b16_encode(flag)enc =""for i, c inenumerate(b16): enc +=shift(c, key[i %len(key)])print(enc)
So first off we know that the ALPHABET is the first 16 letters in the lowercase alphabet. The key is one character long, and is also in ALPHABET. The flag has b16_encode() run on it, and every letter in the result of that is run under shift(). To reverse, it, we have to first undo shift() and then b16_encode().
shift() is a very simple function, although the arithmetic looks a bit strange. t1 is actually the index of c in ALPHABET, as it grabs the character code and then subtracts off that of a. t2 is the same, but for the key. t2 is then added as a shift to t1 and the index in ALPHABET is returned, so it's essentially a Caesar with a shift dictated by k. We can undo this easily, by subtracting instead of adding:
As for b16encode(), it calculates the 8-bit binary representation of the character code and then splits it into two 4-bit values, which are used as indices in ALPHABET. We can undo this too.
defb16_decode(enc): enc = [enc[x:x +2]for x inrange(0, len(enc), 2)] # split into pairs dec =''for pair in enc: bin1 = ALPHABET.index(pair[0]) bin2 = ALPHABET.index(pair[1]) combined =int(bin(bin1)[2:].zfill(4) +bin(bin2)[2:].zfill(4), 2) dec +=chr(combined)return dec
The .zfill(4) ensures that each representation of the two values is 4 bits long, which is very important for parsing it as an 8-bit bniary value at the end!
We then just have to brute force for every possible key in ALPHABET, getting 16 possibilities: