flare24-checksum-cover

checksum presents a binary compiled from Golang. I’ll have to answer a series of math addition problems, and then give it the valid SHA256 hash that matches a static value stored in the binary. On success, it writes an image to my AppData Local directory that has the flag.

Challenge

The challenge prompt reads:

We recently came across a silly executable that appears benign. It just asks us to do some math… From the strings found in the sample, we suspect there are more to the sample than what we are seeing. Please investigate and let us know what you find!

The download has a single 64-bit Windows executable:

oxdf@hacky$ file checksum.exe 
checksum.exe: PE32+ executable (console) x86-64, for MS Windows, 15 sections

Run It

Running the .exe on Windows pops a terminal window with a math prompt:

image-20241101153133868

Answering correctly provides another:

image-20241101153217254

Answering incorrectly prints “Try again! ;)” and closes the window. Entering a non-integer value prints “Not a valid answer…” and exits the window.

If I manually do enough math (the number changes on each execution), it asks for a checksum:

...[snip]...
Good math!!!
------------------------------
Check sum: 9097 + 6900 = 15997
Good math!!!
------------------------------
Checksum: asdfasdf
Not a valid checksum...

Later I’ll see that this is likely a SHA256 hash, and sending it 64 bytes of hex generates a new reply (and exit):

Good math!!!
------------------------------
Checksum: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Maybe it's time to analyze the binary! ;)

Without knowing what it’s calculating, not much I can do here.

RE

Strings

There are lots of string in the binary that suggest it’s written in Golang. For example:

golang.org/x/sys/cpu..inittask
golang.org/x/sys/cpu.Initialized
golang.org/x/sys/cpu.X86
golang.org/x/sys/cpu.options
golang.org/x/sys/cpu.getAuxvFn

There’s also a lot of really long string blogs. For example, there are 24 strings longer than 500 characters:

oxdf@hacky$ strings -n 500 checksum.exe | wc -l
24

That’s because of how Go stores strings, not null terminated, but all jammed together and referenced by offsets and lengths.

main.main

Given that this is a Golang binary, I’ll find the main function under Namespaces in Ghidra:

image-20241101155056748

There’s a bunch of junk in here, but it’s not too hard to get a high level look at what it’s doing:

image-20241101155739785

It starts by getting a random number that’s 0-4 [1], and initializes a count of correct answers to 0 [2]. Then it enters a while loop as long as the number of correct answers is less than 3 + the random number [3]. It generates two more random numbers less than 10,000 [4], and gets their sum [5].

There’s a bunch of difficult to read code for printing this and reading in the user response, which then leads to:

image-20241101160146345

main.b is called to handle errors coming from converting the user input to an int, printing the “Not a valid answer…” message and exiting [1]. If the user input is incorrect, then it prints “Try again! ;)” and returns [2]. Otherwise, it prints the “Good math!!!” message [3], increments the number correct count [4], and continues the while loop.

There’s some stuff handling input, as well as initializing a Chacha20 encryption object, and references to a SHA256:

image-20241101162159212

This section isn’t too important, but it’s where I learn that the checksum is likely a SHA256, and thus 64 bytes of hex.

The important part to note in the next bit is that it is calling main.a to determine if the user-entered checksum is correct:

image-20241101162612597

After that, it does something that involves openubg os.UserCacheDir() + \\REAL_FLAREON_FLAG.JPG:

image-20241101162721181

According to Golang docs, this returns %LocalAppData% on Windows.

I could dive deeper into this, but it seems like if I can figure out what the checksum should be, I can then look for that flag in that image.

main.a

main.a decompiles (at least by Ghidra) a bit oddly:

image-20241101162922327

It starts a counter [1] and loops infinitely, but then has an if in the loop checking to see if it’s looped over the entire string [2]. If so, it does stuff and returns.

At the bottom of the loop [3], it loops over the input XORing it by characters from a global static string, “FlareOn2024”:

image-20241101164247890

Once the entire input has been XORed, then it goes into the if and base64-encodes the result. That must be 0x58 bytes long, and equal to a static string for main.a to return true.

Solve

Find Checksum

To find the correct checksum input, I’ll drop to Python starting with the resulting base64-encoded string:

>>> encoded = "cQoFRQErX1YAVw1zVQdFUSxfAQNRBXUNAxBSe15QCVRVJ1pQEwd/WFBUAlElCFBFUnlaB1ULByRdBEFdfVtWVA=="

I’ll decode it to get the raw encrptyed key:

>>> from base64 import b64decode
>>> enc_key = b64decode(encoded)
>>> enc_key
b"q\n\x05E\x01+_V\x00W\rsU\x07EQ,_\x01\x03Q\x05u\r\x03\x10R{^P\tTU'ZP\x13\x07\x7fXPT\x02Q%\x08PERyZ\x07U\x0b\x07$]\x04A]}[VT"

I’ll use the cycle function to pair each byte of the encrypted string with the right byte of the XOR key, and get the decrypted checksum:

>>> from itertools import cycle
>>> ''.join(chr(x^y) for x,y in zip(enc_key, cycle(b'FlareOn2024')))
'7fd7dd1d0e959f74c133c13abb740b9faa61ab06bd0ecd177645e93b1e3825dd'

Solve

Now I’ll solve the math again and enter this as the checksum:

PS > .\checksum.exe
Check sum: 2355 + 6787 = 9142
Good math!!!
------------------------------
Check sum: 47 + 4874 = 4921
Good math!!!
------------------------------
Check sum: 2630 + 2777 = 5407
Good math!!!
------------------------------
Check sum: 6771 + 2654 = 9425
Good math!!!
------------------------------
Check sum: 3856 + 2597 = 6453
Good math!!!
------------------------------
Check sum: 8759 + 5563 = 14322
Good math!!!
------------------------------
Check sum: 8943 + 4821 = 13764
Good math!!!
------------------------------
Checksum: 7fd7dd1d0e959f74c133c13abb740b9faa61ab06bd0ecd177645e93b1e3825dd
Noice!!

Recover Flag

I know from the RE that something may be writing a JPG to%LOCALAPPDATA%, and there is:

PS > ls $env:LOCALAPPDATA\*.JPG

    Directory: C:\Users\0xdf\AppData\Local

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         11/1/2024   4:49 PM         181532 REAL_FLAREON_FLAG.JPG

It’s got the flag: