RubikCBC
by raghul-rajasekar
I implemented this really cool Rubiks CBC encryption algorithm and tested it on a document with my flag in it, but my dog ate my hard drive so I couldn’t decrypt the file :(
Luckily I backed up the encrypted file. Can you recover my data?
Files:
Solution
We are provided with the encrypted document and a README.txt
file with the following information:
scramble("F", "OOOOOOOOOYYYWWWGGGBBBYYYWWWGGGBBBYYYWWWGGGBBBRRRRRRRRR") => "OOOOOOYYYYYRWWWOGGBBBYYRWWWOGGBBBYYRWWWOGGBBBGGGRRRRRR"
==============================================================
IV = "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuv"
SCRAMBLE = D R2 F2 D B2 D2 R2 B2 D L2 D' R D B L2 B' L' R' B' F2 R2 D R2 B2 R2 D L2 D2 F2 R2 F' D' B2 D' B U B' L R' D'
==============================================================
This shows a sample use of the scramble
function, an IV of length 54 and a series of moves typical for a Rubik’s cube algorithm. From the length of the strings, we can guess that each character corresponds to one of the 54 small squares on a Rubik’s cube. The given sample shows us the effect of turning the front face of one such Rubik’s cube clockwise (refer to https://ruwix.com/the-rubiks-cube/notation/ for a primer on the notation used for Rubik’s cubes). From the sample, I figured out that the mapping from string to Rubik’s cube is:
Now, since we are given an IV and a series of moves, and also since the title of the challenge mentions “CBC”, the steps to decrypt the document are fairly clear:
- Split the encrypted document into blocks of size 54 bytes.
- To decrypt an individual block, perform the reverse of the moves shown in
SCRAMBLE
on each block, assuming it to be a Rubik’s cube. - Do this in CBC mode to get the original document.
The implementation of the scramble
function was the hardest part, which is shown here:
def scramble(move, cube):
rounds = 1
if len(move) > 1:
if move[1] == '\'':
rounds = 3
elif move[1] == '2':
rounds = 2
U = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9]
U1 = [0, 1, 2, 5, 8, 7, 6, 3]
D = [33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]
D1 = [45, 46, 47, 50, 53, 52, 51, 48]
L = [0, 3, 6, 12, 24, 36, 45, 48, 51, 44, 32, 20]
L1 = [9, 10, 11, 23, 35, 34, 33, 21]
R = [53, 50, 47, 38, 26, 14, 8, 5, 2, 18, 30, 42]
R1 = [15, 16, 17, 29, 41, 40, 39, 27]
F = [6, 7, 8, 15, 27, 39, 47, 46, 45, 35, 23, 11]
F1 = [12, 13, 14, 26, 38, 37, 36, 24]
B = [2, 1, 0, 9, 21, 33, 51, 52, 53, 41, 29, 17]
B1 = [18, 19, 20, 32, 44, 43, 42, 30]
if move[0] == 'U':
old = U
old1 = U1
elif move[0] == 'D':
old = D
old1 = D1
elif move[0] == 'L':
old = L
old1 = L1
elif move[0] == 'R':
old = R
old1 = R1
elif move[0] == 'F':
old = F
old1 = F1
elif move[0] == 'B':
old = B
old1 = B1
else:
return
new = old[-rounds*3:] + old[:-rounds*3]
new1 = old1[-rounds*2:] + old1[:-rounds*2]
new = [cube[i] for i in new]
new1 = [cube[i] for i in new1]
cube = list(cube)
for i, c in zip(old, new):
cube[i] = c
for i, c in zip(old1, new1):
cube[i] = c
return ''.join(cube)
On deciphering the document, it turned out to be a PDF file with a QR code in it (which kind of seemed to be the theme of the whole CTF). Scanning it gives the flag.
Flag
rgbCTF{!IP_over_Avian_Carriers_with_QoS!}