HACKvent 2020 - Day 16

01-01-2021 - 3 minutes, 11 seconds - CTF

Challenge - Naughty Rudolph

Santa loves to keep his personal secrets on a little toy cube he got from a kid called Bread. Turns out that was not a very good idea. Last night Rudolph got hold of it and frubl’d it about five times before spitting it out. Look at it! All the colors have come off! Naughty Rudolph!



  • The flag matches /^HV20{[a-z3-7_@]+}$/ and is read face by face, from left to right, top to bottom
  • The cube has been scrambled with ~5 moves in total
  • jElf has already started trying to solve the problem, however he got lost with all the numbers. Feel free to use his current state if you don't want to start from scratch...

(Not the) Solution

The file contained a STL model of a Rubiks cube with the letters of the flag on it, but it was scrambled. According to the hint with about 5 moves of simplified notation (FRUBL). This makes 18 different moves.

\(faces = \{ Front, Right, Up, Back, Left, Down \}\) \(rotation = \{ CW, 2xCW, 3xCW \}\) \(moves = faces × rotation\)

Image of Bochs GUI debugger
The rendered STL file

We started the challenge late in the evening, with two approches. One was to work from scratch, the other one used the provided template, but ported to C#.

First we wrote down the letters from the cube into matrices, then worked on encoding the rotations. Unfortunately we made some mistakes. For one, we wrote 1 (one) instead of l (lowercase L) and second, we had bugs in the rotation algorithm. The provided skeleton code, uses a 3 dimensional matrix to index face, x and y position. This makes the rotation algorithm very time consuming to write because you have to properly index 3 faces per bordering block and 2 per middle block. Not feeling like encoding this for 6 faces we felt clever and reused the existing impl. but just parameterized the faces. This works for the rotated face, but it does not work for the adjacent faces. The block-face indices (x,y) depends on the rotated face and we them hard coded.

By the time we had figured out most of the problems it was already short before midnight. We tried fixing what we knew to be wrong but never got a correct flag. Since full points were now gone and a new day of work coming up, we decided to leave it.

In retrospect, there probably was an issue in our coordinate system. I suspect how we wrote letters of the faces into the matrix, did not match the access pattern of the rotation algorithm. Keeping the axes, indexing via (x,y,z) and having the face as member would have resulted in less complex code I believe.


With the full points and the "perfect scorer" status now gone, we took another look at the challenge over the next few days. After fixing a few more small bugs in the rotation algorithm, we still didn't get a valid flag.

In an act of desperation, we had printed all cube faces, for all reading directions for each step to the screen in which "HV20" occurs. There were many, but we wanted to make sure we didn't miss anything even if our algorithm was off somehow. Fortunately, the following one caught our eye because it read "highscore" for one cube face:

FRONT   HV20{no_s       o0H_{Vsn2       s_on{02VH       2nsV{_H0o
LEFT    ipln_ecs3       cnis_p3el       3sce_nlpi       le3p_sinc
Right   5o_dt@a__       ad5_to_@_       __a@td_o5       _@_ot_5da
Back    }tsal_7a_       7a}alt__s       _a7_last}       s__tla}a7
Top     erocsh6ih       6ceisrhho       hi6hscore       ohhrsiec6
Bottom  e_4wks_le       _welk_es4       el_skw4_e       4se_klew_

The following "words" jumped right out:


We manually parsed and assembled the text fragments in front of us to get the following flag:


Today's source code is a bit longer than for the other challenges, which is why it is attached as archive here:


Next Post Previous Post