st4t1c was the 200 point RE challenge from Bsides Delhi CTF 2018. We are provided with a x86_64 ELF binary in this challenge. As the challenge name suggests, the challenge is probably meant to be solved statically. IDA initially fails to analyze main function and some other functions of the binary because of presence of 0xCC (INT instruction) at various places inside the functions. This is an anti analysis techniques and a simple way to bypass this is to patch 0xCC with 0x90 (nop instruction) because the control flow of the application never goes to these 0xCC instructions.
After this we IDA is able to analyze the binary and we can create functions (P shortcut key) at all the places where IDA failed earlier. So first thing the binary does is it checks if the Environment Variable “team_name” is set to “bi0s”, if its not then it exits.
Now, it is checked if the number of arguments passed to the binary is equal to 2, if not the binary exits . If the previous conditions are met then sub_7EC is executed. Two arguments are passed to sub_7EC , one is the “bi0s” and the other is the string passed as the argument to the binary. Inside sub_7EC, we can see that 22 bytes are copied to the stack (local array) and the string length of input (parameter) string is compared to 22, this suggests that 22 bytes copied to stack are related to the argument (input) string as both have same length. Taking a further look into the function, the function uses the string “bi0s” to generate a number and this number is used to compute an address, and the byte at that address is taken and some operations are performed on it, After further analysis, it becomes the clear the byte is 0xC3 which results in the byte 0xC after the operations.
Taking a look at the assembly in the above image it becomes pretty clear what the binary is doing, it is looping over the input string (passed as param to binary), and it performs two operations on the character depending on its position inside the input string. and eax, 1 determines which operation will be performed on a character, it is same as getting the modulus of 2. So if the above operation returns 0, then as we can see in the left assembly block, 4 is added to the character and then it is xor’ed with our previously computed byte 0xC. If the operation returns 1, in that case 4 is subtracted from the decimal value of the character and it is subsequently xor’ed with 0xC. After all this something interesting happens and the result of the above computations is compared to the 22 bytes that were copied to the stack earlier.
At this point we have enough information to write a script that can automate above steps and can compute the flag for us. Here is a simple python script to compute the flag:
# encoded flag bytes from binary flag = [0x7c, 0x23, 0x34, 0x62, 0x7E, 0x57, 0x37, 0x68, 0x3B, 0x57, 0x6A, 0x3C, 0x35, 0x21, 0x6B, 0x3D, 0x6F, 0x6E, 0x39, 0x62, 0x35, 0x3F] for i in range(0, 22): if i % 2 == 0: flag[i] = (flag[i] ^ 0xc) - 4 else: flag[i] = (flag[i] ^ 0xc) + 4 print ''.join(map(chr,flag))
And this gives us the flag l34rn_7h3_b451c5_f1r57