Jingle Bell Reverse Engineering Writeups
Jingle Bell CTF purpose was to bring people in the world of cyber security and to let them enjoy the process of problem solving. There are 5 basic tasks + 1 medium task. Enjoy reading !
I - Simple Check
First, you need to download to Binary.
Let’s run it !
Result :
1 | ironbyte@Med-Ali-Ouachani:~$ ./check |
As you can see it’s a Crackme Chall, the discription says that it’s the easiest crackme ever. Let’s just use strings and save the effort for the next ones.
Result :
1 | ironbyte@Med-Ali-Ouachani:~$ strings check | grep -a "Securinets{" |
Flag = Securinets{51mp13_ch3ck_n3x7}
II - Simple Encryption
Download the Binary.
We run a file check :
Result :
1 | ironbyte@Med-Ali-Ouachani:~$ file encrypt |
As you can see it’s a dynamically linked ELF not stripped (it means that the functions names and symboles remain the same after compiliation process), maybe if we fire Ghidra and do some static analysis on the binary we can reverse it!
As you can see those are the function that are in the binary, we will navigate to the main and decompile it!
Result :
1 | undefined8 main(void) { |
I will try to rename some variables so that you can understand it better, the first step toward understanding the code is is to change variable names, and redefine structures.
1 | int main(void) { |
That’s better right? so what he is doing he reading 0x20 bytes from the stdin(Standard Input File) and writes the result in the input variable. After that, he checking the input using the Check function. We should analyse that function too (I will try to rename variables for you :) ).
Result :
1 | int checker(char* input) { |
As you can see, he is iterating throught the input and xoring each value of the input at the position of the counter (input[counter]) with a value from the the string key at the position counter & 3.
just a little detail :
1 | Ways of doing 5 MOD 4 = 1 |
what that means is that we can apply the modulos operation by applying an And with the (n - 1) where n refers to the divisor. Next, he is comparing the result of XOR operation with a set of bytes located in enc_flag. I suppose that’s the encrypted flag. So that’s a simple encryption where he encrypted the flag using a key. Next thing we got to do is find the key and enc_flag in the memory by just clicking on them in Ghidra:
Result :
1 | key = \x23\x60\xdc\xff |
Maybe it’s time to write a solver !
1 | #! /bin/python3 |
Flag = Securinets{51mp13_45_41w4y5n3x7}
III - Simple Keygen
Download the Binary.
Let’s run the binary!
Result :
1 | ironbyte@Med-Ali-Ouachani:~$ ./keygen |
As you can see, it’s an interface of login. In the description he said find the username with that password, maybe we should do a file check !
Result :
1 | ironbyte@Med-Ali-Ouachani:~$ file keygen |
As always an ELF that is not stripped cool, let’s fire the Ghidra and decompile the hell out of it, gain some time and find the main and navigate to it.
**Result : **
1 | int main(void) { |
As you can see that Keygen is generating some passwords for each username that is entered, Let’s navigate to the gen function and analyse it a little!
Result
1 | void * gen(char *username) { |
As you can see, he is iterating throught the username and he building the password basically by xoring the username at the position of the counter (username[counter]) and adding the counter to the result. That’s a basic Keygen, let’s find the username for the password = Htprg}{k8<<@ and reverse it the way up.
Solver :
1 | #! /bin/python3 |
Flag = Securinets{Ironbyte1234}
VI - Simple javaEnc
As always Download the Class.
First step is to decompile the class, since we can use an online decompiler let’s just use javadecompiler.com , we don’t have to use a tool i’m too lazy to open the terminal.
Result :
1 | import java.util.Scanner; |
A clear Java code finally, let’s start analysing the code. He is asking for an input, then he is reading 2 bytes from the user. After that, he is encrypting the input using those 2 bytes by just xoring each caractere of the input with those 2 bytes and then generating the result and then comparing it with the string “Ucestohcru}l2p2Y>4s15Y`64e7h?Y73Y36Ye667’{“. The problem is that we don’t have the values of those 2 bytes. Don’t forget that a byte ranges from 0 to 255, maybe a BruteForce Attack? we try all the 256*256 combination and grab the flag dierctly of interesting right?
Solver :
1 | #! /bin/python3 |
A better version
1 | inp = b"Ucestohcru}l2p2Y>4s15Y`64e7h?Y73Y36Ye667'{" |
Flag = Securinets{j4v4_82u73_f02c1n9_15_50_c001!}
V - Simple ASM
Download the asm file.
let’s visualize the code:
1 | SECTION .text |
The first lines are used for someting called linux syscalls , their main purpose was to invoke input and output operations to read your input and to write a message for the user. You can have an idea about the values passed to the registers by checking out the Linux Syscall Table . After that, there is a whole algorithm that is working on the input that we pass that generates an output loaded in the EAX register. The best way to understand such an algorithm is just by executing it.
1 | mov eax,DWORD PTR [ebp - 0x04] eax = 12 34 56 78 |
Input in the base 2:
1 | 0x12345678 = 0001 0010 0011 0100 0101 0110 0111 1000 |
Output in base 2:
1 | 0x1e6a2c48 = 0001 1110 0110 1010 0010 1100 0100 1000 |
As you can see the algorithm is reversing the bits of the input, so to find the input for 0xc848b3d5 all we have to do is apply the same algorithm by just reversing it’s bits. One thing to mention is that algorithm is pretty famous used in a lot of algorithms such as FFT(Fast fourier Transformation) and it’s called the Generalized Bit Reversal.
Solver :
1 | #! /bin/python3 |
Flag = Securinets{0xabcd1213}
VI - Sonara
First, you need to download the Binary.
We come now to most intersting task in the CTF, Without any hesitation we run it :
Result :
1 | ironbyte@Med-Ali-Ouachani:~/Securinets-Chrismas-CTF-2022/Reversing/Sonara$ chmod +x Sonara && ./Sonara |
it looks like it’s a Crackme chall, maybe a file check on the bin ?
Result :
1 | ironbyte@Med-Ali-Ouachani:~/Securinets-Chrismas-CTF-2022/Reversing/Sonara$ file Sonara |
As you can see it’s an ELF 64-bit Little Endian executable that is statically linked, which means that all the libraries that the binary needs are already compiled into it, that’s why it’s size is bigger than the usual ones, another reason come from the fact that it’s a GO (you can know that from the “Go buildID”) binary cuz usual C/C++ binaries are much lighter than GO binaries. Another thing to notice, is the stripped keyword which means that the symboles and the functions names are stripped from the binary. Since, it’s a stripped binary it’s useless to run strings on it !
Let’s not waste too much time and start the static-analysis by firing Ghidra & IDA :
It looks like that this binary is a compiled GO Binary. As you can see the first wall that we are facing is the fact that the function names are stripped ! How are we gonna find our main function ? How are we gonna solve this problem? the answer is simply using plugins and scripts. If you are using IDA you should add those plugins to your IDA, otherwise if you are using Ghidra you should add those scripts to your little Dragon.
Let’s navigate into the script-manager in Ghidra :
Result :
Let’s run the script and recover our functions :
Result :
As you can see we have recoverd the functions names, I will now complete the rest of the static analysis using IDA, I will navigate into the main located at the Address 0x0048c980 and disassemble the whole main !
Result :
1 | sub rsp, 1B8h |
from line 1 to 4 is dedicated to the Welcome function after some investigations, you can investigate it too !
Result :
we can try to check the off_4c in the function and try to see what the binary is trying to print!
Result :
As you can see it’s printing the banner, don’t forget that in GO we use the fmt.Println to print text to the output. Let’s keep analysing the rest of the program !
From line 5 to 38 it looks like he is preparing for the input and is printing the string “Flag : “ using the sub_481360 (refers to fmt function) cuz he already passed the string **flag : ** as an argument before calling it. A cool article that can help you understand better what i’m talking about is this Go Calling Convention .
One thing to keep track is that he is saving our input offset = rsp+1B8h+var_38 in the stack and the length of the input at the offset offset = rsp+1B8h+var_170.
If we continue we see that he is loading some bytes into the RCX register and then charging those bytes into stack (line 39 - 48). The first 8 bytes = “CA DC D2 E4 EA C6 CA A6” it looks like that he is chaining those bytes and throwing them into the stack. Dont’t forget that this is an LSB binary which means that the order is reversed which means that for example the first bytes = “CA DC D2 E4 EA C6 CA A6” should be equal to “A6 CA C6 EA E4 D2 DC CA”, same goes for the rest of the bytes. So if we chain all of them we will have bytes = “A6 CA C6 EA E4 D2 DC CA E8 E6 F6 6E D 66 F2 BE 6A 68 F2 BE 72 6 BE 62 6A BE 6E D 66 BE CC EA 6E EA 64 66 42 FA”. That looks like an array of bytes that he might be using !
From line 49 - 51, he moved 0 to the ECX, ESI, EDX (the 32bit parts of those registers).
I suppose we are starting a loop, since the RCX is known for being the counter of loops!
1 | mov [rsp+1B8h+var_178], rdx |
I suppose this is a clear loop as you can see, first he is comparing the RCX with the value of RBX. Since the RBX contains the length of the input, he is looping throught the input and as you can see in the instruction in line 4 he is loading the input at the position pointed to by RCX4 (input[RCX]). Line 5 - 11, is what we call a compiler optimization that actaully do the modulus operation of the input[RCX] by RCX. It checks whether the value of input[RCX] % RCX = 1. If it’s equal to 1 it will execute the Loc_48CB4A block as you can see.
1 | loc_48CB4A: |
What this block is doing, it takes the input that is saved in RDI and it’s shifting it to the left by 1. I suppose the the first sub_448BE0 function is used for casting the value back to char or string and the other sub_448400 is used for affecting that value to the generated string at the offset = rsp+1B8h+var_140.
Let’s see the else block :
1 | shl rdi, 1 |
As you can see it’s doing the same thing as the other block except that he is not xchg the value of AX since it’s not full a left shift operation, it’s actually a multiplication operation by 2, it means multplying the input at the position RCX by 2.
The next block is used for incrementing the value of RCX by 1 by loading the saved value of it from the offset rsp+1B8h+var_180.
I suppose he was encrypting the input by that simple algorithm, let’s analyse the else block of the loc_48CAE1.
Let’s with the first block :
1 | loc_48CB76: |
It’s just loading the length of the generated string into RBX and moving 0 to RCX. Again, it’s another loop i suppose he will be looping throught that generated string.
1 | loc_48CB8D: |
This block is for checking if we are at the end of the string or not, if the value of RBX is lower or equal to RCX than we go to the loc_48CC01 which contain our Congratualtion Message.
1 | loc_48CC01: |
The else block :
1 | mov edx, [rax+rcx*4] |
The else block, he is loading the byte that is located at the RAX which contains address of the generated string at the offset RCX*4. After he is comparing the value of RCX with 38 than jumps to this block :
1 | movzx r8d, byte ptr [rsp+rcx+1B8h+var_166] |
This block is loading one byte pointed by RCX of the value located at the offset rsp+rcx+1B8h+var_166 into the R8D register, that offset actually is the same offset of the bytes that we have found at the beggining of our analysis. After that, it’s comparing that value with the generated value. The next blocks one for incrementing the RCX (Counter by 1), and the other one i suppose is for the fail function.
I suppose the encryption basically simple but reversing GO binaries is not that simple, GO been known for being a language that many Mallwares been written with, it’s a powerful language worth your time to learn and to master.
We can write a python script that can reverse the code now :
1 | enc_flag = [166, 202, 198, 234, 228, 210, 220, 202, 232, 230, 246, 110, 208, 102, 242, 190, 106, 104, 242, 190, 114, 96, 190, 98, 106, 190, 110, 208, 102, 190, 204, 234, 110, 234, 100, 102, 66, 250] |
Flag = Securinets{7h3y_54y_90_15_7h3_fu7u23!}
I hope you enjoyed playing the Securinets-Christmas CTF 2022, never stop learning and feel free to reach me out either on facebook or in real life if you have any questions.