Tags: engineering reverse

Rating: 4.0

[Deep explanation](https://medium.com/@joshuanatan/dam-ctf-2020-write-up-rev-16fc87961db7)

# **Challenge Introduction**

There were 5 pins should be done before we get the flag. Long story short, the pin sequence is 3-1-5-2-4.

## **Pin 3**
This challenge requires us to guess the random number (obviously) but, when we look under the hood, it is not that "random".

When I do this challenge, I always start by looking the final comparison. If we look at the final comparison, the program compares eax registers and 0x13371337. 0x13371337 is a solid number, eax is a register (variable). we trace back the eax to get the required value. Just top of the comparison (addr: 0xd71), there is xor command. It xor-s eax with a value in address %rbp-0x4. When we trace back the %rbp-0x4, we know that %rbp-0x4 value is set by %eax after get_int function which means it contains the user input. Okay, so we know that %rbp-0x4 is the user input.

Now we trace back the %eax. in order to xor successfully executed, both operands need value. We already get the %rbp-0x4. The value of %eax is derived from %rbp-0x8 (addr 0xd6e) and the value of %rbp-0x8 is 0xdeadbeef (addr: 0xd4e).

Using that information, we can conclude a formula
> 0x13371337 = [user input] xor 0xdeadbeef
>
> [user input] = 0x13371337 xor 0xdeadbeef
>
> [user input] = 0xcd9aaad8 which in integer, 3449466328.

Input that into the program, you will unlock the pin 3

## **Pin 1**
Next one is Pin 1.
This challenge requires us to (again) guess the secret number and again as usual, not that "random".

As usual, I started from the bottom. I started from the final comparison. The final comparison compares rbp-0x16 with 0xee (addr: 0xe47). When we examine the program even more, there is another comparison (addr: 0x41). We will look at the comparison at 0xe41.

Comparison at 0xe41 compares %rbp-0x14 with 5. 5 is a solid value, we need to find out what is %rbp-0x14 value. We can find the %rbp-0x14 value is set by 0 at addr 0xe27. Also we can see the value is added by 1 before the comparison. Just by these informations, we can assume this is a loop and %rbp-0x14 is the loop control. The body of loop is on loc_E30.

Examining even further on the loc_E30, we see that variable eax is set to loop counter (addr: 0xe30). Then the value is used for building an address (addr 0xe35). When building the address, we know that %ebp is not changing, %rax is derived from previous command (addr: 0xe30) and var_E is 0xe. So, the full address is %ebp+(0/1/2/3/4/5) - 0xe. The value inside that address is assigned to eax. %ebp-0xe until %ebp+5-0xe is a kind of key, that is taken one by one to xor the $rbp-0x16 value. Those numbers are * Loop 0 = 0x3e * Loop 1 = 0x57 * Loop 2 = 0x81 * Loop 3 = 0xd3 * Loop 4 = 0x25 * Loop 5 = 0x93 By knowing this, we can conclude the solution. > 0xee = [user input] xor 0x3e xor 0x57 xor 0x81 xor 0xd3 xor 0x25 xor 0x93 > > [user input] = 0xee xor 0x3e xor 0x57 xor 0x81 xor 0xd3 xor 0x25 xor 0x93 > > [user input] = 0x63 which in integer, 99 Input that into the program, you will unlock the pin 1 ## **Pin 5** Another random number guessing (again) but this time it really is a "random" number. Again, let us look under the hood and find the final comparison. The final comparison compares %ebp-0x4 with %eax. %ebp-0x4 value is derived from %eax right after the get_int function is called. Therefore we know that %rbp-0x4 is the user input. The other variable is %eax. %eax value is derived from rand function. rand function in C is not good because you will receive same sequence of random number every time you run the program. That is why we see the seed value (0xec2) and \_srand function being called (0xc7). seems secure huh? no! why? because the seed is hard-coded which means, it will still generate same value every time we start the program, just not the default value. The solution is, we need to set a break point at the final comparison (0xeea). When program stops, we peek what is the value of %eax using _info registers %eax_ command in gdb. That %eax value is our answer. > %eax = 0x54393941 which in integer, 1413036362 Input that into the program, you will unlock the pin 5 ## Pin 2 Another random number guess (again) but this time is getting wild. This time I want to post the program output because there is an important information there. When we start the program, you can notice that the program send us a number that seems like a random one but it is not. How do I know that it is not random? If you try to run the program, the value is not randomized but instead, incremented. That makes me wonder, it can be a time (in milisec) or a program counter. If you wait a little longer and try to run the program again, it incremented by some values not just 1, this is definitely time. Looking under the hood, we notice some familiar function. there are function \_time, \_srand, \_rand. From those 3 function, we know that this is a random using seed that is constantly changes. It is a lot harder than previous challenge because we are needed to supply the value first before the random number is generated, so no peeks. Another thing that scarier is the incremented seed that changes everytime the program starts. So, how to solve this? This kind of scenario still vulnerable, why? because we can know the random value generated by the system by supplying the same seed to our program. I will explain it more detail. If we can make a simple program that takes an integer as a seed, we can produce the random number that will be generated using that seed because same seed, same result. Therefore I made a simple program.  #include <stdio.h> #include <stdlib.h> int main(int argc, char* argv[]){ srand(atoi(argv[1])); printf("%d\n",rand()); return 0; }  The program takes seed as the argument and print the first random number. The execution is as follows: 1. Try unlock pin 2, the program will tell you the seed. 2. Copy that seed and execute our simple program 3. The program will produced the random number, copy that number 4. Use the number to solve the pin 2 ## Pin 4 No more random number guessing, now random sentence guessing. We are asked to input a sentence and the system will check whether it likes it or not. Both pictures are the core logic of the program. It seems large, but we can solve it slowly and calmly. As usual, check the final comparison. The final comparison compares %rbp-0x40 with 0x123 (0x10f5). 0x123 is a solid value so we need to work at %ebp-0x40. Just before the final comparison, we see another comparison that seems like a loop control. It compares %eax and %rbp-0x34. %eax value is derived from %rbp-0x3c (addr: 0x10ed) and the %rbp-0x3c value is somehow added by 1 (addr: 0x10e9), 90% sure this is a loop. If we trace back even more, %rbp-0x3c is set to 0 (addr: 0x10cd). This is the loop control. Now we need to find the value of %rbp-0x34. Tracing back %rbp-0x34, we know that %rbp-0x34 value is set from %eax value and %eax value is derived from strlen function. Now we know it loops all the character we passed. The %rbp-0x40 value is derived from add function inside the loop by %eax (addr: 0x10e6). Long before that, we set the %rbp-0x40 value to 0, hence the %rbp-0x40 value is purely derived from the loop. Okay, inside the loop$rbp-0x40 is constantly added by %eax. %eax value is derived from xor with %rbp-0x38. The %eax value before the xor is derived from al (addr: 0x10e0) which is derived from value of %rbp+%rax-0x30 (s is defined for -0x30). The %rbp value is constant and the %rax value is derived from the loop control/counter (addr: 0x10d6).

On the other hand, the %rbp-0x38 value is set by %eax value (addr: 0x10b4). The %eax value is set by the value inside %rdx+0x41. %rdx value is derived from the long chain of command from \_rand (addr: 0x108a until addr: 0x10af). Because it is really hard for me to understand, I just examine the %rbp-0x38 value. Turns out, it is randomized between (0x41 - 0x4a)

This is when the solution comes. We need a sentence that the sum of every character xor-ed by random value between (0x41-0x4a) %rbp-0x38 resulted 0x123. My solution is as follows. I assume that the random number of %rbp-0x38 is 0x45 (just random, you can use another number between 0x41-0x4a). From there I try to find the appropiate character. I want just only 1 character, obviously to make my life easier. The formula is as follow

> 0x123 = [user input] xor 0x45
>
> [user input] = 0x123 xor 0x45
>
> [user input] = 0x166.

0x166? it is way too far beyond our limit. The limit of ascii alphabets is between 0x41 - 0x7a. 0x166 is way beyond the limit. therefore we need to split them to several characters. Again I random guessing, I want 4 same characters but after a long experiment I found the combination of 5 same characters and 1 different. My steps are as follow:
1. Convert 0x123 to int just to make it easier to count, found 291
2. I need 5 characters hence 58\*5 and 1. The differences is too extreme, justify it, I found 50\*5 and 41
3. Convert those numbers back to hex (0x32 and 0x29)
4. XOR those number with %rbp-0x38, in this case 0x45 (0x77 and 0x6c)
5. Convert those hex to ascii (w and l)

Our answer is wwwwwwl and hopes that in any trial, %rbp-0x38 is set to 0x45

## Finishing the Task
Another problem is the windows is only 20 seconds and we have to do all the cracking withing that range. Therefore, we need a kind of automation. In this point, I do not know about pwntools therefore I use cat, python3 commands, and piping to nc.

cat <(python3 -c ‘print(“3”)’; python3 -c ‘print(“3449466328”)’;python3 -c ‘print(“1”)’; python3 -c ‘print(“99”)’;python3 -c ‘print(“5”)’; python3 -c ‘print(“1413036362”)’; python3 -c ‘print(“2”)’;) — | nc chals.damctf.xyz 31932

Usinc cat with hanging '-' will helps us to keep the stdin open and we can keep the interaction. pin 3,1, and 5 are easily done. Comes the pin 2, we copy the seed and run our program, copy back to the connection and spamming 'wwwwwl' for pin 4 and hopefully \$rbp-0x38 is set to 0x45.

Done!
dam{p1ck1NG_l0Ck5_w1TH_gdB}

Original writeup (https://medium.com/@joshuanatan/).