Hacking Series Part 2
Category: reverse engineering and forensics
We are given a binary named “mystery” and three image files called “mystery.png”, “mystery2.png”, and “mystery3.png”. The actual images of these PNG files are irrelevant and do not contain any information about the flag. But when viewing the contents of the three images with a hex editor (xxd), it is clear that parts of the flag are being stored at the bottom of each image.
The only thing to figure out is which parts are being stored in what file and if there is any attempt at encoding along the way. For that, the binary has to be reverse engineered. When I opened mystery in IDA, I immediately saw that a
flag.txt file has to exist in order for the program to run. Each image file is also immediately opened and returns a corresponding stream. I renamed the stream variables so that it is easier to analyze.
The images are all opened with an append mode, while
flag.txt is opened with a read mode. This confirms the idea that the binary reads the flag from flag.txt and appends it to each image with an unknown logic. The next few blocks of assembly deal with error handling in the instance that these files do not exist. A few characters start being appended at
First, flag.txt is read from, then a character is shifted and written to mystery3.png. If we look at the bottom of mystery3.png, this character most likely results in an “i”. The next character is written to mystery2.png, and is most likely used as obfuscation.
There are many shift and move instructions for this character, which means that restoring the original character could become quite complicated. However, there is one instruction that hints at this being a useless obfuscation:
mov [rbp+var_63], 2Ah ; '*'
IDA tells us that there is an asterisk being stored in the variable that is used to write a character to the stream. This means that all of those shifting and moving operations are being preformed on an asterisk, rather than an important character in the flag. Because of this, it is likely that we can ignore this write to mystery2.png.
The next set of assembly instructions up to the function call to
_fputc writes another character to mystery3.png. There is no shifting or other operations performed on this character, so we can assume that it is the character “c”. Essentially the same process occurs twice more, once for a write to mystery3.png again and once for mystery.png. We can also assume that this series of writing characters writes the beginning of the flag, which can also be guessed since it doesn’t change.
These writes occur six times, so the counter is set to six. Next, there is a conditional jump that compares the counter to the number 9.
If the counter is less than or equal to 9, it will jump to
loc_12F9 . If not, it will continue to the next instruction. That means in
loc_12F9 , three characters are written to mystery.png, since it’s stream is being used to write to. These characters seem to be added to by 1, but this operation may be obfuscation. This is because the character that is being written to the stream is located in
rbp+var_61, but the character that is being added to is located in
rbp+var_63 . It is likely that we can ignore the
add instruction because of this.
If the counter is above 9, it writes a character to mystery2.png instead and moves on. The character that is written is located in
rbp+var_63 , the same one that is added to in the conditional loop. Once this is done, a new counter is initiated and set to 10 (0Ah), then a new conditional statement arises.
If the counter is less than or equal to 14, it jumps to
loc_1347 , if it is above, it sets a new counter and moves on. In
loc_1347, characters are simply written to mystery3.png without additional obfuscation or shifting instructions. This occurs 4 times since the counter moves on at index 15. There is one last conditional loop that deals with characters 15 to 25.
We can assume that the flag is only 25 characters long, since the program exits when the counter reaches above 25. If the counter is less than or equal to 25, it jumps to
loc_1375 . In this location, characters are, again, simply written to a stream without any obfuscation or shifting. This time, it writes to mystery.png.
Now, we know the following information:
- Characters 1–6 are distributed across mystery.png and mystery3.png.
- Characters 7–9 are stored in mystery.png.
- Characters 10–14 are stored in mystery3.png.
- Characters 15–25 are stored in mystery.png.
To combine all of this, I had to look at the different segments stored in each image and slowly reconstruct the original flag. At the end, I got the following result:
If I put the flag into flag.txt and run mystery, it recreates the pattern seen in each image file, which is another way of verifying it.