PicoCTF 2018 - Reverse Engineering writeups


Hello there

I didn't write any topic for a long time but that's because of learning for the AGH Electrical and Electronic Olympics in Poland. After hours of reading about the many topics of computer science to prepare for the AGH competition, I decided to take a look at the most interesting part of the IT for me - security! Today I'm gonna show you my solutions to all (except one) of the RE challenges from Picoctf2018. One more thing - I think that warmup wasn't too hard and interesting so I will avoid it. Enjoy!



-------------------------------------------------------------------------------------------------------------------------------------

assembly-0 (150pts)

 Problem:
What does asm0(0xc9,0xb0) return? Submit the flag as a hexadecimal value (starting with '0x'). 
Hints:
basical-assembly-tutorialassembly-registers

We have to crack some assembly code to get the flag. Here is the code of the file:

It's all about the stack frame of the function. If you don't know what the stack frame is, you can read about it on my blog --> https://shizz3r.blogspot.com/2018/08/reverse-engineering-puzzles-1-stack.html Always when the stack frame of the function is present on the x86-32 architecture first argument of the function is placed at the address [ebp+0x8] and the second arg is at the address [ebp+0xc]. You have to also know that the return value of each function has to be in the eax register at the end of the function's code execution. That being said above code will return the value of the second argument.

Flag:
0xb0

-------------------------------------------------------------------------------------------------------------------------------------

assembly-1 (200pts)

Problem:
What does asm1(0x255) return? Submit the flag as a hexadecimal value (starting with '0x').   
Hints:
assembly-conditions 

One of the most significant instructions in Reverse Engineering is cmp and jmp because these two can change the instructions flow. Hint tells us that this task is all about the conditions. So let's take a look at if statements written in assembly in the above code.

This is checking if the first argument of the function is greater than 0xea. Our function is called with the 0x255 as the first argument so 0x255 is greater than 0xea --> it's true. Then processor will change the value of the EIP register to the address of the part_a label. Let's dive into it.

As you can see we have another compare. It checks if the first argument is not equal to 0x6. That's true so we can jump into part_c.

After the first instruction of part_c label the argument of the function is in the eax register. The next processor will execute adding instruction and in the end, in the eax register, we will have 0x255+3 value. And this is the flag since functions return values from the eax.

Flag:
0x258

-------------------------------------------------------------------------------------------------------------------------------------

be-quick-or-be-dead-0 (200pts)

 Problem:
You find this [1] when searching for some music, which leads you to be-quick-or-be-dead-1 [2] . Can you run it fast enough? 
Hints:
What will the key finally be?
In this time we have the executable file to crack. So the first thing that we can do is to execute this of course.



Our machine is not fast enough, ok. It's time to turn on IDA and look at what is going out internally.



Above is a timer function which is very interesting for us. After 1-second app will call the alarm function and kill the process. So one way to solve this challenge is to patch the argument passed to this method. It can be done using IDA. To do this: Edit-->Patch Program-->Change byte-->Find byte which is the exact location of what you want to change-->Apply changes to input file... I tried to change the seconds variable to 9:



Are nine seconds enough to calculate the key and get the flag?

It works!

Flag:
picoCTF{why_bother_doing_unnecessary_computation_d0c6aace}

-------------------------------------------------------------------------------------------------------------------------------------

quackme (200pts)

 Problem:
Can you deal with the Duck Web? Get us the flag from this program. 
Hints:
Objdump or something similar is probably a good place to start.
Another ELF file is in our hands. Let's run it and see what we can do.



This is normal and popular crack me which we have to crack the correct input. IDA it's a bit boring since NSA published a tool to do RE - Ghidra. Ghidra has a very good decompiler which makes analysis easier. This is the C code of the main generated by Ghidra:



It's intuitive to look into a do_magic function. Here it is:



So when char from our input after xor with char from sekrutbuffer is equal to char from greetingMessage everything is on the right way. When it is true 25 times in a row the do_magic function ends execution. A program which can crack it will be simple, but to write it we have to know what is in the greetingMessage and sekrutBuffer. To do it you can simply click two times at greetingMessage in the Asm code and do it the same for sekrutBuffer. That action will give you the content of these. To crack the password you have to do xor 25 times so you can take only 25 characters from the greetingMessage. This is the simple cracker which will give us the flag:




Flag:
picoCTF{qu4ckm3_9bcb819e}

-------------------------------------------------------------------------------------------------------------------------------------

assembly-2 (250pts)

 Problem:
What does asm2(0x6,0x28) return? Submit the flag as a hexadecimal value (starting with '0x'). 
Hints:
assembly-conditions
The code of the file from assembly-2 task looks like this:

Stack (to visualize where are the arguments):
------------------------------------------------
SAVED EBP (stack frame)
------------------------------------------------
RETURN ADDRESS
------------------------------------------------
0x6 (first arg)
------------------------------------------------
0x28 (second arg)
------------------------------------------------

Stack before the jump to part_b:
------------------------------------------------
0x8
------------------------------------------------
0x28
------------------------------------------------
SAVED EBP (stack frame)
------------------------------------------------
RETURN ADDRESS
------------------------------------------------
0x6 (first arg)
------------------------------------------------
0x28 (second arg)
------------------------------------------------

In the part_b label, it is a compare + jump instruction. That means that we are dealing with a loop. Each time when the value from the address of [ebp+0x8] is less or equal to 0x8f90 processor jumps to part_a label where the value from the address [ebp-0x4] is incremented and value from the address of [ebp+0x8] is increased by 0x8f. After function, we can see that returned value is on the address [ebp-0x4] on the stack. It gives us this simple cracker:

Flag:
0x129

-------------------------------------------------------------------------------------------------------------------------------------

be-quick-or-be-dead-2 (275pts)

 Problem:
As you enjoy this music [1] even more, another executable be-quick-or-be-dead-2 [2] shows up. Can you run this fast enough too? 
Hints:
(1) Can you call stuff without executing the entire program? (2) What will the key finally be?
Again after executing a file from series be-quick-or-be-dead we can see that our machine is too slow to get the flag.



After short research in Ghidra it turned out that the key is calculated by fib function which is the Fibonacci numbers generator, but it uses recursion to do it.



It is not a good idea to use recursion in calculating 1026th fib number cause it's slow. Well... I simply rewrote fib generator to loop version of it.

Result:



So the correct key is 4144667480. The whole logic of the program is in the time of calculating the key so we have to decrease it. Take a look at the asm code in IDA to see if we can patch something to crack this task. In the get_key function the returned value from function calculate_key it's moved to the variable named key. To save time we can change the call calculate_key instruction to mov eax, 4144667480 and throw whole useless recursion out.

To convert mov eax, 4144667480 into bytes you can use asm instruction from the pwntools module.



So our instruction is B8 58 9B 0A F7. After patching in IDA it will look like this:




Click Apply patch to input file... and execute it.



It was quite smart and it works as you see!

Flag:
picoCTF{the_fibonacci_sequence_can_be_done_fast_7e188834}

-------------------------------------------------------------------------------------------------------------------------------------

quackme up (350pts)

 Problem:
The duck puns continue. Can you crack, I mean quack this program [1] as well? 
Hints:
None
We have no hints in this task and one ELF file which has to be cracked. This is the result of running the downloaded app:



It gives us a little hint properly. We can type some text to stdin and can see the encrypted byte of each character typed. Bytes below are probably from the "secret buffer" and we should crack it to get the flag. Let's take a look at the code of this.

main function:



The read_input method is the wrapper to getline function from libc - nothing interesting. To decrypt something it's good to know the encryption method of it. So let's dive into encrypt function, this is probably what we are looking for.

encrypt function:



It encrypts each character of input with this method:
1. Take input[i] and do rol instruction on 4 bits.
2. Take this encrypted value and xor with 0x16.
3. After rol4 and xor, it's time to rol again but this time on 8 bits of this value.

ROL it's a processor instruction which does shift left at the start and bits rotation at the end. To visualize it we can simulate ROL4 instruction on random one-byte value:

1) Shift left 4 bits and save carry bits

([1][1][0][1][0][1][1][0] >> 4) --> [0][0][0][0][1][1][0][1] CARRY BITS: [0][1][1][0]

2) Set 4 places on the left to carry bits:

[0][0][0][0][1][1][0][1] --> [0][1][1][0][1][1][0][1]

And this is the result of this example: 01101101.

ROR instruction is the same as ROL but first, it does shift right and save carry bits on the right side. Since we already know the method of the encryption we can decrypt secret value. To do this we need to walk through each instruction of the encryption from the back and change each rol to ror and ror to rol (a ^ b == c and c ^ b == a so we don't have to worry about xor). And this is the solution:

Proof :) -->



Flag:
picoCTF{qu4ckm3_cba512e7}

-------------------------------------------------------------------------------------------------------------------------------------

Radix's terminal (400pts)

 Problem:
Can you find the password to Radix's login? 
Hints:
The hint tells us that Base64 is the key to solve this task. When you do some research in check_password function it's clear that it's really all about Base64. But to be honest I was lazy and I simply trusted the authors of this challenge. Looking at the asm code using IDA we can see that the strncmp function is called with two arguments. One of them is quite interesting and I decided to try to decode it. Let's take a look:



I got the flag, but I thought about "What if I didn't have the hint?". First of all, I read some papers about Base64 to understand exactly how it works. After a short period of time, I started to analyze the code. From the beginning of my adventure with RE, everyone told me that I should focus on important parts of code since it takes time.

When you are the one who doesn't trust anyone and you would like to get the evidence that it's Base64 you can follow me. I recommend learning about Base64 encoding before reading this analyze.

This is a very important part of the check_password function:



You can see that in each loop iteration it takes three characters from the input. This is the first clue that this is actually Base64 since encoding takes three characters from the plain text in each iteration. The second clue is the buffer called alphabet. You should look at the values inside of it.




It looks familiar, isn't it? These chars are the same as the chars from the table in Base64. In addition, we can see that each character from the input is overwritten by the chars from the alphabet. For me, this is proof that we had to deal with Base64. :)

Flag:
picoCTF{bAsE_64_eNCoDiNg_iS_EAsY_29580993}

-------------------------------------------------------------------------------------------------------------------------------------

assembly-3 (400pts)

 Problem:
What does asm3(0xb5e8e971,0xc6b58a95,0xe20737e9) return? Submit the flag as a hexadecimal value (starting with '0x'). 
Hints:
more(?) registers
In this case, the arguments are greater than earlier and when I was solving this task I decided to not to analyze it at all. How it is possible? Well, it's very easy to understand. I simply rewrote the source file to the NASM, compile it and read the value from the eax register at the end of the function. The source file is written in (or compiled) GNU Assembly but I decided to rewrite it to NASM - it does not make any difference to be honest.

The code of source file in NASM:

To looking internally into process memory using gdb we should compile the file into elf32 executable.



We want to stop the execution of the program before the jump to the return address of the main function. Breakpoint is what we are looking for since we want to read the value from the eax register. Here it is:



And that's how we got the flag. ;)

Flag:
0x7771

-------------------------------------------------------------------------------------------------------------------------------------

keygen-me-1 (400pts)

 Problem:
Can you generate a valid product key for the validation program in /problems/keygen-me-1_0_2b06ee615c1b7021f1eff5829aae5006 
Hints:
None
In this task, we have to generate the correct key to get the flag. To figure out what is going on in downloaded binary we should obviously throw it into IDA or Ghidra. When I was solving this challenge I decided to use IDA and read some assembly code. The first interesting function for us is check_valid_key. In here there is a loop that I will show in parts.



The picture above is the first part of the loop. This is initialization and I variable is set to 0. We can see that we will have to deal with the loop through each character of the key.



The second part is the if statement. It checks if the character from the input is not zero byte or otherwise - null-terminated byte. If this check is not true then the check_valid_char function is called with this char. Otherwise, i is compared with 16. If it's true then the function returns it, otherwise, it returns 0. That means that probably the key should be exactly 16 characters long. Let's dive into the check_valid_char function and see what it does.



There is a couple of if statements but it's not that scary as it may looks. When you look at the variables which char is compared to you can see that there are popular ASCII numbers.
47 --> '/' but 48 is '0'
57 --> '9'
64 --> '@' but 65 is 'A'
90 --> 'Z'
This set of if statements gives us this one:

if ((char >= '0' && char <= '9') || (char >= 'A' && char <='Z')) {
return 1;
} else {
return 0;
}


So we already know that our key has to be 16 characters long and it can contain only letters 'A'-'Z' and numbers '0'-'9'. This is the test:



The next important function is validate_key. This method provides information about what key is valid so this is the last part of our adventure.

This is the first part of the analyzed function:




The code in the above picture is simply the initialization of the loop through each character of the key.

Second part:


size_of_input is the length of input without null-terminated byte. It means that the loop will iterate to the last character of our key. The last instruction from the code which is responsible for last character validation is very interesting. Look:



So the last character has to be equal to the value from the ebx register and our key will be valid! Let's use gdb to check what value should the last character have.



Our key will be validated if the last character will be equal to 0x1c after return from the ord function. This method looks like this in Python:

The script which generates the last character for the ABCDEFGHIJKLMNO key and gets the flag is here:

And this is the result:


Flag:
picoCTF{k3yg3n5_4r3_s0_s1mp13_2243075891}

-------------------------------------------------------------------------------------------------------------------------------------

assembly-4 (550pts)

 Problem:
Can you find the flag using the following assembly source? WARNING: It is VERY long...  
Hints:
Hmm.. There must be an easier way than reversing the whole thing right?
Actually, the code from the source file is quite long and that's why I don't paste the code of it here. The first thing that occurred to me was to simply compile it and check what it will do. Let's try it (this is a 32-bit NASM):



And this is it! The output flag was incorrect for me and I tried to replace the last digit '3' with '}' and it works. I don't know why but whatever. ;)

Flag:
picoCTF{1_h0p3_y0u_c0mP1l3d_tH15_2418650440}

-------------------------------------------------------------------------------------------------------------------------------------

special-pw (600pts)

 Problem:
Can you figure out the right argument to this program to login? We couldn't manage to get a copy of the binary but we did manage to dump some machine code and memory from the running process. 
Hints:
Hmmm maybe if we do the reverse of each operation we can get the password?
When you don't use decompiler this task might be quite difficult. For the first time I did this without decompiler and I had to do static + dynamic analyze to understand what actually happens. I show you the easier method with a decompiler and I strongly recommend you this way.

First of all, it is important to know what is the version of the asm in this source file. This is GNU Asm and when we know it we should change this code a little bit to do the correct compilation process and throw the executable file into Ghidra. This is how the changed code looks like:

Changed code:

This is the compilation process:



And this is the C code of the main function:



All encryption is in the main function in the first while loop. Then after our input is encrypted this program compares each encrypted character to the corresponding byte from the encrypted_password buffer (memory dump). So the encrypted_password buffer is actually the encrypted flag ( :) ) and to get the flag we need to decrypt this buffer.

To do it we have to write the code which does the opposite operations to encryption since the decryption is the opposite of encryption. This is the cracker written in Python obviously (when you don't understand any operation you can look at the comments in the code below):

Result:



Flag:
picoCTF{1_h0p3_y0u_c0mP1l3d_tH15_2418650440}

-------------------------------------------------------------------------------------------------------------------------------------

keygen-me-2 (750pts)

 Problem:
The software has been updated. Can you find us a new product key for the program in /problems/keygen-me-2_1_762036cde49fef79146a706d0eda80a3 
Hints:
z3
This task is similar to keygen-me-1. For example, check_valid_key and check_valid_char functions are the same so our key has to be 16 characters long with letters 'A'-'Z' and (can be) with digits '0'-'9'. When we know it we can focus on the validate_key method.



From the name of the functions key_constraint it's simple to guess that it's a Constraint satisfaction problem. It turns out that a lot of keygen problems are equal to constraint satisfaction problems. As the hint says - we can use z3 to solve it.

My code isn't too beautiful but does the job well. The important thing is that in this code we don't need to worry about ord function from the file to crack since the arithmetic and checks in key_constraint functions are always after the ord. It means that values from the defined constraints are also after the "ording". I present you the code of the program which breaks the key using epic z3 solver.

Getting the flag:



Flag:
picoCTF{c0n5tr41nt_50lv1nG_15_W4y_f45t3r_3846045707}

-------------------------------------------------------------------------------------------------------------------------------------

circuit123 (800pts)

 Problem:
Can you crack the key to decrypt map2 for us? The key to map1 is 11443513758266689915. 
Hints:
Have you heard of z3?
Hint said that we can use z3 again to generate the key. After some research of the decrypt and map2 file, I thought that z3 is actually a good idea to solve it since I'm too lazy to write brute force for it. A file called decrypt is the file with some Python code. It looks like this:



And map2 which we have to decrypt to get the flag is like this:



A lot of similar instructions and this is the end which will be important to us...



I will start the analysis of the decrypt.py file from the beginning.

1. So the cipher variable is the long number in the beginning of the map (11290419911155290710690302751351816427340816196576026120444648063369847565343076531411187044376577503480139343099182304342421923153437113486849423485713547 for map2). chalbox is the rest of the content.

2. The key variable is the first argument from the command line and we can type and it can't be longer than chalbox[0] bits because of the mod. So chalbox[0] is actually the length of the key.

3. Next we have verify function called with the key and chalbox. It's obvious that this method has to return True if this will happen we will get the flag. verify function is the validation of the key.
Let's analyze this very interesting function:

1. Firstly we have three some variables. As we already know length is the length of the decryption key. gates is a list that describes the whole validation circuit. check is the variable that checks the exact bit of the key.

2. b is the reversed key.

3. name --> the name of the current logical gate in validation, args --> values which will be computed by the current logical gate.

4. Next, we have all calculations using gates on exact bits of the key and we know that the exact bit from the "check gate" should be equal to 0. When it will be true we will get the flag after decryption map2.txt.

Summary:

1. We can create a bit vector with the length of 128 bits (since chalbox[0] == 128) and this will be the key after the work of the Solver from z3. This variable will be named solver_key.

2. verify function is our check and constraint. We need only one constraint to get the correct key. When verify(solver_key, chalbox) == True we are done (if this problem is satisfiable).

To do all these things we need to change the code of the decrypt file a little bit. Here is what I create:

Getting the key and the flag:



Flag:
picoCTF{36cc0cc10d273941c34694abdb21580d__aw350m3_ari7hm37ic__}

-------------------------------------------------------------------------------------------------------------------------------------

And that's all folks!


Thank you for reading this and I hope that my writeups will help at least a few people. I didn't solve the Be-quick-or-be-dead-3 challenge because it wasn't interesting for me but maybe I will do this later and write about the solution.

H4v3 4 N1c3 D4y!

Comments

  1. Picoctf 2018 - Reverse Engineering Writeups >>>>> Download Now

    >>>>> Download Full

    Picoctf 2018 - Reverse Engineering Writeups >>>>> Download LINK

    >>>>> Download Now

    Picoctf 2018 - Reverse Engineering Writeups >>>>> Download Full

    >>>>> Download LINK se

    ReplyDelete

Post a Comment

Popular posts from this blog

Learning of malware analysis. Solving 9-1 lab from the "OllyDbg" chapter. ("Practical Malware Analysis" book)