This challenge was solved by one of my teammates, vek and me, and the writeup was written by vek.
The Choices binary was bascially a giant switch case in a loop and the executed cases were determined by the user’s input at each iteration.
Based on the user inputs, the binary generated a 56-byte flag from an array of constants that’s the same size. What we needed to figure out is the order in which the switch cases needed to be executed. The flag was compared to the correct flag’s hash at the end, so we would know when we’re right.
The binary was compiled with the clang plugin lib0opspass.so. After reversing the library, we found that it took the original program’s basic blocks and turned them into individual cases of the giant switch statement. The user input for a corresponding switch case was generated by a scramble32
function from the BBs’ ID.
scramble32
used a key
generated from a constant seed BAADF00DCAFEBABE3043544620170318
to scramble the BB IDs, but it didn’t change the scrambler’s internal state, so a given (key, ID) pair always gives the same scrambled output. This means that we only had to extract the key and use it to call scramble32()
on the first few integers to get the correct input for each BB.
The key could be extracted by running the supplied compilation command on a dummy source in gdb and breaking right before a call to scramble32()
. After this, we could extract the scrambling code and write a C program that generated the scrambled version of the numbers from 0 to 1000. It seemed that the number for 0, 1, 2, etc. all matched a possible user input in the binary, so we found the IDs of all the original basic blocks! We still didn’t know the original control flow graph from this, but we assumed that each BB followed one with one smaller ID, so we ran:
And it gave us the flag, so it turned out we were right!