Reverse Engineering

Challenge
Link

ebin (464 pts) πŸ₯‡

CK0P0 CTYXHET (497 pts)

Napoleon (497 pts)

Here

Secret meringue recipe (n pts)

Here

Cloud Storage (n pts)

Here

ebin (464 pts)

Description

Hello, dear bruh. Do you know that once upon a time there were cute small Ebins walking around the planet? And with the each day passed they evolved, mastered something new... And of course... They were breeding... They were becoming more and more perfect... The Ebins developed their own languages... And even philosophy... The Ebins wrote and listened to music... And of course they danced to it... And they applauded to each other... The Ebins went so far in their brogress that they invented anime... But after... The snakes came up to something terrible...

Solution

Given PE32+ file, open it using IDA.

Look at the Strings tab, it looks like executable compiled with pyinstaller.

Using this scriptarrow-up-right we can extract the pyc files and all of its dependencies.

Now, look at main.pyc which is the main code of the executable. Use pycdc to decompile the pyc to py file.

Although the pyc file has been decompiled, the code still obfuscated. Lets dive to the first obfusction

F and C function

Actually the decompiler is not purely decompile the pyc file, there are some wrong code in the decompiled version. To defeat this, we can actually debug the python in "executable". To do that, i use pyinjectorarrow-up-right. So the idea is using pyinjector to dump the opcode then manually convert it back to python code.

  • run the executable (ebin.exe)

  • put code.py in the same directory with the executable (ebin.exe)

  • open process hacker

    • find ebin.exe -> right click -> miscellaneous -> Inject DLL

    • Choose PyInjector_x64.dll

For the xD function, it looks like string obfuscation and not related to input validation. So for the xD we can defeat it by running the xD function for all obfuscated strings. I use regex and sublime to get all the argument for xD function.

Now, replace all obfuscated strings with deobfuscated string.

Now, continue with the actual obfuscated function that used to processed our input.

Convert the opcode manually to python code, validate the code by utilizing dis function.

  • Function aggept

  • Function eberse_drift

  • Function dibide

Spawn function

There are 4 functions constructed with function spawn which are brother, ebonatto, eggbonde, and ergipto. Lets take a look on spawn function

There is one crucial variable accessed through globals function which is brother. Because if brother defined, it will call brother after calling virtualprotect. From the documentationarrow-up-right we know that VirtualProtect will change the protection on region of memory and the memory address will be in first argument of the function call. The first argument is buf which is the return ctypes.c_buffer from first spawn argument. So basically the buf is the address of first argument in spawn. Now, look at the initialization of brother variable we can see that the first argument of spawn function is ebor. Because the process only change the protection we can assume that the ebor value should be assembly bytecode. Lets dump the value and open it using IDA.

  • Function brother

Load as x64 file, then go to address 0x0. Go to the address of jmp instruction to view the actual code.

Press p or create function to make a function of those code.

Lets modified some variable to make it easier to understand. Convert the __int64 to char * also for several array variable.

So basically it just do xor for two data, look at brother function call we can understand that it xor first and second argument.

Next, convert brother algorithm to python code

We already have brother function, so lets get another function bytecode. The problem is during the competition i can't find the correct key for each function, so i choose to directly dump the bytecode from memory using python. To dump all the bytecode we need to do it sequentially like the original code.

Write to file again for all the bytecode then open each of it using IDA.

  • ebonatto function

Convert it to python code

  • eggbonde function

Convert it to python code

  • ergipto function

Convert it to python code

Put it all to one file

I alsi create function that communicate through pyinjector to debug each function

During the competition, after recover all the code my team notice that the algorithm is RC5. After that we directly try to use rc5 to decrypt but it failed, so there is something "modified" from the RC5 algorithm. It takes long time for me to notice the different and the different is the loop, look at the code below

  • Line 7 and 9 in original RC5 use S[2*j] instead of S[2*i]. So the code above use value from the block loop instead the round loop.

Modifying the original rc5 then we can decrypt the ciphertext from the challenge. To get all parts of the flag we need to reverse the flow.

  • Total input is 24 bytes

  • First encryption

    • Sparde is last 8 bytes (16-24), so the first encryption use the last 8 bytes as the key and it will encrypt the 0-16 bytes

  • Second encryption

    • Spurdo is first 8 bytes (0-8), so the second encryption use the first 8 bytes as the key and it will encrypt the 8-24 bytes.

We know that the second encryption use the first 8 bytes of data and it not encrypted and the end. So basically we know the key of the second encryption, after that we can get the first encryption key which is the last 8 bytes. Last, we can reverse the operation to get the rest flag.

Did little guess for the middle 8 bytes and got the flag

Flag : SAS{h3_1S_do1n6_l3g_3x3rC1s3}

CK0P0 CTYXHET (497 pts)

Description

The integrity of a world is at stake. A notorious hacker 3vil_Uranus has taken the FNEICS (Federal Nuclear Energy Interactive Control System). Try to understand this complicated interface faster than the 3vil_Uranus can do. Hurry up, you have very little time!

Solution

Given access to URL, we can see that the URL do validation for the input on wasm. The validation can be seen in index.js, so the next step is try to decompile the wasm file using ghidra.

Look at exports section to get all the check function.

Check1

Check1 is straight forward, so just copy the value for each index.

Below is the solution for check1

Check2

Check2 just use xor operation with static value as validation

Below is the solution

Check3

For function check3, it just generate static value using unnamed_function_6 with argument 0x2e. The easy way is by leaking the value through debugger.

To call the function directly from console we can use below code (i got it from my friend, vidner).

Set breakpoint at 0x8f70 then run the code above.

Look at stack and the target value is 1836311903 (0x6d73e55f). Put it on python script

Check4

For the check4, we can reverse the operation to get the valid input.

Below is the solution for check4

Check5

Check5 also has reversible operation

Below is the solution

Check6

In last check (check6) there are so many input validation

But we can see there are pattern on the validation so we can extract constraint and solve it using z3. During the competition i can't got valid solution for the constraint and when checking it on ghidra i found something weird.

Take a look on the offset, on disassembler it shows 0x4 but on decompiler it shows 1. Besides that there are also different in size used for each index, sometime it use 4 bytes, sometimes 2 bytes, and sometimes also 1 byte. To overcome this issue we can decompile the wasm to c using wasm2c then parse the decompiled code to get valid index and valid size. So the last step is writing the solution which has been explained previously in python, below is my solution

During the competition i can't get valid solution although all the constraints are correctly parsed and after after the competition i found that the issue is on the definition of variable param1 and conversion function (to_word, to_byte, to_dword). So by modifying the function and param1 i can get the valid solution.

Combine all solution and convert the value to hex then call it through console.

Flag: SAS{0k_u_54v3d_7h3_p14n37_n0w_u_5h0u1d_61v3_m34n1n6_70_l1f3}

Napoleon (497 pts)

Description

Some crazy guy imagined himself to be Napoleon and attacked our network. Now all the computers in our office crash as soon as we power them on. That freaky guy demanded $10K as ransom to make our computers work again. Our IT specialists have looked at the broken computers and found a suspicious driver in it, however, we are too afraid to look at it more closely - the guy told us that he will wipe all the data in our company if we try to remove the infection without his help. The freak also offered us to remove the infection from one of the machines for free. We managed to capture the process of him interacting with the driver, however, we weren't able to make sense out of these recordings. It seems like there is nothing we can do...

Solution

TBU

Last updated