/ 2023-11-13-write-ups-for-flare-on-10
back to the top

Write-ups for Flare-On 10

2023-11-13 — last updated 2023-12-13

With the official write-ups and countless community blog posts out there, much has already been said. Still, this blog post serves as a way for me to document my own process and share some notes. For brevity, a bunch of hitting-my-head-against-the-wall was omitted.

As I did not take detailed notes while Flare-on 10 was running, I will try to reconstruct my paths through the challenges as time and memory permits. This page contains write-ups for the following challenges:

1: X

TLDR: click the button 42 times.
Welcome to the 10th Annual Flare-On Challenge!

Statistically, you probably won’t finish every challenge. Every journey toward excellence starts somewhere though, and yours starts here. Maybe it ends here too.

This package contains many files and, I can’t believe i’m saying this, click the one with the “.exe” file extension to launch the program. Maybe focus your “reverse engineering” efforts on that one too.

The first challenge is the classic liveness check. Starting the executable shows a big cross, and a bunch of buttons that we can click to interact with it; presumably we need to find the right 2-digit code and press the unlock button.

X.exe appears to be a plain MSVC/C++ executable, but all bundled DLLs suggest there’s some .NET going on here. In addition to X.exe, there’s also an X.dll that seems interesting. The naming suggests it’s not library code, and Detect It Easy identifies it as .NET.

After loading the .dll in DNSpy and clicking around a bit, I quickly succumbed to frustration of not finding the relevant code (it seems I overlooked monogame.Game1 entirely) and just decide to click the buttons a bunch of times. I should’ve known: the number 42 leads to the flag.

2: ItsOnFire

TLDR: find string AES-CBC and IV. Build the AES key using string slices. Decrypt iv.png.
The FLARE team is now enthusiastic about Google products and services but we suspect there is more to this Android game than meets the eye.

This challenge consists of a single APK file called ItsOnFire.apk. Straight into Android on the second challenge; might as well get it over with.

Without a ready-to-go setup for Android analysis, I scramble a bit and install Android Studio to have a look at what we’re dealing with. I play a bit of Space Invaders.

For static analysis, I open the APK using jadx-gui. It appears the APK is obfuscated; all names were replaced with meaningless placeholders. I start at the top and stop when I find something of interest; in f.b, I identify code that performs CRC32, and there’s symmetric crypto going on. Two of the functions retrieve various values from the APK’s resources

private final File c(int i2, Context context) {  
	Resources resources = context.getResources();  
	Intrinsics.checkNotNullExpressionValue(resources, "context.resources");  
	byte[] e2 = e(resources, i2);  
	String d2 = d(context);  
	Charset charset = Charsets.UTF_8;  
	byte[] bytes = d2.getBytes(charset);  
	Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)");  
	SecretKeySpec secretKeySpec = new SecretKeySpec(bytes, context.getString(R.string.ag));  
	String string = context.getString(R.string.alg);  
	Intrinsics.checkNotNullExpressionValue(string, "context.getString(R.string.alg)");  
	String string2 = context.getString(R.string.iv);  
	Intrinsics.checkNotNullExpressionValue(string2, "context.getString(\n     …             R.string.iv)");  
	byte[] bytes2 = string2.getBytes(charset);  
	Intrinsics.checkNotNullExpressionValue(bytes2, "this as java.lang.String).getBytes(charset)");  
	byte[] b2 = b(string, e2, secretKeySpec, new IvParameterSpec(bytes2));  
	File file = new File(context.getCacheDir(), context.getString(R.string.playerdata));  
	FilesKt__FileReadWriteKt.writeBytes(file, b2);  
	return file;  
}  

This appears to build some sort of crypto object using R.string.ag and R.string.alg to identify the algorithm, and R.string.iv as an IV. I find out that Android apps store their strings in res/values/strings.xml, and identify the following strings:

<string name="alg">AES/CBC/PKCS5Padding</string>
<string name="ag">AES</string>
<string name="iv">abcdefghijklmnop</string>

So, AES it is, then! There’s also a bunch of strings regarding days of the week and a couple URLs, and I resist the urge to browse to them (.. briefly – there’s a Youtube URL that’s not clearly Rick Astley, and I find out it’s Working for the Weekend by Loverboy). More carefully reading through the function, it appears that the function d is responsible for generating the AES key.

private final String d(Context context) {  
	String slice;  
	String string = context.getString(R.string.c2);  
	Intrinsics.checkNotNullExpressionValue(string, "context.getString(R.string.c2)");  
	String string2 = context.getString(R.string.w1);  
	Intrinsics.checkNotNullExpressionValue(string2, "context.getString(R.string.w1)");  
	StringBuilder sb = new StringBuilder();  
	sb.append(string.subSequence(4, 10));  
	sb.append(string2.subSequence(2, 5));  
	String sb2 = sb.toString();  
	Intrinsics.checkNotNullExpressionValue(sb2, "StringBuilder().apply(builderAction).toString()");  
	byte[] bytes = sb2.getBytes(Charsets.UTF_8);  
	Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)");  
	long a2 = a(bytes);  
	StringBuilder sb3 = new StringBuilder();  
	sb3.append(a2);  
	sb3.append(a2);  
	String sb4 = sb3.toString();  
	Intrinsics.checkNotNullExpressionValue(sb4, "StringBuilder().apply(builderAction).toString()");  
	slice = StringsKt___StringsKt.slice(sb4, new IntRange(0, 15));  
	return slice;  
}

The function loads two strings and performs a bit of slicing to extract 16 characters. Let’s grab the relevant resources.

<string name="c2">https://flare-on.com/evilc2server/report_token/report_token.php?token=</string>
<string name="w1">wednesday</string>

Ah, so this is where the URL comes into play! I replicate the functionality in Python, armed with the knowledge that function a is just a wrapper around CRC32.

from binascii import crc32

string = "https://flare-on.com/evilc2server/report_token/report_token.php?token="
string2 = "wednesday"
sb = string[4:10] + string2[2:5]

a2 = crc32(sb.encode('utf-8'))
s = str(a2) + str(a2)
print(s[:16])

This outputs 4508305374508305 (and it has me double-checking whether that’s a 16-byte character string, or an integer).

Okay, now we have AES with a key (4508305374508305) and IV (abcdefghijklmnop), but no ciphertext to decrypt yet. Clicking through the resources, there’s an odd iv.png and ps.png in res/raw; these are not valid PNG files. Could it be..?

Exporting a resource is done by clicking Save as gradle project in the JADX interface. Turning to CyberChef, we can decrypt the images using AES in CBC/NoPadding mode.

ps.png

iv.png

3: Mypassion

TLDR: decompile, step along in a debugger. Gradually work through the stages, tweak input until all checks are satisfied.
This is one of those ones that will work under the right circumstances, Steve. May I call you Steve? Before you take to twitter complaining about a broken challenge, think about how you can be the change you want to see in the world, Steve.

4: Aimbot

I hope this is the only aimbot on your system. Twitch streaming probably pays better than being a mediocre reverse engineer though.

TODO

5: Where_am_i

I wish we had more easy challenges for you this year for you to rack up your personal high score. It wouldn’t help you improve but it would feel great and that is what is most important.

TODO

6: FlareSays

TLDR: run the game in DOSBox-X. Fiddle a bit with the built-in debugger. Give up. Analyse the 16-bit assembly statically. Decrypt strings. Find keyboard input check. Note that file is self-modifying after 128 levels. Patch binary to always set correct input, run until level 128. Run modified file; no success. Note Konami code input check on start screen and that it affects RNG setup. Press Konami code, see blinking screen. Run again. Run modified file.
You’re doing great champ! This challenge is a modern (and retro) take on the classic game Simon Says. The cool thing about this one though is it will make you give up, and maybe take that sales job after all. It’s better for everybody that way.

7: Flake

TLDR: play snake for a bit. Run strings, find d3m0_c0nf.txt file name and XOR key. Decode config file. Set starting score to 10000, run into errors. Try to set up Nuitka to understand how to do injection. Fail misserably. Use Cheat Engine to set Oliver's score to 1. Overtake Oliver.
Subject: need your help... Oliver Shrub, Gardens Department Head, keeps bragging about his high score for this rip off Snake game called Flake. I'm pretty sure he found a way to cheat the game because there is no way it's possible to score 10,000 points...I mean the game ships with a sketchy TXT file so there must be something fishy going on here. Can you look at the game and let me know how I can beat Oliver's high score? Best, Nox Shadows Department Head

8: AmongRust

Our customer recently found the following malware executing on one of their machines. The system was left with a very silly looking wallpaper, and a lot of executables on the machine had stopped working. The customer had successfully restored from backup, but we are still interested in understanding all capabilities of the malware. A packet capture file has been provided to aid your analysis.

TODO

9: Mbransom

TLDR: use bochs debugger to start disk image. Identify deobfuscation, set breakpoints. Figure out keyboard input loop. Find key validation. Compute first 12 characters (XOR constant with 0x55). Use Unicorn to emulate the code and brute force the remaining 2 bytes.
You’re doing so great! Go out and celebrate. Take a day off kid, you’ve earned it. Watch your scoreboard position fall like the sand through the hourglass. Avoid this VM and feel the joy the outside world has to offer. Or crush this one and earn even more internet points, those will come in handy.

10: Kupo

TLDR: boot pdp11 using SIMH. Use rawtap to extract binary from tape. Learn some Forth. Try to debug using built-in adb debugger. Parse the PDP11 symbol table to label functions. Distinguish between native functions and Forth code. Statically figure out that decrypt is a xor-loop (use p/q2-q4! as key) and decode is some accumulator loop. Re-implement in Python.
Did you know the PDP-11 and the FOURTH programming language were both released 53 years ago, in 1970? We like to keep our challenges fresh here at Flare-On, in-line with the latest malware trends.

11: Over_the_rainbow

TLDR: identify XOR and ChaCha20. Decrypt RSA public key. Attack given ciphertext using Coppersmith and stereotyped message.
I’m told this one is easy if you are really good. Based on your solve times so far Google Bard predicts your performance will be: “1 out of 5 stars. I’d give it 0 stars if I could. Food arrived over an hour late, covered in oil. I wouldn’t feed it to my dog”

12: HVM

TLDR: write code to decrypt RC4-encrypted functions. Note two input parameters. XOR hardcoded strings to get FLARE2023FLARE2023FLARE2023FLARE2023 as arg1. Figure out Salsa20 keystream; re-implement using Unicorn. Invert the XOR-loop function through tedious manual labor. Input the result as arg2.
This is the second smallest challenge this year! If only that mattered.

13: Y0da

TLDR: re-use the deobfuscator from challenge 5; point it at all thread entrypoints. Decompile. Use HashDB to identify API calls. Fix API calls by creating an enum. Find gimmie_advic3 and gimmie_s3cr3t commands. Identify MD5 hash aa321932ccdd8cce334a1c3354eed3b1. Google it. Find password patience_y0u_must_h4v3. Find fake flag . Cry. Obtain base32 strings. Fix string by making input to Mersenne Twister constant. Zero the output of the RNG entirely. Decode base32 using custom alphabet. .. ROP chain? What ROP chain?
So close to the end you can almost taste it. Keep going champ, complete this challenge and take your place as one of the Reverse Engineers of All-Time.