Write up Flare on 2022 — Challenge 7 — Anode

Maulvi Alfansuri
10 min readMay 15, 2023

--

Last year, I was stuck on this challenge during the Flare-On and couldn’t solve it until the end. After few months, I read a similar challenge write-up, “NasiBungkus”, at Idsecconf 2022 (which I also solved after competition), and finally managed to solved this challenge. Here, I would like to share my experience on solving this.

Thanks to kosong for giving me hints about this challenge after end of Flare-On, and to Rendi Yuda for sharing write-up on “NasiBungkus” challenge.

Table of content

· Table of content
· File analysis
· Reverse engineering parts
· Weirdness
· Mathematical operation reversed
· Strategy
· Coding parts — Patching JS
· Coding parts — Parsing Javascript code
· Conclusion

File analysis

We were provided with a file “anode.exe”, which had a node.js icon and was quite large, exceeding 50 MB. When the binary is executed, it asks for input and displays “try again” when the wrong flag is inputted.

Program had huge size and had node js icon
Program do input checks

When inspecting the binary, I notice that loading it into Ghidra or IDA is would wait for few minutes — an hour depend on the machine. Therefore, while waiting for the decompilation, I try to analyze this binary using other tools. Checking the binary on tools “Detect It Easy” revealed some interesting outputs.

In the overlay section, we found an interesting string inside the binary.

Detect It Easy had overlay section

We should be able to see JavaScript code in this section, including the string “nexe”.

View overlay section of this binary found nexe string

A quick search on GitHub led us to this project: https://github.com/nexe/nexe. This project embeds JavaScript code into .exe files and includes a pre-built Node.js binary in the .exe to run this JavaScript.

Nexe project used to compile node js script to exe

On the same overlay section, we encounter the string “Enter flag”, which should be the main JavaScript code in the binary. We should be able to manually extract the JavaScript code by extract string from this offset.

Program logic code exist on the offset

However, I discovered a tool named nexeDecompiler on GitHub, which can be used to extract the JavaScript code from the binary.

Run on the anode.exe and we would get decompiled javascript code of this program

Run nexedecompiler would provide program jscode
Program js code had huge size

All decompiled code can be viewed here and a part of it is shown below.

In the code, we can see several sections. The program reads the input, checks if its length is 44, and then converts our flag to char code, storing it in array ‘b’.

Program read input and check if length of input is 44

After input initialization, I found strange code: on line 18, the program executes a condition that should always be true. However, when we run the program, it does not display the output “uh-oh, math is too correct…”, suggesting that there’s a modification in boolean comparison behavior.

In the next code section, we find an extensive code block that contains a switch case. This code starts on line 23 with state initialization and continues until line 9583.

Program do massive switch case routine — 1
Program do massive switch case routine — 2

This is a huge switch case and is the primary focus of our analysis because the array ‘b’ we initialized earlier is modified here. We’ll discuss this later.

In the next section, there’s a comparison between the target and the array ‘b’, which has been altered inside the switch case.

Program compared target with b value that already processed in switch case routine

Reverse engineering parts

Let’s shift our focus to understanding how the ‘while true’ loop with the massive switch case works in this scenario.

The program initially initializes the variable ‘state’ with the value 1337. Then the program enters the while loop, and ‘state’ is xored with the Math.random() value. The program then moves to the next state, for example, state 306211, and enters the corresponding switch case.

Program had schema switch case routine to go to each operation

After entering the switch case, the program executes a “boolean comparison”. If the boolean comparison has the correct value, the program performs the first mathematical operation and if has false value, program would do second mathematical operation.

All the switch cases within this ‘while true’ loop follow the same schema.

All switch case had same behaviour

If we were to visualize this comparison, we’d have a graph of it.

Program flow on switch case routine

Weirdness

Before diving deeper into the solution, it’s important to understand some anomaly in this program.

Reviewing the JavaScript comments, we find two unusual comments. The first one, as mentioned above, is the strange behavior on line 18, where the program executes a boolean comparison. This value should always be true, but in this program, it’s parsed as false.

This code supposed to be broke execution but not happened if we run anode.exe

The other strange comment is found on line 13176, which states, “uh-oh, math.random() is too random…”.

This code supposed to be executed because math.random value, but did not executed on anode.exe

If we comment out the code from lines 18–21 and run the JavaScript outside the anode.exe binary directly in Node.js, the program defaults to the default case.

Run js directly would return to math.random is too random

This could be because the program executes a xor operation on the ‘state’ with the value from Math.random(), causing ‘state’ to become any number and potentially not falling within the defined cases, thus defaulting.

This state supposed to be static inside anode.exe

However, this behavior doesn’t occur when we run the anode.exe file. The program always completes the switch case routine and never go to default case.

Every run of anode.exe did not result to Math.random too random

This suggests that the program somehow modifies the random code to have a static seed that always matches the defined switch case.

From these peculiar behaviors, we can conclude the following about the anode.exe program:

  1. The program modifies boolean comparisons, so we cannot rely on our logic in the if-else case.
  2. The program modifies randomness, giving a default seed to the program.

Mathematical operation reversed

First, we need to understand how to solve the mathematical operation before going into the solving strategy.

For instance, if we only pick out the mathematical operations, remove all switch cases, and assume the code is executed linearly, we find some simple code.

Program had mathematical operation inside every switch case
Example if all switch case and comparison removed, we would get this code

If we know the target and want to find the input, we can reverse the code. Several approaches can be used, such as using an SMT solver like z3, but we should be able to reverse the code.

If we use the target value as input, perform the mathematical operation in reverse (execute the latest operation first), and invert the symbols (change (-) to (+), (+) to (-), and keep (^) and (&) the same), we should get the input value from the target value.

Create reverse of this code to get input from output
Program successfully get input from output

Our goal in this scenario is to obtain all these mathematical operations in the correct order, perform reversed mathematical operations on the target, and retrieve the correct flag.

Strategy

Now, how do we overcome these strange behaviors and solve this challenge? There are several ways to do this, but in this write-up, I will choose to overwrite the JavaScript inside the application.

If we calculate the offset of our JavaScript code in the binary, we find the value 0x035e3806. This value indicates where our JavaScript code is located.

Program had offset 0x035e3806 on real binary

To simplify the analysis, I will assign names to a few things.

  1. Case scope is all code inside each case, in example can be viewed below:
Case scope included all code inside switch case

2. Comparison scope is code inside each comparison, can be viewed below. First part is true condition scope and second part is false condition scope.

Comparison scope include code inside if else case

Now, what part code that we need to modify in the JavaScript program?

We know that the program executes strange boolean comparisons and generates odd random values, so we don’t know the order of states, nor the scope of the comparison we would enter — the if case or the else case.

We did not know state order and if else that program would entered

So, I had an idea to modify the JavaScript inside the binary, appending some code to each switch case scope and comparison scope.

In the comparison scope, I would add code that assigns a variable. If the program enters the true comparison code (if scope), the program assigns this variable with 1, and if it enters the false comparison (else scope), it assigns this variable with 0.

In the comparison scope, I would also append code that if there is a Math.random used in this scope, the program would save this Math.random() value to a variable and print this variable to log the Math.random() value. The draft code below demonstrates how I would modify the code.

Append code to log program routes and random value

This task should be done programmatically, so we need to parse the JavaScript code to append this logic. For example, if our code is successful, our patched code should look like the one shown below.

Js code that already modified by program

Another challenge is that the program has a JavaScript length limit, so we cannot make our patched JavaScript code size larger than the actual JavaScript code. To resolve this, we can use a JS minifier. In my case, I used https://pypi.org/project/jsmin/.

Program had limit, if we append we should break the limit of this code

After patching the code, we should minify our JavaScript patched code to ensure our code has a length below the real JavaScript code, then pad it with space to make sure our patched code has the same length as the real JavaScript code.

Coding parts — Patching JS

The hardest part is the coding section that implements our ideas from the previous segment.

Below is my code that implements all my strategies that I have explained above. I’ve added some comments to help you understand my goals.

I used split, select, and replace functions to ensure I was able to do this properly. This code can be optimized using regex or something similar, but let’s save that for another write-up.

After running the code, we will get a list of states (signature state and comparison state) in the correct order and the random value that the program used.

After running patches code, program would return state in correct order and which if else that entered included random value

Coding parts — Parsing Javascript code

Now we have a bunch of lines containing signature states entered, comparison states, and random values used by the operation.

List of all log from patches code

We need to use this data to parse the real JavaScript code. The logic I used for parsing the JavaScript code can be explained as follows:

  1. We need to do a reverse operation, so we need to reverse the list.
  2. Save all signature and random values in different arrays because we want to use these separately.
  3. Loop through all state values and use them in parsing the code. Signature states are used to select the code in the switch case scope and comparison states are used to select the comparison scope.
    Extract the mathematical operation and reverse the symbol of this equation (for example, if (+), revert to (-), and vice versa).
  4. If the comparison scope program gets a value from Math.random(), get the value from the random array and increment the random iterator.
  5. Loop through all state arrays until all states are used.
  6. Save all extracted mathematical operations and save them to a file.

Below is the code implementation of our ideas

Once you run this code, you will get a nice and clean list of all mathematical operations.

After running this code, nice mathematical operation would generated — 1
After running this code, nice mathematical operation would generated — 2

Now just append the target value as a ‘b’ array and print these ‘b’ characters at the end of the code to get our flags.

Below is our final snippet code. The complete snippet can be viewed here.

Once you run the script, you will find your flag

Add convert and print output data would get the flag

Conclusion

In this write-up, we finally solved Anode challenge from the 2022 Flare-On competition, which involved reverse engineering and JavaScript modification. We observed peculiar behaviors in the program, including altered boolean comparisons and random number generations.

Our approach involved overwriting JavaScript code within the application and handling the switch case and comparison scopes. This process required us to extract, reverse, and reapply mathematical operations while navigating through various states and random values.

In the end, we successfully parsed the JavaScript code, do reverse math operations, and gett he flag. I hope that this write-up provides valuable insights for those interested in reverse engineering. The journey, from understanding the problem to finally solving it is really satisfying.

--

--