Writeup - Flare-On 2018

Writeup - Flare-On 2018

I’ve been working quite long on documenting my take on this years Flare On Challenges and although a bit late can now finally call that one done.

For those not aware Flare-On is an annual contest ran for a bit more than a month, featuring 12 challenges and hosted by FireEye featuring mainly Windows themed Reverse Engineering Challenges.

This year I can proudly say that I was one of the 114 players that finished it and thought that it would make sense to present my solutions hoping that some might be interested.

If you haven’t already I can only recommend to try the challenges yourself before looking through this because it will obviously contain spoilers.

While I tried to make this writeup readable for everyone I’m not sure in how much I succeeded at that so I can only recommend to read through the official writeups for more detailed insights into the challenges

Also even though multiple times linked in the writeup all my solutions can be found here

Minesweeper Championship Registration

Using jd-gui to decompile the JAR file I got the flag directly presented

Ultimate Minesweeper

This time it’s a time limited minesweeper game that contains a lot of mines.

Using dnspy I again got a pretty good view of the decompiled code

Using dnspys feature to recompile modified code I changed the MineFieldControl_Paint function to this:

The small changes I did made all fields visible (thus revealing all mines and number fields) and marks the already clicked ones with the flag symbol

FLEGGO

The FLEGGO challenge consists out of 48 weirdly named binaries that are very similar in functionality, all of them ask for a password and decrypt a image if the right password is entered.
After looking at it dynamically I spotted that the password of each binary is hard coded and actually within the binaries in string format (as a unicode string).

Running this small script, that bascially just runs “strings” on the binaries and saves the output, made all binaries drop their content (images) and produce this mapping:

['65141174.png => w', '85934406.png => m', '67782682.png => m', '75072258.png => r', '16544936.png => e', '67322218.png => _', '58770751.png => o', '64915798.png => 3', '88763595.png => e', 
'18376743.png => _', '36870498.png => m', '72501159.png => c', '47619326.png => p', '70037217.png => m', '18309310.png => @', '15566524.png => e', '82100368.png => m', '60075496.png => s', 
'71290032.png => a', '33718379.png => .', '42255131.png => t', '16295588.png => a', '61333226.png => f', '13147895.png => w', '16785906.png => 4', '80333569.png => o', '37723511.png => n', 
'44958449.png => _', '30171375.png => s', '72263993.png => h', '82236857.png => e', '33098947.png => _', '33662866.png => r', '47893007.png => _', '61006829.png => l', '89295012.png => 0', 
'87730986.png => 0', '65626704.png => 3', '72562746.png => -', '36494753.png => 0', '79545849.png => s', '63223880.png => a', '51227743.png => a', '73903128.png => u', '52817899.png => n', 
'19343964.png => o', '12268605.png => s', '47202222.png => n']

As you can see each of those images contains a number, sorting the to the pictures mapped characters after the given numbers reveals the flag:

[email protected]

binstall

This time it’s about malware.
Running the binstall binary already shows possibly malicious behaviour as it drops a file named “browserassist.dll” and registers it to AppInit_Dlls.

This registry entry causes the dropped DLL to be loaded into every new 32bit process.
Inspecting its behaviour in a debugger (and following the “especially if they are a Firefox user” hint) reveals that the DLLs main function aborts if the started program isn’t Firefox or if the Firefox version is above 54.

When running Firefox 54 32bit with an internet connection after running binstall I noticed an interesting GET request (InternetOpenA) from the DLL to http://pastebin.com/raw/hvaru8NU and by stepping through the process in a debugger I also found that the content is at a later point decrypted

The decrypted data (available here for anyone interested) consists out of a structure dictating javascript injections for the content of the website http://flare-on.com.
Looking into the injections reveals that they add a “su” command and a “folder” that only root can go into

Reversing the relatively simple password checking code, entering the password (“k9btBW7k2y”) and going into the secret folder (“Key”) prints out the flag

Web 2.0

Web 2.0 consists out of a Webpage that uses WebAssembly to determine whether the given input is correct or not.

By default (or entering wrong input) it displays this:

A quick look into the JavaScript that invokes the WebAssembly shows that the input is given by the url parameter q

    let b = new Uint8Array(new TextEncoder().encode(getParameterByName("q")));

and that based on the input the displayed emoji changes

    if (instance.exports.Match(pa, a.byteLength, pb, b.byteLength) == 1) {
        // PARTY POPPER
        document.getElementById("container").innerText = "🎉";
    } else {
        // PILE OF POO
        document.getElementById("container").innerText = "💩";
    }

Stepping through the executed WebAssembly code quickly reveals a point of character comparison though, so not too much reversing is necessary:

Manually converting and writing down the compare characters reveals the flag:

Magic

Magic is in contrary to the previous challenges an ELF Executable for Linux.

The challenges expects you to enter 666 passwords until it finally reveals the flag, looking into the code shows that there is no bypass to this as the flag is the result of xoring all entered passwords.

Inspecting it further showed me that there are a few rather long functions in place that check whether the password is correct.
Interesting here is that those functions are always only given 1 to 3 characters of the password to check and that after each password the verification code changes itself.

My solution (that avoided reversing all validation functions) was to write a gdb python script that halts before the validation functions start, copy them into a unicorn emulation environment and brute force the correct character combination based on the given character length.
This was pretty slow so I added rainbow tables for some of the functions used for checking 3 characters at once and while that speed it up quite a bit it still took multiple hours to brute force all 666 passwords.

After all flags were bruteforced I used a simple script to xor them together onto the base values defined in the binary to calculate the flag:

(The scripts I used are accessible here)

WOW

WOW is an interesting challenge because it doesn’t validate whether what we enter is correct (and that it didn’t even run on my normal VM):

Setting a breakpoint at the exported function (X64Call) reveals that the executable contains a 64 bit DLL and calls into it (even though the binary itself is a 32bit one)

Continuing execution shows the code that prompts the input and that it starts trying to connect to localhost with the given number input as the port.

Inspecting this behaviour showed me that it doesn’t really try to connect, but that the 64bit DLL hooked a ntdll 64bit low level handler (ZwDeviceIoControlFile) and overwrote the behaviour and output for it.
Following it leads to the following comparison:

While I could just have read the compare values from there and restart it after each read value I instead looked into how the comparison value is calculated:

Based on that I wrote a very simple python script that outputs me the correct “ports” and entered them into the binary:

data = [15, 87, 97, 119, 11, 250, 181, 209, 129, 153, 172, 167,144, 88, 26, 82, 12, 160, 8, 45, 237, 213, 109, 231,224, 242, 188, 233, 242]
data2 = []
data2.append(data[0])
for cryptoIndex in range(1,len(data)):
        data2.append(data[cryptoIndex]^data[cryptoIndex-1])
print data2

Doogie Hacker

Doogie Hacker is a plain binary file, inspecting it in a Hexeditor reveals that it might start with a x86 boot sector because of a 0x55 at offset 510 and 0xAA at offset 511 (which is the convention to indicate such).

Booting it with qemu greets with some ascii art and a password prompt:

Again there is no password validation and entering something wrong results in gibberish being displayed:

Reversing the relative simple realmode code I saw that it repeat xors the data first with the current date (day, month, year, century) and then with the entered password and prints the output until it hits a null byte.

Although not perfect, my solution for finding the password was first xoring the data with the date given in the welcome (February 06, 1990), then calculating the key length with the smallest hamming distance from the parts to each other and then trying to search for newlines and carriage returns, which returns this:

It took a bit of fiddling around to finally get the correct password “ioperateonmalware” out of that. I then changed the result from the date getting function from within a debugger to the date from the password screen and got the flag:

(Link to the script for anyone interested)

leet editr

leet editr is a binary that opens up Internet Explorer with an interactive webpage after confirming that one actually wants to run it.

When trying to start it with some common debugging tools active it doesn’t start at all for which I found the reason when looking into the event-logs:

This is easily circumvented by just renaming the tool binaries to something not on the list.
Now looking further into the code it seems that there are instructions executed that don’t really make sense there and the behaviour (jumps) doesn’t match what it displays:

As it turns out the program initializes an exception handlers for execution/single-stepping and reading outside of virtual memory allowed for that and initializes it’s code in memory without any access right (meaning that they can’t be read, written or executed).
The exception handler catches this illegal read/execute, makes the page readable and executable, changes the illegal read/executed instruction to what it actually is, enables single-step (causing another exception on the next instruction to revert everything) and returns code execution to the now correct instruction.

Using x64dbgpy I wrote 3 scripts that first dump the actual executed code, then dump some read memory handled a bit differently and at last load/prepare the binary in a way where the obfuscated code and data is replaced and executed in a readable and followable format.

Looking at the deobfuscated code I found this:

and a VBS script that gets executed by the binary and which controls the Internet Explorer webpage displayed

Within the VBS script the following JavaScript statements are executed under the Internet Explorer webpage context to validate whether the ASCII-Art and Title match the wanted answer before they get forwarded to the decryption of the flag:

g_interval = setInterval(function(){
        if ((textin.value.indexOf('j##mmmmmmm6') != -1) && (strhash(textin.value) == 1164071950)) { 
            hint(' - But did you think of a title for your masterpiece?'); 
            textin.style.color = '#5ccfe6';
            clearInterval(g_interval); 
        } 
    }, 2500)
g_interval2 = setInterval(function(){ 
        if ((title.value.indexOf('title') != -1) && (title.value.indexOf('FLARE') != -1) && (strhash(title.value) == -1497458761)) {
            hint(' - That\'s a nice title!');
            clearInterval(g_interval2);
        }
    }, 2500)

With those given restrictions and the information I had I was able to figure out the correct combination after some annoying hours of trying everything out that seemed somewhat reasonable:

And got the flag:

Link to the scripts
Link to dumped functions,data and internal script

golf

golf was another very interesting challenge which at first didn’t even run for me as I tested it in VirtualBox (well it did run but crashed the VirtualBox process).
After setting up the same environment using VMware Workstation and configuring that to allow nested virtual machines it worked after fulfilling the base conditions this binary sets:

It requires to be started as an administrator, an additional 24 character parameter and the system to allow to load not signed drivers (which can be enabled with “Bcdedit.exe -set TESTSIGNING ON”).
The binary then loads a driver to check the correctness of the 24 character password which if correct is then outputted as the flag.

Looking into the exact behavior of how the process interacts with the kernel driver I found multiple VMCALL instructions, which are probably the reason why it crashed in VirtualBox.

Further inspecting the execution on userland shows that it at some point gives code execution to “gibberish code” which wouldn’t really make much sense to be actually executed

Now just as a warning in advance, I did the whole challenge after this point purely by statically analyzing the dropped kernel driver binary (fhv.sys):

After finding the weird userland code I asked myself where it came from as it was dynamically allocated and written, in the driver I found code that looked like 4 big write operations:

the data here doesn’t look like the weird code I saw earlier but after the writes into a buffer the whole data gets xored with 0xE2

0xC8^0xE2 = 0x2A, 0xF0^0xE2 = 0x12, 0x08^0xE2 = 0xEA, 0x00^0xE2 = 0xE2 yep looks like the first line of all of those write to buffer blocks (mov dword [rdi], 0xE2EA122A)

Now looking even further into the driver I found a point where a value is compared to a lot of different possibilities

and each of them is connected to specific function

After looking into those for a while I realised that I had some sort of instruction set of a virtual machine in front of me which is processing code instead of it being directly executed on the cpu.

To solve this I first extracted and xored all of those possibly executed instructions and wrote a python script for the emulation.
Every time I hit a missing opcode I looked into the driver and implemented it to prevent having to work through everything.

The extracted virtual machine programs weren’t completely trivial but with enough verbose output and a bit trial and error I figured out that each of them get a small part of the password to check and return the success of the comparison in what I called register 15.

For code snippet 3 it for example looked like this:

Based on that I figured out what the snippets do and got the password parts:

1. Fullify the following restrictions: c[0]+c[1]+c[2]+c[3]+c[4] == 0x16b, c[0] == 'F', c[4] == '3',  c[2]+c[3] == 0x86, c[1]+c[2] == 0xa0 (solved with a short z3 script with those constrains) ["Fl4R3"]
2. Xor inputed values with 0x52 and another index depending value and compare to hard coded values (solved xoring with the compare value instead of user input) ["w1th_"]
3. Xor all inputed characters with 0x75 and compare them to some hard coded values (same as 2.) ["ur_v1s0r_"]
4. Compare values directly with hard coded characters ["We4r_"]

Link to the script and code dumps

malware_skillz

Ok now we are at the last two challenges which were actually very time consuming and hard for me which means my solutions aren’t straight forward nor very detailed.
Also the resources I provided probably are not enough to fully reconstruct the process I explain here, mainly because getting them work was each around a week of work with lots of scrapped or half working steps (although I still added everything I still had into the github repository for the ones interested).

The pcap given with the binary starts out with lots of sequential DNS requests which are used within the binary to load code, so my first step was to emulate this traffic within the binary.

As it seems a executable gets transmitted over the DNS requests which then connects to the malware host at analytics.notatallsuspicio.us:9443.

Next, I patched the binary that is loaded at runtime to send all traffic to localhost instead which made it possible for me to replay the interaction with the binary.

As it turns out the malware can both accept, decrypt and work with the commands and responses which made it possible (by recording the content of the buffers where the data got decrypted in) to decrypt large parts of the communication without further reverse engineering on the malware itself:

cd c:\
dir 

06/10/2009  02:42 PM                24 autoexec.bat
06/10/2009  02:42 PM                10 config.sys
07/13/2009  07:37 PM    <DIR>          PerfLogs
02/20/2013  05:08 PM    <DIR>          Program Files
08/01/2017  10:07 AM    <DIR>          staging
07/23/2018  12:38 PM    <DIR>          temp
05/24/2017  11:45 AM    <DIR>          Users
02/20/2013  05:18 PM    <DIR>          Windows
07/23/2018  07:45 AM    <DIR>          work

cd c:\work\
dir

07/23/2018  07:45 AM    <DIR>          .
07/23/2018  07:45 AM    <DIR>          ..
05/26/2013  08:36 AM    <DIR>          AX_Code
05/26/2013  08:37 AM    <DIR>          EX_Code
05/26/2013  08:40 AM    <DIR>          FlareOn2016
05/26/2013  08:37 AM    <DIR>          FlareOn2017
07/23/2018  07:53 AM    <DIR>          FlareOn2018
05/26/2013  08:41 AM    <DIR>          HX_Code
05/26/2013  08:41 AM    <DIR>          Malware
05/26/2013  08:40 AM    <DIR>          NX_Code
05/26/2013  08:37 AM    <DIR>          RSA_factoring
       
cd c:\work\flareon2018\
dir

07/23/2018  07:53 AM    <DIR>          .
07/23/2018  07:53 AM    <DIR>          ..
07/23/2018  07:45 AM    <DIR>          Challenge01
07/23/2018  07:45 AM    <DIR>          Challenge02
07/23/2018  07:45 AM    <DIR>          Challenge03
07/23/2018  07:46 AM    <DIR>          Challenge04
07/23/2018  07:46 AM    <DIR>          Challenge05
07/23/2018  07:46 AM    <DIR>          Challenge06
07/23/2018  07:46 AM    <DIR>          Challenge07
07/23/2018  07:46 AM    <DIR>          Challenge08
07/23/2018  10:44 AM    <DIR>          Challenge09
07/23/2018  07:46 AM    <DIR>          Challenge10
07/23/2018  07:53 AM    <DIR>          Challenge11
07/23/2018  07:53 AM    <DIR>          Challenge12
          
cd c:\work\flareon2018\Challenge09
dir

07/23/2018  10:44 AM    <DIR>          .
07/23/2018  10:44 AM    <DIR>          ..
07/23/2018  10:45 AM               119 README.md
           
type README.md

Larry is running late again. Check the wiki (http://wiki.flare.fireeye.com:8081) for latest updates.

ping wiki.flare.fireeye.com

Pinging wiki.flare.fireeye.com [192.168.200.4] with 32 bytes of data:
Reply from 192.168.200.4: bytes=32 time<1ms TTL=64

After pinging the server the further communication packets contains the files meant to be displayed in a browser.
The last of the transmitted pages is the most interesting as it contains the password to a file I later get my hands on:

After displaying the webpages the packet dump contains the transfer and execution of the malware to another host over SMB, but instead of connecting to the original malware host it connects to the first infected device which acts as the host for that connection.

Further looking over the connection shows the upload of a easily dumpable “level9.crypt” file over FTP from the second infected device.

Considering we are after the unencrypted version of this I reconstructed and analyzed the connection between the first and second infected device and saw that it drops a “Cryptor.exe” executable that is used to encrypt the wanted file before uploading it over FTP.

cd c:\
dir

06/10/2009  02:42 PM                24 autoexec.bat
06/10/2009  02:42 PM                10 config.sys
07/13/2009  07:37 PM    <DIR>          PerfLogs
07/19/2018  03:02 PM    <DIR>          Program Files
07/23/2018  09:29 PM    <DIR>          temp
05/24/2017  11:45 AM    <DIR>          Users
08/10/2018  08:21 AM    <DIR>          Windows
07/23/2018  10:26 AM    <DIR>          work
             
cd cd:\work
dir

07/23/2018  10:26 AM    <DIR>          .
07/23/2018  10:26 AM    <DIR>          ..
07/23/2018  10:26 AM    <DIR>          FlareOn2017_challenge10
08/10/2018  08:08 AM    <DIR>          FlareOn2018_Challenge9
07/23/2018  10:26 AM    <DIR>          Helix
07/23/2018  10:25 AM    <DIR>          NX_Code
07/23/2018  10:26 AM    <DIR>          X16

cd c:\work\FlareOn2018_Challenge9
dir

08/10/2018  08:08 AM    <DIR>          .
08/10/2018  08:08 AM    <DIR>          ..
07/19/2018  02:24 PM             6,656 level9.exe
08/10/2018  07:35 AM            14,502 level9.png
08/10/2018  07:40 AM            10,223 level9.zip

...
CreateFileW(c:\work\FlareOn2018_Challenge9\Cryptor.exe")
...
WriteFile("Cryptor level9.crypt level9.zip")
...
del level9.zip

To get that file I repeated exactly the relevant upload traffic within the malware which made it drop the Crypto executable in the “C:\work\FlareOn2018_Challenge9” folder I created for that.

A quick run through de4dot made it pretty readable:

The encryption program operates in an interesting way:
1. It makes a GET request to “https://github.com/johnsmith2121/react/blob/master/README.md” which is a fork of React.
2. It searches for a specific string in the file which looks something like

3. It uses the first 8 numbers for identification and adds them after the “cryptar” to the encrypted file
4. It uses the next 16 byte as a key and the last 16 byte as IV for the AES encryption of the files with additional information (name length, name, sha256 hash, length, content)

Knowing the inner workings and that the identification dates are chronologically sorted made the decryption of the zip file pretty easy.
Opening it with the password shown previously on the page reveals another executable and a completely white looking picture.

A quick look inside shows that it just draws the flag in a very similar to the background looking color, changing the color of the background reveals the flag:

suspicious floppy

For the last challenge we get a big .img file to work with.

After figuring out that the img file is a normal DOS image with an additional executable added to the autostart I downloaded DOS Box Debugging tools and ran the executable multiple times while searching for different things:

Searching for the origin of the characters getting printed (int 0x10) lead to me to the actual relevant code getting executed. Also noteworthy is the very slow speed the characters get written at (which isn’t DOS Box fault but the actual very high number of executed instructions).

Even more inspection showed me that the executed code wasn’t directly part of the executed binary, so I dumped it and inspected it more throughout:

Inside DOS Box debug window:

BP 9800:0220
<Running>
<Enter something into the program>
MEMDUMPBIN 9800:0000 FFFF
-> MEMDUMP.bin (I named it BEFORE_EXECUTION.bin)

A day of research later made clear that this is in fact a Subleq VM!

To get a first overview I wrote a Python implementation of it and tried to mark relevant reads and writes to the input

But as it turns out it’s not just a normal subleq program, after a day of further static reversing it turns out that even the subleq code executes something VM like:

After many further hours reversing it I at some point started to treat the VM within the subleq VM as a blackbox and only focused on analysing input and output relations without any further understanding of the inner workings.

So to speed up execution I wrote a fitting subleq implementation in C and added lots of different methods of debug output to get the actual data written and context for it.
After that I ran it with lots of different inputs and inspected parts of the 38000 lines log multiple times over a few days until I worked through what is affected by the memory cells given by the user and found out about certain calculations done with them and subtractions/comparisons done with the results.

For example that there is a memory cell calculated through adding all user input values together and adding the length*0xC00 to it (which is then used for further calculations)

After that I found out that this base value and the character in groups of two are calculated into a temporary value (base+(input[index*2+1]<<7+input[index*2])) and then compared to a constant value which depending on its outcome changed the code flow, from that I concluded that all groups of two characters should match the conditions to make the outcome of the subtraction with the constant 0.

Now the last piece was to find out the difference between the calculation of groups, which was that they were xored with different numbers.
By calculating (base+(input[index*2+1]<<7+input[index*2]))^actualValueFromLog I was able to find out that depending on the index they were xored with 0x21*index so the final calculation made for each pair was:

(base+((input[index*2+1]<<7+input[index*2])^(0x21*index)))

Now for to finish it up I put all those math equations into a fancy script and let Z3 solve it all:

Conclusion

As a conclusion I have to say that I had lots of fun playing through the challenges and learned to use quite a few new tools, many of the challenges had a very unique and amazing concept to work through and required quite a bit of thinking for me to solve them.

Thank you very much for reading through this and thanks to FireEye for hosting such an awesome event!

24 Likes

Congratulations on your success in Flare-On 2018 @Leeky, I am very impressed; the challenges were though and required a lot knowledge of a large range of subjects. Thanks for the writeup, I enjoyed reading it and it was cool to see how you tackled the problems you encountered. The last couple of challenges were a bit hard to grasp, but that is understandable since the official write-ups are over 30 pages long.

3 Likes

This topic was automatically closed after 30 days. New replies are no longer allowed.