In July, the FireEye Labs Advanced Reverse Engineering (FLARE) team created and released the first FLARE On Challenge to the community. A total of 7,140 people participated and showed off their skills, and 226 people completed the challenge. Everyone who finished the challenge received a challenge coin to commemorate their success.
The coveted challenge coin
We are releasing the challenge solutions to help those who didn’t finish improve their skills. There are many different ways to complete each challenge, so we waited to see what solutions people devised. We found the following solutions posted online and recommend taking a look at these as well to see how the later challenges can be solved in different ways.
In this initial issue of the blog series, we focus on the first five challenges, which were easier. We hope that by starting with easier solutions, people who have never reversed before can still benefit. Below is a write up of how to solve the first five challenges, written as if we’d never seen them before. The goal of each challenge is to find a key in the form of an email address that allows you unlock the next challenge. The archive of challenges have been posted to the challenge website.
Stay tuned for Part 2 where we show two different and interesting ways of solving Challenge 6.
Challenge 1: Bob Doge
The first challenge starts out pretty easy. When we drop the binary into CFF Explorer (or equivalent PE tool), it informs us that we’re dealing with a PE 32-bit .NET Assembly, so we can run it in an x86 Windows VM and see what happens. A decode button appears to have two functions: transforming Bob Ross into Bob Doge, and converting the top label into some unprintable strings. We drop the binary into ILSpy (or equivalent .NET decompiler) to get an idea what this decode button is doing. The decompiled code is shown in the top of Figure 1.
Figure 1: Decode button code in ILSpy (top) and re-implemented in Python (bottom)
The button changes the image to Bob Doge, and encodes a resource string twice and sets the label text to the result. If we save out the resource that is being manipulated, we can play around with it. The Python code in the right side of Figure 1 is the decode button re-implement to help us figure out what we are dealing with. When this Python code is run, the following is printed out showing the solution to Challenge 1 as “Text 1” in the output.
Figure 2: Challenge 1 result
Challenge 2: Javascrap
The next challenge (to the bane of some of our players) is not a Windows PE file. Instead we have a version of the website www.flare-on.com. There has to be something special about this version of the website. So we look at page source of home.html. If we compare this version with the live challenge website, one line in particular stands out:
<?php include "img/flare-on.png" ?>
Why would a PNG image be loaded as a PHP script? When we open this image with an image viewer, the banner comes up so it is definitely an image. Since we know that the image is being loaded as a PHP script, we search for php inside of the image file and find the following:
19C0 AE 42 60 82 3C 3F 70 68 70 20 24 74 65 72 6D 73 .B`.<?php $terms
19D0 3D 61 72 72 61 79 28 22 4D 22 2C 20 22 5A 22 2C =array("M", "Z",
19E0 20 22 5D 22 2C 20 22 70 22 2C 20 22 5C 5C 22 2C "]", "p", "\\",
19F0 20 22 77 22 2C 20 22 66 22 2C 20 22 31 22 2C 20 "w", "f", "1",
1A00 22 76 22 2C 20 22 3C 22 2C 20 22 61 22 2C 20 22 "v", "<", "a", "
1A10 51 22 2C 20 22 7A 22 2C 20 22 20 22 2C 20 22 73 Q", "z", " ", "s
Pulling this out of the image leaves us with:
$terms=array("M", "Z", "]", "p", "\\", "w", "f", "1", "v",...
$order=array(59, 71, 73, 13, 35, 10, 20, 81, 76, 10, 28, 63, 12,...
So we have an array of characters, and then an array of indexes into the array of characters being used to build a string that gets evaluated. If we replace that eval with a call to echo, we can see the following strings in the display:
This reveals more obfuscation! Applying the same trick again results in the following lines of code:
So from this, we decide we should base64 decode the $_ string and, lo and behold, we have our key.
This is a string that is made out of mixing hexadecimal and ordinals. By writing a quick decoder for this conversion we get a11DOTthatDOTjava5crapATflareDASHonDOTcom. We then replace “DOT”, “AT”, and “DASH” with the corresponding character and get the key: firstname.lastname@example.org.
Challenge 3: Shellolololol
Challenge 3 is an x86 PE file. We drop the binary into IDA Pro to see what it shows us:
add esp, 14h
mov eax, [ebp+var_24]
mov eax, [ebp+var_20]
mov eax, [ebp+var_1C]
add esp, 0Ch
mov [ebp+Code], eax
mov eax, [ebp+Code]
The function sub_401000 looks interesting to us since all of the other functions called before it have symbols associated with them, and 0×401000 is the beginning of the code section, commonly where the beginning of any user-written code exists.
mov ebp, esp
sub esp, 204h
mov eax, 0E8h
mov [ebp+var_201], al
mov eax, 0
mov [ebp+var_200], al
mov eax, 0
mov [ebp+var_1FF], al
mov eax, 0
mov [ebp+var_1FE], al
mov eax, 0
mov [ebp+var_1FD], al
mov eax, 8Bh
After just a cursory look, we see single bytes being moved onto the stack one at a time. After we get past all of the bytes being moved onto the stack, we see the following:
lea eax, [ebp+var_201]
mov eax, 0
The binary is calling the location of the first byte it moved onto the stack, so we’ll need to deal with a buffer of shellcode. At this point static analysis is much more work than dynamic, so we drop this into a debugger.
We set a breakpoint at the call eax above and let the code run to catch the program before it calls into the shellcode. Now we can dump the stack memory to a file and analyze it in IDA Pro as shown in Figure 3. All of the following analysis could be done in the debugger, but we decided to show the steps in IDA Pro.
Figure 3: 0×66 decoding loop
Figure 3 shows a loop decoding everything after the jmp instruction by XORing each byte with 0×66. We decided to write a script to do the decoding for us rather than running it and dumping it in the debugger again.
loc = 0x1DA0
for i in range(0x1DF):
idaapi.patch_byte(loc+i, idaapi.get_byte(loc+i) ^ 0x66)
When we run this script we get the following decoded string:
00001DA0 aAndSoItBegins db 'and so it begins',0
Additional code that has also been decoded, showing another decoding loop:
00001DB0 push 'su'
00001DB5 push 'ruas'
00001DBA push 'apon'
00001DBF mov ebx, esp
00001DC1 call $+5
00001DC6 mov esi, [esp]
00001DC9 add esi, 2Dh ; '-'
00001DCC mov ecx, esi
00001DCE add ecx, 18Ch
00001DD4 mov eax, ebx
00001DD6 add eax, 0Ah
00001DD9 cmp eax, ebx
00001DDB jnz short loc_1DE2
00001DDD mov ebx, esp
00001DDF add ebx, 4
00001DE2 cmp esi, ecx
00001DE4 jz short loc_1DEE
00001DE6 mov dl, [ebx]
00001DE8 xor [esi], dl
00001DEA inc ebx
00001DEB inc esi
00001DEC jmp short loc_1DD9
This time the encoding is a multi-byte XOR, so we write another script:
loc = 0x1DF3
key = “nopasaurus”
for i in range(0x18C):
Scripts like this are often needed when reversing malware to decode strings used by the program. After this script executes it seems we’ve gotten further because we have another string that has been decoded:
00001DF3 aGetReadyToGetN db 'get ready to get nop',27h,'ed so damn hard in the paint
And following this we now have more code as shown in Figure 4.
Figure 4: GlOb decoding loop
What a surprise: another deccoding loop. By now, we’ve gotten pretty decent at writing these scripts, so here’s another one:
loc = 0x1E47
key = “bOlG”
for i in range(0x138):
After this one executes we have more code to look at. This is the last decoding step using the key of “omg is it almost over?!?” This time the script looks like:
loc = 0x1EA9
key = “omg is it almost over?!?”
for i in range(0xD6):
And we have our next key.
00001EA9 aSuch_5h3110101 db 'email@example.com
We could have come to the same conclusion by stepping through the whole binary in a debugger. But we wanted to have a bit of fun in IDA Pro scripting!
Challenge 4: Sploitastic
Challenge 4 requires that we examine a PDF. Let’s see what happens when we open this in an unpatched version of Adobe Reader that is highly exploitable, like 9.0 as shown in Figure 5.
Figure 5: Malicious PDF
It looks like the malicious PDF popped open a message box, so we set a breakpoint on MessageBoxA and MessageBoxW. Figure 6 shows the arguments on the stack when this breakpoint hits.
Figure 6: MessageBoxA arguments
From the strings that are on the stack, we know that we are in the correct call to MessageBoxA. So if we trace back to the address that made this call, we find the following shellcode block:
00000000 E8 00 00 00 00 8B 14 24 81 72 0B 16 A3 FB 32 68 .......$.r....2h
00000010 79 CE BE 32 81 72 17 AE 45 CF 48 68 C1 2B E1 2B y..2.r..E.Hh.+.+
00000020 81 72 23 10 36 9F D2 68 71 44 FA FF 81 72 2F F7 .r#.6..hqD...r/.
00000030 A9 A9 0C 68 84 E9 CF 60 81 72 3B BE 93 A9 43 68 ...h...`.r;...Ch
00000040 D2 A3 98 37 81 72 47 82 8A 62 3B 68 EF A4 11 4B ...7.rG..b;h...K
00000050 81 72 53 D6 47 C0 CC 68 BE 69 A4 FF 81 72 5F A3 .rS.G..h.i...r_.
00000060 CA 54 31 68 D4 AB 65 52 8B CC 57 53 51 57 8B F1 .T1h..eR..WSQW..
00000070 89 F7 83 C7 1E 39 FE 7D 0B 81 36 42 45 45 46 83 .....9.}..6BEEF.
00000080 C6 04 EB F1 FF D0 68 65 73 73 01 8B DF 88 5C 24 ......hess....\$
00000090 03 68 50 72 6F 63 68 45 78 69 74 54 FF 74 24 40 .hProchExitT.t$@
Examining this in IDA shows the following:
00000359 call $+5
0000035E mov edx, [esp]
00000361 xor dword ptr [edx+0Bh], 32FBA316h
00000368 push 32BECE79h
0000036D xor dword ptr [edx+17h], 48CF45AEh
00000374 push 2BE12BC1h
00000379 xor dword ptr [edx+23h], 0D29F3610h
00000380 push 0FFFA4471h
00000385 xor dword ptr [edx+2Fh], 0CA9A9F7h
0000038C push 60CFE984h
00000391 xor dword ptr [edx+3Bh], 43A993BEh
00000398 push 3798A3D2h
0000039D xor dword ptr [edx+47h], 3B628A82h
000003A4 push 4B11A4EFh
000003A9 xor dword ptr [edx+53h], 0CCC047D6h
000003B0 push 0FFA469BEh
000003B5 xor dword ptr [edx+5Fh], 3154CAA3h
000003BC push 5265ABD4h
000003C1 mov ecx, esp
000003C3 push edi
000003C4 push ebx
000003C5 push ecx
000003C6 push edi
So it looks like we have strings that are being encoded on the stack in some way. Since our breakpoint hit after this code has already run, we can go back and force the debugger to execute this code again to see what is revealed on the stack.
Figure 7: Breakpoint to reveal the key
And there it is in Figure 7 as referenced by ESP. So we’ve found the next key.
Challenge 5: 5get_it
Challenge 5 is a Windows PE DLL, so we drop it into IDA Pro and started jumping around the functions to see if anything interesting pops out. After bouncing around a bit, we stumble upon this huge function starting at 0×10001240. This function takes no arguments and appears to build a giant stack string. Since it takes no arguments, we tried setting eip to the entry of the function in a debugger and running it, which reveals the image shown in Figure 8.
Figure 8: Challenge 5 message box
Interesting, but it doesn’t seem to contain the key. Checking the cross references to this function leads us to the function shown in Figure 9.
Figure 9: Subroutine that calls the message box function
That doesn’t tell us much by itself. We see this function checks a global variable to determine whether to open a MessageBox, and then the letter “m” is returned. Cross references to this function show a function whose graph is shown in Figure 10.
Figure 10: “if” statement control function
This function is huge and contains some sort of if/else statement. At the top of it we see the following bit of code.
10009ECD movsx edx, [ebp+var_4]
10009ED1 cmp edx, 0DEh
10009ED7 jg loc_1000A3A4
10009EDD movsx eax, [ebp+var_4]
10009EE1 push eax
10009EE2 call ds:GetAsyncKeyState
10009EE8 movsx ecx, ax
10009EEB cmp ecx, 0FFFF8001h
10009EF1 jnz loc_1000A39F
An if/else statement based on GetAsyncKeyState sounds like we are dealing with a keylogger. It appears that pressing the “m” key causes a specific global variable to be set, which later causes the program to pop up a message box. So what is this global variable, dword_100194FC, and what sets it? Cross references to this are shown in Figure 11.
Figure 11: Cross references to dword_100184FC
The global variable is initialized to zero, and then some other function sets it to one. The function that sets it is sub_10009B60 and is shown in Figure 12.
Figure 12: “o” keystroke function
This appears to be the function that handles the keystrokes for the “o” key. The last condition checks if dword_100194F8 is set, and if so then set dword_100194FC to 1. Those global variables are in sequence, so from here we play on a hunch and look at the memory addresses of the global variables and start naming them as follows:
As we start doing this, a pattern starts to emerge:
We notice that the pattern “dotcom” emerge so we can do this for the rest of the global variables (which are a series of global variables controlling a state machine based on key strokes) and we get the final key: l0gging.Ur.firstname.lastname@example.org.