## Flare-on9 Write-up ### 01_flaredle - After extracting, i realized it's a Rev-Web challenge for the beginning of a whole contest. - Reading js code, along with examining the front-end. I can say that the game word list is definitely there. - After playing several times, i realized the color changes based on whether the answer is correct or not. By tracing green color, i found this. ![image](https://hackmd.io/_uploads/Sy7qZerjC.png) - Wow, i tried taking words[57] into the game, but it was wrong. - I tried debugging in the browser and saw this: - ![image](https://hackmd.io/_uploads/rJu6bersA.png) - Let's submit! ![image](https://hackmd.io/_uploads/rJxeGxHiR.png) ### 02_PixelPoker - We have a 32-bit PE file, and when I execute it, a picture appears on my screen. I have to select the correct point (or pixel) on that picture to get the flag. In addition, i have a total of 10 attempts. - Well, at the very first time i reversed this chall, i tried finding the fuction contained some related strings and this is what i found: ![image](https://hackmd.io/_uploads/S18ZsRHpR.png) - Obviously, this is a sort piece of code that i found but generally thanks to these comments i know what next step should i go. As this program spawns a child window so all i need now is to find the Window Procedure which is store all the fuctions of a Window class, thereby controlling the window's behavior and appearance. - And here it is. ![image](https://hackmd.io/_uploads/B1LOpASpC.png) - When i explored its window procedure, I focused on looking for some suspicous Windows Api functions since this chall requires the real-time interaction with player. - There are 2 fuctions which are most worthlily attensive: **SetWindowTextA** and **GetProcessHeap**. - During reading the code i met this: ![image](https://hackmd.io/_uploads/rydZ-1L6C.png) - And surprisingly **'lparam'** contains 4 bytes, the first 2 bytes stores X's value, the second 2 bytes stores Y's value, so i just reversed a little bit and found a point (x,y) = (95,313) and put it in the game. Then, this is finally i found: ![image](https://hackmd.io/_uploads/SyROMJ8pR.png) ### 03_Magic8ball - We continue with a 32-bit Windows PE file. We can see this after running it: ![image](https://hackmd.io/_uploads/SymY4lvaR.png) - After a quick playthrough, we have tasks like entering questions with a maximum of 75 characters, along with actions using the arrow keys to shake the ball. - I found that there are quite a few commands related to SDL (Simple DirectMedia Layer). Following the same approach as before, I focused on finding commands related to output or input that could be influenced by the user. ![image](https://hackmd.io/_uploads/rkUSUxvT0.png) - This section contains quite a few strings related to the game's interface. At first glance, I noticed the string **'gimme flag pls?'** which looks very suspicious. I also observed that this string is copied into **this + 92**. - Overall, for challenges that interact with the user and have a UI with more than one function, like this one, debugging can be a bit tricky. So, I decided to fully exploit dynamic analysis techniques. During this, I discovered a section with quite a few comparisons: ![image](https://hackmd.io/_uploads/S1SLFevaA.png) - With a careful look, I see that the numbers being compared are the integer values of the characters 'LDUR'. It's easy to guess that these characters are hints for the arrow keys: Left, Down, Up, Right. - From there, i decided to try immediately, this is what i got. ![image](https://hackmd.io/_uploads/BkDRFlvTR.png) > Với mình, bài này không quá khó nhưng cũng đã cho mình cảm giác quen thuộc hơn với dạng file chứa GUI này và hơn hết là các lệnh của SDL. ### 04_Darn_mice - Not like 2 lastest challenge i've solved. This is again 32bit PE file but it doesn't have any GUI. - Firstly, i try to run it normally, but its terminal pops out and closes immediatelly. So it's time for static analysis. ![image](https://hackmd.io/_uploads/BJqOjiF6R.png) - Okay, it only works when we run with argument. ![image](https://hackmd.io/_uploads/S1XhsitT0.png) - **Sub_A01240** has its own argument when being called and it seems like it just a simple output function: output a string which is its own argument. - When i try to run again with random argument. Everytime with diffirent arguments, all that i receive is: ![image](https://hackmd.io/_uploads/rJs3niYa0.png) - Well, it is easy to explain why there are some crashes there. - By watching assembly code and pseudo code, i have a deduction like this: Let's call my argv[1] is **Str**. When it enters the loop, it calls **VirtualAlloc**. After that, it modifies a character of **Str** and call to its value, a little bit surprising right :D. it's reason why we have crash as the memory we point to is meaningless. - So personally, i think i should by-pass or do something to solve that crash. Honestly, i came up with the idea to make a every NOP fuction in every loop. But it was fail, do you know why :)). After a short time thinking quite deeply, i realized that if that code i created are NOPs so what happen after the final NOP?. It's still some meaningless value :vv, so it would crash again. So all of this leads me to a decision that i'll make all of them RETNs instead of NOPS :)), - And this is my reward :3 ![image](https://hackmd.io/_uploads/B1Unk3F6C.png) ### 05_t8 - Well, coming to this challenge, i realized this is the toughest among the first 5 challenges of that year. - I have 2 files: t8.exe and traffic.pcapng. From my experience, this is a challenge related to malware analysis! - i tried to run the executable file first, but it seems nothing turns up. - Look back to the pseudocode i saw this: ![image](https://hackmd.io/_uploads/HyQwMHSDJx.png) - Tracing **xmmword_45088C** ![image](https://hackmd.io/_uploads/Hy12GSBwyx.png) - It's in another function which is called after entering main function. I see some APIs which are used to retrieve timestamp at the moment when i run this file. So, i decided not to dig into analyzing that function as my priority is still to understand the main idea of the whole program. - Debugging a file lacking of symbols is always really time-consuming. So, i tried to rename all the necessary functions the easiest way to imagine what they mainly do. ![image](https://hackmd.io/_uploads/H1bfArrwJl.png) - the loop above creates string "flare-on.com". After that, watching program flow, we jump into this function, which likely sets up for a struct or class : ![image](https://hackmd.io/_uploads/ryW3RHSwye.png) - After an appropriate of time spent debugging, the name i renamed for every function in vftable of CClientSock: ![image](https://hackmd.io/_uploads/HJeMxLSPkx.png) - There are still some functions that are just my predictions. But i'm quite confident about some of them. - After some implementations, we come into a function **something** (which is in the vftable). it is about to call function **md5** ![image](https://hackmd.io/_uploads/rJU8ZIHw1g.png) - it is not hard to realize that the input for the hash is - ![image](https://hackmd.io/_uploads/S1WeM8HPJx.png) - Remembering again about the first function which is called after jumping into the main function which i have a screenshot of above. It does exactly concatenate some digits to the end of string **"FO9"** so that's the reason why every time we run the program we see diffirent values. - Watching and debugging more, we will see after hashing with md5 we will come to another algorithm used in this program is RC4 but ... What does it encrypt and what is the key used for? - If we debug carefully, we will see there are 2 phases of RC4 which i renamed: ![image](https://hackmd.io/_uploads/HJhkN8Bw1g.png) - Basically it uses the result of MD5 hash as a key for RC4 (in widechar format). How about the input? - The input actually is string "ahoy" which remains for every time the program runs. ![image](https://hackmd.io/_uploads/S1_pHUrwyg.png) - finally it uses base64 to encode and changes it to wstring ![image](https://hackmd.io/_uploads/Skwkw8SP1x.png) ![image](https://hackmd.io/_uploads/rJOlD8SPJx.png) - As the wstring, i had to spend a lot of time debugging :) my fool ! - So from there i guess i have to make an output base64 encode like the first http request i send to the server. ![image](https://hackmd.io/_uploads/rkxQdUSD1x.png) - i have to have a correct input md5 and it is "FO9" + some digit (maximum 5 and sometime it's 4). So that is the clue which leads our to this solution: **Bruteforce**! ```python from hashlib import * from base64 import * def rc4(key, data): S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % len(key)]) % 256 S[i], S[j] = S[j], S[i] i = 0 j = 0 keystream = [] for _ in range(len(data)): i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] keystream.append(S[(S[i] + S[j]) % 256]) result = bytes([d ^ k for d, k in zip(data, keystream)]) return result def translate(x): ans = b"" for i in x: ans += i.encode() ans += b"\x00" return ans def solution(): for p1 in range(10): for p2 in range(10): for p3 in range(10): for p4 in range(10): for p5 in range(10): data = "FO9" data += chr(ord('0') + p1) data += chr(ord('0') + p2) data += chr(ord('0') + p3) data += chr(ord('0') + p4) data += chr(ord('0') + p5) md5_hash = translate(md5(data.encode('utf-16LE')).hexdigest()) tmp = rc4(md5_hash,translate("ahoy")) v = "790064004e00380042005800710031003600520045003d00" if (translate(b64encode(tmp).decode()) == bytes.fromhex(v)): # for i in tmp: # print(i,end=' ') print(data) return md5_hash solution() #FO911950 ``` - I run the above python script and luckily all i receive is **"FO911950"**. - Come to another part is how can i simulate the network traffic? tbh, this is new for me, i have looked for some solutions to simulate network traffic and... finally i decided to use a flexible python web frame called Flask! ```python from flask import request,Flask app = Flask(__name__) cnt = 0 @app.route("/", methods=["POST"]) def endpoint(): global cnt cnt += 1 if cnt == 1: response = "TdQdBRa1nxGU06dbB27E7SQ7TJ2+cd7zstLXRQcLbmh2nTvDm1p5IfT/Cu0JxShk6tHQBRWwPlo9zA1dISfslkLgGDs41WK12ibWIflqLE4Yq3OYIEnLNjwVHrjL2U4Lu3ms+HQc4nfMWXPgcOHb4fhokk93/AJd5GTuC5z+4YsmgRh1Z90yinLBKB+fmGUyagT6gon/KHmJdvAOQ8nAnl8K/0XG+8zYQbZRwgY6tHvvpfyn9OXCyuct5/cOi8KWgALvVHQWafrp8qB/JtT+t5zmnezQlp3zPL4sj2CJfcUTK5copbZCyHexVD4jJN+LezJEtrDXP1DJNg==" else: response = "F1KFlZbNGuKQxrTD/ORwudM8S8kKiL5F906YlR8TKd8XrKPeDYZ0HouiBamyQf9/Ns7u3C2UEMLoCA0B8EuZp1FpwnedVjPSdZFjkieYqWzKA7up+LYe9B4dmAUM2lYkmBSqPJYT6nEg27n3X656MMOxNIHt0HsOD0d+" return response app.run(port=80) ``` - i actually just cared about in the data it sends back to me so i paid more attention to the last raw data it manipulates and this is what i got! ![image](https://hackmd.io/_uploads/ryDmnIHDye.png) - It's just the first response of the server what about the last? ![image](https://hackmd.io/_uploads/HJCD28SDJx.png) ### 06_à la mode - At this level, we received a dll file named HowDoesThisWorks.dll and a IRchatlog.txt (may be a hint?) - I opened IRchatlog.txt first: ``` [FLARE Team] Hey IR Team, it looks like this sample has some other binary that might interact with it, do you have any other files that might be of help. [IR Team] Nope, sorry this is all we got from the client, let us know what you got. ``` - Seems like nothing useful here. Ok let's check out the dll ![image](https://hackmd.io/_uploads/BkZocGtwJe.png) - Well, it is a binary written in .NET. With .NET we use dnSpy to decompile the binary. ![image](https://hackmd.io/_uploads/SyTd2ztwkl.png) - Looking at this code, we can see that **Getflag** method has an argument named password. It communicates with a server named "FlareOn", after sending password it receives something and write it out (this "something" is highly likely the flag, i guess so) - Let's analyze more with ida. ![image](https://hackmd.io/_uploads/HJO3gEFwJx.png) ![image](https://hackmd.io/_uploads/rJHBW4FPye.png) - Wow, nothing there :)) at this moment i think i should do some research about dll. With at least .NET dll i figure out these steps of how does a dll .NET work: ```python DllEntryPoint -> CorDllMain -> (some piece of code when needed) -> Load CLR -> Find EntryPointToken -> Execute Managed Code ``` - So our task now turns to find the EntryPointToken. There is a tool called CFF Explorer (Custom File Format) which helps us to read information about PE file, including the **EntryPointToken**. - After a quick static analysis, i found this function with a lot of code pieces following the same logic: ![image](https://hackmd.io/_uploads/HyxfbH4Fvye.png) - it seems like sub_100014AE performs XOR operation with each character of the first argument. As for the second argument, which appears every time we call sub_100014AE, it just helps complete the task. - For futher analysis, this is what i have guessed and renamed: ![image](https://hackmd.io/_uploads/HJQCIVYv1g.png) - I know it's quite messy but still easy to understand right!! ![image](https://hackmd.io/_uploads/SJ4VvNKwye.png) - At this point, i strongly believe that function i haven't rename yet will check or resolve the password client sends to the server. ![image](https://hackmd.io/_uploads/SkgbxuNtvyl.png) - It's not hard to identify the algorithm it uses is RC4. RC4 has basically 2 phases: key_scheduling and encrypt/decrypt step. - Surprisingly, all arguments are available so all i need to do is try to implemente RC4 decrypt code. Here is the message from weird_managed and thirty_bytes_weird_managed, respectively. >Big thanks to S1gm4 for helping me find a small bug in my code @@ ![image](https://hackmd.io/_uploads/rygnOVFPkx.png) - Here is my python solution script: ```python! def rc4(key, data1, data2): S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % len(key)]) % 256 S[i], S[j] = S[j], S[i] i = 0 j = 0 for _ in range(len(data1)): i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] print(chr(data1[_] ^ (S[(S[i] + S[j]) % 256])),end='') print() for _ in range(len(data2)): i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] print(chr(data2[_] ^ (S[(S[i] + S[j]) % 256])),end='') key = bytes.fromhex("55 8B EC 83 EC 20 EB FE") data1 = bytes.fromhex("3E 39 51 FB A2 11 F7 B9 2C") data2 = bytes.fromhex("E1 60 A1 18 93 2E 96 AD 73 BB 4A 92 DE 18 0A AA 41 74 AD C0 1D 9F 3F 19 FF 2B 02 DB D1 CD 1A") rc4(key,data1,data2) ``` ### 07_anode >It's a bit delayed for me to finish this challenge, especially since our national New Year celebration is just around the corner. Anyway, Happy Lunar New Year!! - After extracting, i got a file named "anode.exe" and it's quite large! ![image](https://hackmd.io/_uploads/BJsO9ZBOkl.png) - For safety, i used DIE to gather more information from this file but nothing paticularly special turned up. ![image](https://hackmd.io/_uploads/By-GsWrO1x.png) - It's basically a flag checker machine: Receive flag and check it. ![image](https://hackmd.io/_uploads/SJp3iWHuyl.png) - When i loaded it into IDA, OMG, it took a lot of time. However, i found something special: ![image](https://hackmd.io/_uploads/BJiPTbH_1l.png) - V8, a compiler? It made me think of a javaScript engine: Chrome V8. While examinning it carefully, i continously found this: ![image](https://hackmd.io/_uploads/HJ1qJfBuJl.png) - From there, i personally think there might be JavaScirpt code embedded in that binary and whenever we run the script that JavaSciprt code will be executed. To test my assumption, i run the script again with len(flag) = 44. ![image](https://hackmd.io/_uploads/Sy-FxGS_yg.png) - It's ... quite weird ?!? As we can see, there is an if statement after succesfully passing the flag length check: **if (1n) { console.log("uh-oh...") ... }** - It obviously appears to be there but it's not! I decided to extract all JavaScipt code from that binary into a whole new js file to test again. While extracting it i realize something interesting from the binary. Specifically it has been compiled from JS code to an execuatable file using tools similar to PyInstaller (with python code). - It's surprised when i run it in another JavaScript file ![image](https://hackmd.io/_uploads/rkbkBGSOJl.png) - To run Javascript code successfully i had to remove some initial code. It makes a lot of sense that author patched something related to the compiler so that when we run the binary it doesn't produce the same result as we run the JavaScirpt file. - To be honest, i spent an amount of time looking at the initial code to realize how does it work, but unluckily, the first section of JS code which i strongly believe related to the patched compiler is quite tough for me to find out something useful... - Temporarily skip the question of how the compiler works. Looking at the main flow of the code, it checks the flag like this: it uses a state and with every loop,the state will be XORed with a random number (Math.random()). After that based on the value of the state it will jump to a case and execute some expressions there. ![image](https://hackmd.io/_uploads/B1nRwGHOJe.png) - Finally, it performs basic comparision. ![image](https://hackmd.io/_uploads/SJg2dzBdJe.png) - But ... it's random!! and with random number we can not control or predict what the next number will be. So how can we reach the target? - There is a hypothesis like this: the if (1n)... is like a hint that the compiler is patched and if the compiler is patched how likely is it that the randomness is not actually random, as we initially thought :)) ? - When i tried patching the binary to print something to test some of my assumptions. i realized that i could print whatever i want or do whatever i needed with that environment (compiler) as long as i maintained the initial size of the binary. ![image](https://hackmd.io/_uploads/SJ9jnGHdJl.png) - After several times running i realize all the value do not change and i have all state :)) ![image](https://hackmd.io/_uploads/B1lfpfS_Jg.png) - I tried the same with Math.random() and it's not too surprising that the values are the same everytime i run it. There is still one more thing i have to figure out: When does if (...) return true? Looking at the switch...case i realize there are 3 kinds of if statement: if(Math.random() < 0.5) and if (X) and if(Xn) (bigInt type). if the value of X and Xn are within a small range we can definitely brute-force to determine which value will evaluate to true in the if statement, but the all the values are too large. In my opinion there are 2 ways to solve this problem: we can extract all value in an array and test each value one by one or we add in each if ... else statement a "console.log(0/1)". As it always follows a specific flow in the switch ... case so to me, i choose the second way which is might be easier ?!? - After gathering all necessary information, we have to figure out how to retrieve the input array. This is never an easy task but if we have a closer look at how the if statement modifies our initial input: ![image](https://hackmd.io/_uploads/Bk7SfmBuye.png) - sometime it uses "-=", other times it uses "+=" or "^=" and all these stuffs can be reversed! - "**-=**" to "**+=**" - **+=**" to "**-=**" - "**^**" are **remained** - First i need to use some regex to extract all the correct expressions in order. ```javascript! var random = [0.9409662359975814, 0.8785819538827613,...]; var state = [1010356043, 497278214, 181384715, 957436102, 605458814, 429116243,..]; var accept = [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,...]; var target = [106, 196, 106, 178, 174, 102, 31,...] var cnt = -1 function check(x) { return accept[cnt]; } console.log(state.length); var state = 1337; var index_random = 0; var express = 'start!\n'; while (true) { cnt++; state ^= Math.floor(random[index_random++]* (2**30)); console.log(express); switch (state) { case 306211: if (check(random[index_random++]< 0.5)) { express = `b[30] -= b[34] + b[23] + b[5] + b[37] + b[33] + b[12] + Math.floor(`; index_random++ ; express +="random[index_random++] "; express += `* 256); b[30] &= 0xff ;` } else { express = `b[26] -= b[24] + b[41] + b[13] + b[43] + b[6] + b[30] + 225; b[26] &= 0xff ;` } state = 868071080; continue; case 311489: if (check(random[index_random++]< 0.5)) { express = `b[10] -= b[32] + b[1] + b[20] + b[30] + b[23] + b[9] + 115; b[10] &= 0xff ;` } else { express = `b[7] ^= (b[18] + b[14] + b[11] + b[25] + b[31] + b[21] + 19) & 0xff ;` } state = 22167546; continue; case 755154: if (check(93909087n)) { express = `b[4] -= b[42] + b[6] + b[26] + b[39] + b[35] + b[16] + 80; b[4] &= 0xff ;` } else { express = `b[16] += b[36] + b[2] + b[29] + b[10] + b[12] + b[18] + 202; b[16] &= 0xff ;` } state = 857247560; continue; ... ``` - Full code link: https://github.com/anotherme13/flare-on9/blob/main/07_create.js - We need to do everything in reverse,meaning we have to change position of the expressions so that we can process them from the end to the beginning. Basically i used a basic python script to parse the expressions, along with some regex to handle +=,-=. Last but not least, i paid attention to the constraint that b[x] &= 0xff. As i am too lazy to think of a more elegant solution, i decided to apply &= 0xff to every element in the b array whenever any modification happens! ```javascript! var b = [106, 196, 106, 178, 174, 102, 31, 91, 66, 255, 86, 196, 74, 139, 219,...]; var random = [0.9409662359975814, 0.8785819538827613, 0.5130407304372815,...]; b[39] -= b[18] + b[16] + b[8] + b[19] + b[5] + b[23] + 36; for (var x = 0; x < b.length; x++) b[x] &= 0xff; b[22] -= b[16] + b[18] + b[7] + b[23] + b[1] + b[27] + Math.floor(random[1563] * 256); for (var x = 0; x < b.length; x++) b[x] &= 0xff; b[34] -= b[35] + b[40] + b[13] + b[41] + b[23] + b[25] + 14; for (var x = 0; x < b.length; x++) b[x] &= 0xff; b[21] -= b[39] + b[6] + b[0] + b[33] + b[8] + b[40] + Math.floor(random[1559] * 256); for (var x = 0; x < b.length; x++) b[x] &= 0xff; b[11] +=b[32] + b[8] + b[9] + b[34] + b[39] + b[19] + 185; b[19] ^= (b[0] + b[35] + b[14] + b[30] + b[21] + b[33] + 213) & 0xff ; for (var x = 0; x < b.length; x++) b[x] &= 0xff; b[40] -= b[13] + b[3] + b[43] + b[31] + b[22] + b[25] + 49; b[17] ^= (b[41] + b[14] + b[43] + b[6] + b[7] + b[28] + 196) & 0xff ; for (var x = 0; x < b.length; x++) b[x] &= 0xff; b[38] -= b[20] + b[30] + b[31] + b[8] + b[37] + b[33] + 54; b[19] ^= (b[3] + b[30] + b[17] + b[15] + b[13] + b[18] + Math.floor(random[1550] * 256)) & 0xff ; for (var x = 0; x < b.length; x++) b[x] &= 0xff; b[30] +=b[25] + b[34] + b[36] + b[6] + b[41] + b[11] + 108; b[23] ^= (b[30] + b[1] + b[2] + b[25] + b[42] + b[36] + 233) & 0xff ; b[37] ^= (b[28] + b[39] + b[41] + b[11] + b[10] + b[9] + 223) & 0xff ; b[33] ^= (b[10] + b[22] + b[2] + b[1] + b[30] + b[11] + Math.floor(random[1545] * 256)) & 0xff ; b[13] ^= (b[0] + b[29] + b[26] + b[14] + b[15] + b[7] + 31) & 0xff ; for (var x = 0; x < b.length; x++) b[x] &= 0xff; b[29] -= b[39] + b[36] + b[23] + b[31] + b[5] + b[26] + 105; for (var x = 0; x < b.length; x++) b[x] &= 0xff; ... for (var i = 0; i < b.length; i++) process.stdout.write(String.fromCharCode(b[i])); ``` ![image](https://hackmd.io/_uploads/BJeoLXBdJx.png) - Full code link: https://github.com/anotherme13/flare-on9/blob/main/07_ansScript.js > This is a fascinating problem that i have solved, i've learned a lot from this challenge too, from regex to Javascript, and more. A big thanks to author team! ### 08_Backdoor ![image](https://hackmd.io/_uploads/HkQss_mZxx.png) - There is only one file in this challenge. When i run it nothing shows up, but it seems like it's processing something background. ![image](https://hackmd.io/_uploads/rJ8VepruJx.png) - Moreover, it indicates that this binary is writen using the .NET framwork. ![image](https://hackmd.io/_uploads/rJ0tWaruJe.png) - Alright, it's time to open Dnspy. ![image](https://hackmd.io/_uploads/Sy40w6Uqkl.png) - There're so many classes there and it keeps getting complicated as each class contains a bunch of functions as well. - Moreover, an interesting thing is that there are numerous functions fail to be decompiled by Dnspy. ![image](https://hackmd.io/_uploads/SJNltaIqyg.png) - Look at **main** ![image](https://hackmd.io/_uploads/S1r8KpU5ke.png) - And flared_38: ![image](https://hackmd.io/_uploads/SyCdFp89Jl.png) - So it always turns to FLARE15.flare_70(). Let's have a look at it. ![image](https://hackmd.io/_uploads/BJM3YpL9Jl.png) - Since FLARE15.flared_70 fails to be decompiled so it turns out that it always jumps to execute FLARE15.flared_71 ![code](https://hackmd.io/_uploads/S1kOo68qye.png) - From this, we can partially guess that flare_71() which is often called in the catch branch executes a dynamic method. So, my quetion is: why don't they call flare_71 directly but instead place it in catch branch. What is the purpose of this? - After an amount of time reversing flare_71 function, all i have to be attentive is 5 last lines of it. ![image](https://hackmd.io/_uploads/rkKdp6LcJg.png) - Since array b[] is modified before reaching to dynamicILInfo.SetCode and dynamicMethod.Invoke. So all i need to do is set a breakpoint after the dynamic method is executed and take the data of array b[]. >- When you're creating a DynamicMethod and emitting IL, the IL stream doesn't directly store high-level objects like MethodInfo or Type. Instead, it works with tokens that point to metadata entries. >- The GetTokenFor method is used to generate a new token in the dynamic method's context. This token links back to the metadata entry in the original module, allowing the dynamic method to reference the same information. ### 09_Encryptor - Initially, i expected this chall would be harder but ... it was not as hard as i thought. - There are 2 files: 1 executable file and 1 data file. The executable requires an additional arguemnt (a path to a data file) when executing to encrypt it. Ultimately, it will produce a file that is encryted result. - Open the executable in ida: ![image](https://hackmd.io/_uploads/Bk4yQL3KJl.png) ![image](https://hackmd.io/_uploads/BJof782KJx.png) - There is something interesting in sub_40239B ![image](https://hackmd.io/_uploads/B1AtQU2tkx.png) - Sounds like it is a real ransomware simulation :vv. - After analyzing for a long time both statically and dynamically, I discoverd that the algorithm used to encrypt data is chacha20 based on some of its signatures ![image](https://hackmd.io/_uploads/SyJ_EU2tJl.png) - So ... all we have to do next is to find nonce and key. But ... ![image](https://hackmd.io/_uploads/S1y0VI3Kyx.png) - And the important part is that **SystemFunction036** generates a pseudo-random number. From there, i decided to dig deeper into the analysis. - Luckily, i figured out that the encryption process wasn’t fully completed. The key & nonce used in chacha20 was then encrypted using RSA with private key and a modulus. But how can we recover the them? - Look at sub_4021D0() we can see something useful there. ![image](https://hackmd.io/_uploads/SJNvvL3Y1x.png) - It's not hard to realize that the author directly uses **e** to generate **d** in the process and the value of **d** is always equal to 0x10001. So, obviously, it's so reckless! - Taking advantage of it, i make a simple script to rechieve the flag: ```python from Crypto.Util.number import bytes_to_long, long_to_bytes from Crypto.Cipher import ChaCha20 from Crypto.Random import get_random_bytes n = bytes_to_long(bytes.fromhex("dc425c720400e05a92eeb68d0313c84a978cbcf47474cbd9635eb353af864ea46221546a0f4d09aaa0885113e31db53b565c169c3606a241b569912a9bf95c91afbc04528431fdcee6044781fbc8629b06f99a11b99c05836e47638bbd07a232c658129aeb094ddaf4c3ad34563ee926a87123bc669f71eb6097e77c188b9bc9")) d = 0x10001 C = bytes_to_long(bytes.fromhex("5a04e95cd0e9bf0c8cdda2cbb0f50e7db8c89af791b4e88fd657237c1be4e6599bc4c80fd81bdb007e43743020a245d5f87df1c23c4d129b659f90ece2a5c22df1b60273741bf3694dd809d2c485030afdc6268431b2287c597239a8e922eb31174efcae47ea47104bc901cea0abb2cc9ef974d974f135ab1f4899946428184c")) M = pow(C, d, n) msg0 = bytes.fromhex("7f8afa63659c5ef69eb9c3dc13e8b2313a8fe36d94863421462b6fe8ad308d2a79e8ea7b6609d8d058023d97146bf2aa608506484d970e71ea820635ba4bfc518f06e4ad692be6255b") msg1 = M.to_bytes(len(long_to_bytes(M)),byteorder='little') key = msg1[:0x20] nonce = msg1[-0xc:] cipher = ChaCha20.new(key=key, nonce=nonce) ciphertext = cipher.encrypt(msg0) print(ciphertext.decode(errors = 'ignore')) ``` ![image](https://hackmd.io/_uploads/rkfaIE3Fkx.png) ### 10_Nur geträumt >This challenge is a Macintosh disk image (Disk Copy 4.2 format, for those who need to know) containing a 68K Macintosh program. You must determine the passphrase used to decode the flag contained within the application. Super ResEdit, an augmented version of Apple's ResEdit resource editor which adds a disassembler, is also included on the disk image to help you complete the challenge, though you will likely also need to do some outside research to guess the passphrase. This application can be run on any Macintosh emulator (or any real Macintosh from as far back as a Mac Plus running System 6.0.x up to a G5 running Classic). The setup of the emulation environment is part of the challenge, so few spoilers live here, but if you want to save yourself some headaches, Mini vMac is a pretty good choice that doesn't take much effort to get up and running compared to some other options. This application was written on a Power Macintosh 7300 using CodeWarrior Pro 5, ResEdit, and Resourcerer (my old setup from roughly 1997, still alive!). It was tested on a great many machines and emulators, and validated to run well on Mac OS from 6.0.8 through 10.4. Happy solving! Be curious! - It's exactly what is in Readme.md. Besides, we have a file with **.img** extension: ![image](https://hackmd.io/_uploads/H1JRa_Vqkl.png) - I chose Mini vMac to takle this challenge: https://www.gryphel.com/c/minivmac/download.html - After setting it up, the final step was just simply dragging the img file onto the Mini vMac screen. I had tried several times but all my attempts fail. After a while, i realized that the name of the file contains some special characters so that the disk image could not be opened: ![image](https://hackmd.io/_uploads/Sy6wCdN91x.png) - After renaming it, there will be something like this: ![image](https://hackmd.io/_uploads/ry1JJt4cyg.png) - Open the file which has the same name with our challenge,it shows that our task is to find out the correct password to obtain flag. - After playing it many times, i figured out that there is a constant array which is used to perform XOR with my original password ![image](https://hackmd.io/_uploads/ryDxJtE9Jx.png) ![image](https://hackmd.io/_uploads/HkQ31YN5Jg.png) ![image](https://hackmd.io/_uploads/HkcJgK45kl.png) ... - Leaving my exploration there, i moved on to digging into ResEdit. There are multiple (may be) files there. There is one containing a message: ![image](https://hackmd.io/_uploads/ryn3duVqyg.png) - It leaves some clues there but ... personally i found it still hard to apply now. - After spending a quite long time on recovering, i found something interesting there: ![image](https://hackmd.io/_uploads/S1tAbt4ckg.png) - Trying to get flag string in hex view: ![image](https://hackmd.io/_uploads/SklXMY45Je.png) - I found that except for the first byte, the others look like the values of an array used to XOR with my original input. I tried entering nothing: ![image](https://hackmd.io/_uploads/HkloGKNcJe.png) - From there i became quite confident that those hex value are part of the constant array. - Taking a step back, i recalled that our flag format always includes a substring: "@flare-on.com". So i tried to xor it with all possible substrings of that hex value. ![image](https://hackmd.io/_uploads/HkFeOdV9Je.png) - It printed out numerous strings but one of them stood out as suspicious. ![image](https://hackmd.io/_uploads/HyQDvuV51l.png) - Yes it is "du etwas Zei", the most meaningful one. - Asking AI for help: ![image](https://hackmd.io/_uploads/H1sX4YVqkg.png) - i gave that string a try: ![image](https://hackmd.io/_uploads/Hk6g5d4q1e.png) - Fortunately,the first few characters were somewhat readable and may have also a meaning too. ![image](https://hackmd.io/_uploads/HkFhiOEqJx.png) - Actually the character "ü" can not be written in there or at least, i wasn't sure how. So, I once turned to AI for the last step. ![image](https://hackmd.io/_uploads/r1Fz3O49Jl.png) Correct flag: **Dann_singe_ich_ein_Lied_fur_dich@flare-on.com** ### 11_the_challenge_that_shall_not_be_named - Coming to the last challenge, we have just only one executable file: 11.exe. I check it using DIT: ![image](https://hackmd.io/_uploads/rkp6w0z9yl.png) - Well, it's quite clear that the file was originally written in python and later converted into an executable using PyInstaller. - I used Pyinstxtractor to extract PYC file and then decompiled it to retrieve the original python code. ![image](https://hackmd.io/_uploads/B1H6DRM5yg.png) - Turn pyc to py: ![image](https://hackmd.io/_uploads/ByEWEZQ91l.png) - Unluckily, the primary code was protected by pyarmor (a strong command-line tool designed for obfuscating Python scripts) - At the first attempt to run the pyc file, it didn't work due to incompatible python version. After having a quick look at the version of python used in that code. It turned out to be **3.7.9** ![image](https://hackmd.io/_uploads/BJXW_0f5ke.png) - Running it again, it shows an error: ![image](https://hackmd.io/_uploads/B1jAwCf9ye.png) - From there, library dependencies allow us to perform techniques like DLL hijacking. This is my own _crypt.py: ![image](https://hackmd.io/_uploads/HJvZVIXqye.png) - There are punch of stuffs printed out: ![image](https://hackmd.io/_uploads/r1YNL875kx.png) - Perhaps the most important thing we need to pay attention to is: ![image](https://hackmd.io/_uploads/ryOkwI7cke.png) - Finally, i edited _crypt.py again: ![image](https://hackmd.io/_uploads/Bk1wP8Xc1x.png) ![image](https://hackmd.io/_uploads/Syk5w8QcJl.png) - if we look carefully, we will see the last contest flag!: flag: ***Pyth0n_Prot3ction_tuRn3d_Up_t0_11@flare-on.com***