Another reverse engineering challenge from the ZeroDays Capture the Flag 2018 event was named “Hodor”. The challenge description went as follows:
Oh you said Hodor!
I thought you said Harder!
The first thing to do is check what the file is:
The file is a 64-bit ELF which means we need to have a 64-bit support for the debugger. Before anything else however, doing “strings” shows the following output:
Since the output doesn’t really show anything interesting for the flag, running the executable should output:
Similar to the challenge “WhattaMan“, it is asking for a flag input to check if it’s correct or not. By entering the command “gdb ./Hodor”, GDB should load the executable for debugging:
Again, similar to the challenge “WhattaMan“, doing “info functions” doesn’t show us any interesting functions except seeing the main method:
Type in the command “set disassembly-flavor intel” to set the instruction format to Intel although this is optional and do a “disas main”:
The offsets <+0> to <+5> are basically some memory adjustments required for the program so looking at the instructions starting from <+9> to <+36>:
0x0000555555554d13 <+9>: movabs rax,0xb4e8a45decf0e629
0x0000555555554d1d <+19>: mov QWORD PTR [rbp-0x27],rax
0x0000555555554d21 <+23>: mov DWORD PTR [rbp-0x1f],0x90e23d5d
0x0000555555554d28 <+30>: mov WORD PTR [rbp-0x1b],0xa6b0
0x0000555555554d2e <+36>: mov BYTE PTR [rbp-0x19],0x0
Notice that literal values are being placed in the stack starting from address rbp-0x27. To picture this out:
After placing the literal values in the stack, the next few instructions seem to point on how the program is asking for the flag as an input.
The first red box shows an “ostream” call which points to having the program output the question about what the flag is. The second red box shows an “istream” call which points to having the program ask an input for evaluation while the last red box is simply getting the length of the input. Notice that the length is being compared to 0xe. This means that the string length is being checked if it is 14 characters long because 0xe is 14 in decimal.
Another thing to note is the location of our input string. Based from the function where the length is being recovered, the input string is placed in the stack starting from rbp-0x50 as seen in <+102>. From <+127> up to <+297> is where the real data manipulation process is being worked out.
I have highlighted 3 specific group of instructions as seen here:
The first red box shows that the input (by character of course) is shifted to the left 2 times before being stored in ebx. The second red box shows that the input (still by character) is shifted to the right 5 times and a bitwise “OR” operation is processed between them before having a bitwise “XOR” operation is done.
This equation can be simplified to:
[(INPUT << 2) OR (INPUT >> 5)] XOR 0x23
After having the input manipulated using the equation, it appears to be used for comparison with the values stored in rbp-0x27 as seen previously in the excel sheet.
This means that the complete equation to get the flag is:
[(1ST CHAR INPUT << 2) OR (1ST CHAR INPUT >> 5)] XOR 0x23 = VALUE STORED IN rbp-0x27
[(2ND CHAR INPUT << 2) OR (2ND CHAR INPUT >> 5)] XOR 0x23 = VALUE STORED IN rbp-0x26
[(14TH CHAR INPUT << 2) OR (14TH CHAR INPUT >> 5)] XOR 0x23 = VALUE STORED IN rbp-0x1a
Since there is a bitwise “OR” operation, this equation can’t be reversed easily because the “OR” operation is destructive. What we can do here is do somekind of a brute force application that can operate the equation and see if the resulting character is the one from the stack. To automate the process, a C# application has been created:
Running the application should present us the flag:
By testing the result as an input in the ELF program: