Hacking Series Part 13

Challenge: OTP Implementation

97108
4 min readFeb 6, 2021

--

Category: reverse engineering

We are given a binary called “otp” and a text file called “flag.txt”. If you execute otp, you will notice that you need to pass a key as an argument in order for the program to run. When I opened otp in IDA, I saw that this key is scrambled with unknown logic, then compared to another string. If these strings match, the program prints “You got the key, congrats! Now xor it with the flag!”

This indicates that flag.txt does not contain any part of the actual flag, and finding the key is likely the only way to get the flag. If you look at how the key is scrambled, you see that there is a function named jumble that is responsible for this, as well as multiple shr, add, and, sub instructions afterward.

This means that trying to reverse jumble would probably take too much time, and there is likely an easier way of finding the original key. Before jumble is called, there is a function called valid_char, which makes sure each character in the passed key is between a range of characters or numbers. Figuring out this range is important, since all other characters immediately invalidate the key, which makes the program exit.

In order for a character to be “valid” the function must return 1. If you look into valid_char, you see that there are two cases where the function returns 1, and one where it returns 0.

This is where the range of the characters allowed is determined. One case where the function returns 1 is if the character is greater than “/” and less than or equal to “9”. Another case where the function returns 1 is if the character is greater than “`" and less than or equal to “f”. In other words, the characters need to be in the range of 0–9 or a-f. This indicates that the key is a hex value.

It is also important to note that the key needs to be exactly 100 characters, or the program will immediately exit as well, as shown in the following cmp instruction.

If you continue to look through the assembly, you will see that after the key is scrambled, it must equal the following string starting with “bajbg…”, which is being passed to strncmp.

Now that we know these important pieces of information, we can try to automate the task of figuring the key out letter by letter. Since the key is only 100 characters long, this is an effective method and shouldn’t take too long. We can do this by using a Python script with gdb.

We first have to place a breakpoint in a place where we can access the scrambled key, or rbp+string_compared in the above image. The easiest place to do this would be at the strncmp call, then access the memory location in rax, since that is where the scrambled key would be stored. We would then need to compare every valid character from our defined range to the string shown in the assembly.

If the first character in our scrambled key matches the first character in the string shown in the assembly, we add the value we used to the key. This should be a loop that repeats 100 times.

I made the following Python script to demonstrate this process, it can be run with gdb using the command gdb -q -x script.py .

At the end, it prints a 100 character key. In order to make sure I got the right key, I ran it with otp.

It prints the correct message, which means I got the correct key. It then says to xor it with the flag, which I assume means the string found in flag.txt. The xor operation would use the following values:

Key:

8fc4afdf180cd43aec851ba9ad2f7c74f9c2ecd71bedfeb34d392938f4d3913ebdf81ff3ac9ac2969d819d5854db6872f9ff

Flag.txt:

ffadccb05b5892418ff068dd9d42231e8caf8ebb289ea1873f0a474cabe7ce598db77bac9dfef1d7c2b5af3c35bf5844c082

The correct flag would be the result.

picoCTF{cust0m_jumbl3s_4r3nt_4_g0Od_1d3A_42dad069}

--

--

97108

I like to make things.