A Successful Yet Failed Flare ON Challenge - The Write-up

Introduction

Before I start going discussing how I solved challenges 1-9 (yes, I did not complete binary 10 or 11) I wanted to give my reasoning for doing a write-up despite not finishing. I had started challenge 1 with little expectation that I would make it very far (ie: finishing). Perhaps this is due to my feeling of not being “great” at reverse engineering (you’ll see lolz in my solutions) or because I simply assumed they would be too hard (the FLARE team is top notch). With that said… advancing as far as I did has left me with a greater level of confidence in my ability in RE and I am glad I pushed myself to learn new skills and tactics along the way! I do wish I would of had the time to mess with 10 as judging from other write-ups I might have gotten it!

Secondly, reading other write-ups has shown me that we all solved these challenges very differently and I wanted to put mine forth into the community to have more methodologies of obtaining the same result. I love this aspect of challenges like these and enjoy the knowledgeable bombs associated with write-ups. Reading some of them has shown me how little I actually understood on some of these at the internal level. Next year I will spend more time on the higher level logic instead of trying to do random things (you’ll see).

I also wanted to thank the FLARE team for providing such a fun set of binaries!

You’ll see in my solutions that I had crooked logic, some luck and overall some hilarious code! Like the FLARE team say though, “There is no such thing as cheating in malware analysis”.

Note: I apologize for the memes... but they suit my approaches. 

Challenge 1

Challenge 1 was a fun yet easy start as the e-mail will later show. The binary is i_am_happy_you_are_to_playing_the_flareon_challenge.exe. Me too. Let’s go.

Popping the binary into IDA we see it is not very complicated

/resources/posts/flare/babiesfirst.PNG

We need to view the byte_402140 location to grab the values we are comparing each character of our password to after the XOR.

.data:00402140 byte_402140     db 1Fh               
.data:00402141                 db    8
.data:00402142                 db  13h
.data:00402143                 db  13h
.data:00402144                 db    4
.data:00402145                 db  22h ; "
.data:00402146                 db  0Eh
.data:00402147                 db  11h
.data:00402148                 db  4Dh ; M
.data:00402149                 db  0Dh
.data:0040214A                 db  18h
.data:0040214B                 db  3Dh ; =
.data:0040214C                 db  1Bh
.data:0040214D                 db  11h
.data:0040214E                 db  1Ch
.data:0040214F                 db  0Fh
.data:00402150                 db  18h
.data:00402151                 db  50h ; P
.data:00402152                 db  12h
.data:00402153                 db  13h
.data:00402154                 db  53h ; S
.data:00402155                 db  1Eh
.data:00402156                 db  12h
.data:00402157                 db  10h

User input get XOR’d with 0x7d and compared to a byte at location byte_402140

Let’s write a Python script to solve! (I have IDA Demo so no IDAPython)

def xor_delta(s, key_len = 18):
   email = ''
   for x in s:
      email += chr(x ^ 0x7D)
   return email
      
list = [0x1f,0x8,0x13,0x13,0x4,0x22,0xe,0x11,0x4d,0xd,0x18,0x3d,0x1b,0x11,0x1c,0xf,0x18,0x50,0x12,0x13,0x53,0x1e,0x12,0x10]
print xor_delta(list)
C:\Users\Topher\Downloads\flare-on\1>xor.py
bunny_sl0pe@flare-on.com

C:\Users\Topher\Downloads\flare-on\1>i_am_happy_you_are_to_playing_the_flareon_challenge.exe
Let's start out easy
Enter the password> [email protected]
You are success

Challenge 2

Another console binary! This one, like the first, asks for a password!

C:\Users\Topher\Downloads\flare-on\2>very_success.exe
You crushed that last one! Let's up the game.
Enter the password> What Could It Be?

Opening the file in IDA I saw the main routine asking for the string and traced it into the decryption loop at sub_401084. I noticed it was using a lot of weird x86 instructions and saw the location were it would validate a character in our inputted password.

.text:004010C9                 cmovnz  cx, dx
.text:004010CD                 pop     eax
.text:004010CE                 jecxz   short loc_4010D7
.text:004010D0                 sub     edi, 2
.text:004010D3                 loop    loc_4010A2
.text:004010D5                 jmp     short loc_4010D9

loc_4010A2 causes the program to continue to traverse our password. Looking at the assembly closer I realized that eax is always xor’d with itself when a character is not in the right location… I’ll come back to this.

I also saw what the minimum length of the string needed to be based off of a counter and that the EDI register is always subtracted by 2 when a character matches

.text:0040108E                 mov     ecx, 25h

.text:004010D0                 sub     edi, 2

Tracing back to the applications entry point we see at the first call we obtain a series of bytes as our saved EIP on the stack and that value is later popped into EAX, which will eventually become “EDI” in our decryption loop.

At this point I knew enough about the program and decided to take a brute force approach by patching the binary! I replaced the xor which will be our return value (0) when the program exits, telling us are failure, to take in the value in EDI instead! Knowing EDI decrements every time we get a character correct, we can use that to build a forcer.

.text:004010D7                 xor     eax, eax

becomes

.text:004010D7                 mov     eax, edi

I also had to patch some bytes in sub_401000 in order to return the value of EDI

.text:00401067                 test    eax, eax
.text:00401069                 jz      short loc_401072
.text:0040106B                 push    offset aYouAreSuccess ; "You are success\r\n"
.text:00401070                 jmp     short loc_401077

becomes

.text:00401067                 mov     esp, ebp
.text:00401069                 pop     ebp
.text:0040106A                 retn

and my python script to do this was

from subprocess import Popen, PIPE
import time

def edi(string):
   child = Popen(["very_successEdited.exe"], stdin=PIPE)
   res1 = child.stdin.write(string)
   time.sleep(.5)
   streamdata = child.communicate()[0]
   returnEDI = child.returncode
   return returnEDI

savedRc = edi("0000000000000000000000000000000000000") # get inital EDI
keys = "abcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ@-.$"
string = ""
savedString = ""
while True:
   for c in keys:
      string = savedString + c
      string = string.ljust(37, '0') #string size program expects
      rcCur = edi(string)
      print "\n[+] string = " + str(string) + " : " + str(rcCur)
      if rcCur < savedRc: #EDI has decremented.. we have a character!!
         savedRc = rcCur
         savedString = savedString + str(c)
         string = savedString
         print "\n [+]current string is " + str(string)
         continue

and eventually I got the password!

C:\Users\Topher\Downloads\flare-on\2>flare2.py
You crushed that last one! Let's up the game.
Enter the password> You crushed that last one! Let's up the game.
Enter the password>
[+] string = a000000000000000000000000000000000000 : 4198664

 [+]current string is a
 
[SNIP]

[+] string = a_J0000000000000000000000000000000000 : 4198663
You crushed that last one! Let's up the game.
Enter the password>
[+] string = a_K0000000000000000000000000000000000 : 4198663
You crushed that last one! Let's up the game.
Enter the password>
[+] string = a_L0000000000000000000000000000000000 : 4198662

 [+]current string is a_L

See what I meant about my methods being crude? :) Yeah yeah I know this is a horrible way to solve this one (or is it?), but hey it worked!

Challenge 3

Good ol’ Elfie…

/resources/posts/flare/elfie.PNG

Knowing this binary, elfie.exe, was a python compiled exe (you can tell by the icon in this case) I ran pyinstxtractor on it to get the source files.

C:\Users\Topher\Downloads\flare-on\3>pyinstxtractor.py elfie.exe
Successfully extracted Pyinstaller archive : elfie.exe

Performing this action lead to an awful lot of files… in the “out00-PYZ.pyz_extracted” directory that pyinstxtractor left me I noticed the file “elfie” which when opened is a giant blob of garage!

import base64

O0OO0OO00000OOOO0OOOOO0O00O0O0O0 = 'IRGppV0FJM3BRRlNwWGhNNG'
OO0O0O00OO00OOOOOO0O0O0OOO0OOO0O = 'UczRkNZZ0JVRHJjbnRJUWlJV3FRTkpo'
OOO0000O0OO0OOOOO000O00O0OO0O00O = 'xTStNRDJqZG9nRCtSU1V'
OOO0000O0OO0OOOOO000O00O0OO0O00O += 'Rbk51WXI4dmRaOXlwV3NvME0ySGp'
OOO0OOOOOOOO0000O000O00O0OOOO00O = 'ZnJvbSBQeVNpZGUgaW1wb3J'

[SNIP]

import base64
exec(base64.b64decode(OOO0OOOOOOOO0000O000O00O0OOOO00O + O0O00OO0OO00OO00OO00O000OOO0O000 + O00OO0000OO0OO0OOO00O00000OO0OO0 + [SNIP] ))

Knowing this was base64, I just added the result into a string and printed it instead of having the program actually run itself

what = base64.b64decode(OOO0OOOOOOOO0000O000O00O0OOOO00O + [SNIP]

print str(what)

Now having this, I ran elfie as a python script

C:\Users\Topher\Downloads\flare-on\3>mv elfie elfie.py

C:\Users\Topher\Downloads\flare-on\3>elfie.py > whatNow.py

and obtained another massive file!

Getting lucky and going to the bottom of the file I saw it (on line 15)!

reversed('[email protected]')))):

which reversed is our key,

Elfie.L0000ves.YOOOO@flare-on.com

bonus fun: changing the code on line 11 (notepad++) of

self.OOOOOOOOOO0O0OOOOO000OO000OO0O00 = False
       

to

self.OOOOOOOOOO0O0OOOOO000OO000OO0O00 = True

gives us success elfie without telling elfie our password

/resources/posts/flare/best.PNG

Challenge 4

Getting the binary, youPecks.exe we can see it tells us 2+2=4

C:\Users\Topher\Downloads\flare-on\4>youPecks.exe
2 + 2 = 4

Using peparser, a script I wrapped around pefile (a crummy one I made when I was bored one evening during my undergrad… surprised it came in handy), we can also see that the binary is packed with upx.

C:\Users\Topher\Downloads\flare-on\4>peparser.py -f youPecks.exe -v | grep IMAGE_SECTION -A 1
[IMAGE_SECTION_HEADER]
0x1F8      0x0   Name:                          UPX0

Knowing that, I didn’t even bother trying “upx -d” (I already heard not to) and just went straight to unpacking it manually… this is where I became immensely confused. Not because I can not manually unpack upx, but because I could not get OllyDbg to do it! I eventually was able to unpack using Immunity, but I still could not figure out what to do inside the binary in IDA.

I then just ran the binary in OllyDbg, broke at the point where it was fully unpacked and looked to see what I could find with my eye.

/resources/posts/flare/bp1.PNG

Once the binary was unpacked in OllyDbg, I started looking around in memory for something.. anything. I found stuff.

/resources/posts/flare/stuff2.PNG

/resources/posts/flare/stuff.PNG

and set some breakpoints.

Nothing was happening so I decided to throw in an argument to the program

/resources/posts/flare/args.PNG

Lo and behold, I did get further in! At this point I just felt pure luck (especially after reading other write-ups).

I was now looking at all of the base64 strings

/resources/posts/flare/args.PNG

At this point I took a step back and realized that there were 24 base64 strings

K7IfRF4nOiNn9Jsqt9wFCq==
vAvack0BPyMQiq0MkChFqq==
NMImwkviE46VACNHafRqVW==
HMzOnqAQZzalVvP0Re7FAa==
7h9+E7q3qS6gGux3htE1pa==
I7BbEdHKp5ArZgPn5Suxcq==
bUYHTdFhKeZdZMvgYbebea==
IEDozaUmrIv6kD4gfNLnxq==
4RQqw/mg9g+SOIptYYdIZW==
xNmQghI+i0lB/V9F48PAOW==
AlmP2PIt40czX9ITxlNjqa==
e8J/2xCbnWoNaC+oeD6Szq==
wmIvyVwp0NB1KKiaAnUmcq==
3lM+l2boxFKD65zzVTr0Jq==
tE2YjaOEdWonZCIZ3PiMta==
2dHPhL1k0gH5YNiuqUId1a==
AZg9+N+B/S4Mm4h/QrVwQq==
r+1Zo40qVIjEZRO0tvm1HG==
QerwgAVqJZUG6/YZeyp3+q==
/+uDpN2cLYn1Ihbo7DXQSG==
fFqAlPA640hD5iw7dNJ0Hq==
9AFKD80WqRsAKixwiWFnka==
V21SGz7jDBbdRSucfNW9fq==
Hp8u+Kw+pkrZNNWcDXELqq==

and I was validating against all of them while I was single stepping.. so, I wrote another forcer! :)

import os
num = 0
while True:
   os.system("youPecks.exe "  + str(num))
   time.sleep(.5)
   num+=1
C:\Users\Topher\Downloads\flare-on\4>youPecks.py
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
2 + 2 = 4
Uhr1thm3tic@flare-on.com
2 + 2 = 4
2 + 2 = 4

I know, I can’t believe that worked either. Reading write-ups I now understand it wanted the system date! While doing this write-up it is currently 5:20 PM so it makes sense the 17th run I got the key.

/resources/posts/flare/bruteMe.jpg

Challenge 5

This challenge gave us a challenge.pcap file and sender.exe.

Going immediately to the pcap I followed the TCP streams one at a time (there were 12). I did this manually in WireShark as I just wanted to get whatever it was that was hiding in there.

/resources/posts/flare/wireshark.PNG

And on and on until I got the base64 value

UDYs1D7bNmdE1o3g5ms1V6RrYCVvODJF1DpxKTxAJ9xuZW==

Anytime I get a base64 string I always decode it just to see what it is and in this case, it was nothing useful.

C:\Users\Topher>echo UDYs1D7bNmdE1o3g5ms1V6RrYCVvODJF1DpxKTxAJ9xuZW== | base64 -d
P6,╘>█6gD╓ìαµk5Wñk`%o82E╘:q)<@'▄ne

Thinking that sender.exe was creating this encoded value, I opened it in IDA.

The first thing I noticed was that sender.exe wanted to open a file, key.txt and read its contents into a buffer.

/resources/posts/flare/readme.PNG

I figured that key.txt would contain the e-mail.

Continuing to do some quick analysis in IDA I came across the subroutine at sub_401000 and saw that it was performing the HTTP requests that the pcap file had.

Note: Anything I come across a binary performing actions such as this 
(using win API InternetOpen, InternetConnect, HttpOpenRequest) I try to run it and see what I can observe.

Having the knowledge I had I figured this binary could also be brute forced. (I will later learn this was an awful approach, but bear with me).

I found an easy to use HTTP mockserver in java that I could stand up on my machine to have sender.exe properly send HTTP requests, which contained the encoded base64 data. The one I went with was http://www.mock-server.com/where/downloads.html. I liked this as it puts all HTTP traffic to a log file!

Starting up the mockserver, I began to fill key.txt with strings until I knew the length my input needed to be for the base64 encoding. Each time I ran sender.exe I read the mockserver logs to see how many bytes long the base64 string was.

C:\Users\Topher\Downloads\flare-on\5>cat key.txt
a@flare-on.com
C:\Users\Topher\Downloads\flare-on\5>java -jar mockserver-netty-3.9.17-jar-with-
dependencies.jar -serverPort 80
2015-09-12 14:46:09,824 INFO o.m.m.MockServer MockServer started on port: 80

and then running sender.exe mockServer fills with log entries!

2015-09-12 14:50:03,245 INFO o.m.m.MockServerHandler returning response:

        {
          "statusCode" : 404
        }

 for request:

        {
          "method" : "POST",
          "path" : "/",
          "headers" : [ {
            "name" : "User-Agent",
            "values" : [ "Mozilla/5.0 (Windows NT 6.1; WOW64) KEY" ]
          }, {
            "name" : "Host",
            "values" : [ "localhost" ]
          }, {
            "name" : "Content-Length",
            "values" : [ "4" ]
          }, {
            "name" : "Cache-Control",
            "values" : [ "no-cache" ]
          } ],
          "keepAlive" : true,
          "secure" : false,
          "body" : "4Di="
        }

Because I knew the e-mail had to end with @flare-on.com I only had to guess the number of characters to the left. I continued to do this until the base64 string ended with ZW==.

Doing so, I came back with the string

C:\Users\Topher\Downloads\flare-on\5>cat key.txt
aaaaaaaaaaaaaaaaaaaaa@flare-on.com

My terrible idea was then that I could brute force the key, grep the output of the mockServer and attempt to brute the key 3 characters at a time (in order to find one of the 4 byte base64 values as I knew I needed 3 chars of input for 4 bytes of base64)

I quickly realized after trying this for about 5 minutes of forcing that it would take FAR too long and I would be sitting for days trying to brute the key this way.

My python script to do this, for the lolz (it is awful), was

from subprocess import Popen, PIPE

keys = "abcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ@-."
f = open("key.txt", 'w')
while True:
   for c in keys:
      for d in keys:
         for e in keys:
            string = c+d+e
            f.seek(0)
            f.write(string)
            child = Popen(["sender.exe"]).wait()
            print "\n[+] string = " + str(string)
            for line in open("bruted.txt"): #where mockServer is logging to after grepping the body value out
               if ("UDYs" in line):
                  print line
                  print "\n [+]current string is " + str(string)
                  exit()

So, I tried to think of something else… I went back into IDA to look for more information.

I located sub_401250 by tracing where the input buffer from key.txt was going. In this subroutine there is a lot of shifting of the input string.

What I then decided to do was rewrite this subroutine in inline assembly in a c++ program and see what I could do. I also traced a couple of registers that I needed to know of and placed them into my inline asm.

Note: The following c++ code is some of the worst I have 
ever written and I by no means condone using it for any reasons! 
I say this because I could not functionalize the inline ASM without crashing :(
int main()
{
	int returnVal = 0;
	char * val = "abcdefghijklmnopqrstuvwxyz_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ@-.";
	char * flarebearstare = "flarebearstare";

	for (unsigned int i = 0; i<strlen(val); i++)
	{
		for (unsigned int j = 0; j<strlen(val); j++)
		{
			for (unsigned int k = 0; k<strlen(val); k++)
			{
				char guess[] = "[email protected]"; //changed each time I got 3 characters 


				//change these each run ++ ie guess[0] -> guess[3]

				guess[0] = val[i];
				guess[1] = val[j];
				guess[2] = val[k];
				char *buffer = guess;

				__asm
				{
					pushad;
					mov edx, 0x23; //from sub_401100

					mov ecx, buffer;//from sub_401100

					mov edi, edx;
					xor esi, esi;
					mov ebx, ecx;
				loclea:
					lea ecx, [ecx + 0];
				loc_401260:
					mov     eax, 0x24924925;
					mul     esi;// starts at 0

					mov     eax, esi;
					sub     eax, edx;
					shr     eax, 1;
					add     eax, edx;
					shr     eax, 3;
					lea     ecx, ds:0[eax * 8];
					sub     ecx, eax;
					mov     eax, esi;
					add     ecx, ecx;
					sub     eax, ecx;
					push edx;
					mov edx, flarebearstare;
					mov     al, [edx + eax];
					pop edx;
					add[esi + ebx], al;
					inc     esi;
					cmp     esi, edi;
					jb      loc_401260;
				done:
					//mov eax, [buffer];

					popad;

					//ret;

				}
				std::string amIRightNow = base64_encode((unsigned const char *)buffer, 34);

				//3 bytes of input for 4 bytes of base64 :) put here for reference

				char *hash = "UDYs1D7bNmdE1o3g5ms1V6RrYCVvODJF1DpxKTxAJ9xuZW==";
				if (amIRightNow.find("UDYs") != std::string::npos)
				{
					printf("Found with %c, %c, %c", val[i], val[j], val[k]);
				}
			}
		}
	}
}

base64_encode was just an off the shelf encoder I pulled from cplusplus.com here as I did not want to code one myself at this moment in time.

Note in the code I had already found flarebearstare and had noticed it was being referenced in the subroutine in question.

I then proceeded to run my brute forcer! I noticed immediately that the base64 coming out of mine was switched with the pcaps in that it was lowercase instead of uppercase!

So I made those changes

char *hash = "udyS1d7BnMD1O3G5MS1v6rRycvVodjf1dPXktXaj9XUzw==";
if (amIRightNow.find("udyS") != std::string::npos)

Now I was on the right track and I was brute forcing the string 3 characters at a time!

Eventually I got the whole e-mail (which is very clever)

Challenge 6

Ah an Andriod .apk file… Should have guessed based off of the last e-mail!

You saved the internet with that one! There is more to reversing than just Windows programs though. 
Cell phones and tablets are now on the information superhighway. 
We want to see what can do with an Android app. 
I've attached the next challenge. 
The password to the zip archive is, as always, "flare".

Be the hero this challenge deserves!

-FLARE

Alright, well we can just unzip the .apk file.

C:\Users\Topher\Downloads\flare-on\6>unzip android.apk
Archive:  android.apk
  inflating: AndroidManifest.xml
  inflating: res/anim/abc_fade_in.xml
  inflating: res/anim/abc_fade_out.xml
  inflating: res/anim/abc_grow_fade_in_from_bottom.xml
  inflating: res/anim/abc_popup_enter.xml
  inflating: res/anim/abc_popup_exit.xml
  inflating: res/anim/abc_shrink_fade_out_from_bottom.xml
  inflating: res/anim/abc_slide_in_bottom.xml
  inflating: res/anim/abc_slide_in_top.xml

Afterwards I was instantly attracted to the lib\armeabi directory and the libvalidate.so library as I figured this was not going to be another “find the magic in the source for Elfie” type challenge.

The function Java_com_flareon_flare_ValidateActivity_validate was too obvious so I started looking there. Opening this in IDA left me confused, scared and anxious! I did my best to look at the ARM disassembly and to my dismay was unable to get very far. I located the tables it was referencing at .data:00005004 off_5004 and the bigger table at .data:00007E7E unk_7E7E but was unable to put it all together.

I decided to try and attempt to live debug the andriod app and installed the andriod dev kit, gdb plugins and more but was unable to get very far and realized this was the wrong approach. (This was about a week at this point on #6)

I managed to obtain the decompiled form of the ASM off the internet and this began to make sense.

int __fastcall lolwut(int a1, int a2, int a3)
{
  int v3; // r5@1

  const char *v2; // r0@1

  const char *v1; // r6@1

  int (__fastcall *v6)(int, const char *); // r3@4

  int v7; // r0@4

  const char *v8; // r1@4

  unsigned int v4; // r4@5

  int v10; // r7@6

  size_t i; // [sp+0h] [bp-1BB8h]@3

  int var_1BB4; // [sp+4h] [bp-1BB4h]@3

  int v14; // [sp+8h] [bp-1BB0h]@1

  signed int v15; // [sp+Ch] [bp-1BACh]@3

  unsigned int v16; // [sp+10h] [bp-1BA8h]@7

  char dest[92]; // [sp+1Ch] [bp-1B9Ch]@1

  char s; // [sp+78h] [bp-1B40h]@1


  v3 = a1;
  v14 = a3;
  j_j_memset(&s, 0, 6952);
  j_j_memcpy(dest, &off_5004, 92);
  v2 = (const char *)(*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v3 + 676))(v3, v14, 0);
  v1 = v2;
  if ( v2 && j_j_strlen(v2) <= 46 )
  {
    var_1BB4 = 0;
    v15 = 1;
    for ( i = 0; i < j_j_strlen(v1); i += 2 )
    {
      j_j_memset(&s, 0, 6952);
      v4 = 0;
      if ( v1[i] )
      {
        v4 = v1[i];
        if ( v1[i + 1] )
          v4 = (unsigned int)&unk_7E7E >= ((v1[i] << 8) | (unsigned int)v1[i + 1]) ? (v1[i] << 8) | v1[i + 1] : 0;
      }
      v10 = 0;
      do
      {
        v16 = *(_WORD *)((char *)&unk_2214 + v10);
        while ( !(v4 % v16 & 0xFFFF) )
        {
          ++*(_WORD *)(&s + v10);
          v4 = v4 / v16 & 0xFFFF;
          if ( v4 <= 1 )
            goto LABEL_10;
        }
        v10 += 2;
      }
      while ( v10 != 6952 );
LABEL_10:
      if ( j_j_memcmp(*(const void **)&dest[4 * var_1BB4], &s, 3476) )
        v15 = 0;
      else
        ++var_1BB4;
    }
    (*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)v3 + 680))(v3, v14, v1);
    v6 = *(int (__fastcall **)(int, const char *))(*(_DWORD *)v3 + 668);
    v7 = v3;
    if ( var_1BB4 == 23 && v15 )
      v8 = "That's it!";
    else
      v8 = "No!";
  }
  else
  {
    (*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)v3 + 680))(v3, v14, v1);
    v6 = *(int (__fastcall **)(int, const char *))(*(_DWORD *)v3 + 668);
    v7 = v3;
    v8 = "No!";
  }
  return v6(v7, v8);
}

This is where I really started pushing myself. With a small amount of help I was able to take the decompiled code and transform it into easier to read c++ code that could be used to brute force the key.

It took awhile but I got some usable c++ code and wrote a small forcer

char table[] = {};
char char_table[] = {};

bool check_email(char* email)
{
	char s[6952] = { 0 };
	char byte_7E7E[2098] = { 0 };
	int curr_value = 0;
	int table_pos = 0;
	int v15 = 1;
	unsigned int test_value = 0;
	int v13 = 0;
	char dest[0x5c] = { 0 };
	int i;
	for (i = 0; i < 46; i += 2)
	{
		memset(s, 0, 0x1B28u);
		curr_value = 0;
		if (email[i])
		{
			curr_value = email[i];
			if (email[i + 1])
				curr_value = (unsigned int)&byte_7E7E >= ((email[i] << 8) | (unsigned int)email[i + 1]) ? (email[i] << 8) | email[i + 1] : 0;
		}
		table_pos = 0;
		do
		{
			test_value = *(WORD *)((char *)&table + table_pos);

			while (!(curr_value % test_value & 0xFFFF))
			{
				++*(WORD *)&s[table_pos];
				curr_value = curr_value / test_value & 0xFFFF;
				if (curr_value <= 1)
				{
					goto LABEL_10;
				}

			}
			table_pos += 2;
		} while (table_pos != 6952);

	LABEL_10:

		if (memcmp(&char_table, s, 0xD94u))
		{
			return 0;
		}
		else
		{
			printf("%c%c\n", email[i], email[i + 1]);
		}
	}
}

static const char alphabet[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"-_@+=.#-_~!$&'()*+,;=";
static const int alphabetSize = sizeof(alphabet) - 1;

int main(void)
{
	char guess[] = { "[email protected]" };
	for (int i = 0; i < alphabetSize; ++i)
	{
		guess[0] = alphabet[i];
		for (int j = 0; j < alphabetSize; ++j)
		{
			guess[1] = alphabet[j];
			check_email(guess);
		}
	}
	return 0;
}

To populate table i just copied and pasted the huge byte dump from IDA. For char_table (the one that changes with each successive character match) I wrote a small python script after noticing that each one of them was 0x1b27 bytes long to copy them from the binary and into my clipboard for ease of pasting into my c++ program!

file = open("libvalidate.so", "rb")
file.seek(0x21E08-0x1000)#Location in binary to grab a table from... offset is 0x1000 from IDA address. 
data = file.read(0x1b27)
hex = ",0x".join("{:02x}".format(ord(c)) for c in data)
import pyperclip
pyperclip.copy(hex)
spam = pyperclip.paste()

Repeatedly performing these actions slowly gave me something that looked like the key, but I was missing something…

Should_havaog0ne_to_tashi_**[email protected]

The “”s above were characters I could not brute. I realized my alphabet must not be big enough so I added some more characters *=.#-_~!$&’()+,;=*. Doing this resulted in

which still did not seem like it was right. Going back to the block with “ao” I realized there were multiple chars that matched and obtained the full key

Challenge 7

I did this write-up before thinking I would do the full one and it is here.

Challenge 8

This challenge gave us a binary, gdssagh.exe, telling us

C:\Users\Topher\Downloads\flare-on\8\gdssagh.exe
the one who seeks finds...

Okay FLARE Team… what are you saying?

Opening the binary in OllyDbg showed a large chunk of base64 in the code section.

Naturally, I pulled it out and decoded it!

string = "all that stuff" #Replaced for brevity
newstr = string.replace(".", "") # no "." in base64!
print newstr
C:\Users\Topher\Downloads\flare-on\8>base64.py | base64 -d > what

C:\Users\Topher\Downloads\flare-on\8>file what
what: PNG image data, 600 x 480, 8-bit/color RGB, non-interlaced

Okay, so we have a PNG image!

/resources/posts/flare/what.PNG

How pretty!

I had never played with stenography before and was completely baffled!

I tried doing silly things like running strings.exe on it, opening it in photo editors to see if the string was embedded in the photo and was getting nowhere.

/resources/posts/flare/what-sorcery-is-this.jpg

Flailing to figure this out, I discovered Zsteg. This is where I really started to overthink the challenge… being jacked on caffeine did not help at this point, either. I instantly ran Zsteg with the -a flag to perform all possible methods of finding me what I needed.

C:\Users\Topher\Downloads\flare-on\8>zsteg -a what.PNG
imagedata           .. text: "\r\t(%%*,&"
b1,r,msb,xy         .. file: Applesoft BASIC program data, first line number 64
b1,rgb,msb,xy       .. file: MS-DOS executable
b1,bgr,lsb,xy       .. file: GLS_BINARY_LSB_FIRST
b2,rgb,msb,xy       .. text: "UDDADPAE"
b2,bgr,msb,xy       .. text: "|IAEQ@DDD"
b4,r,msb,xy         .. text: "Ab@pT&we-b e"
b4,g,msb,xy         .. text: "%`$Q\"wTf@"
b4,b,msb,xy         .. text: "C$qFqgf#0wpq"
b4,rgb,msb,xy       .. text: "BcrpAPpv#"
b4,bgr,msb,xy       .. text: "@CrbqP@v s"
b6,g,lsb,xy         .. text: "iI0jH&\nJ:"

Not realizing that I needed was right there the whole time, I continued to perform similar actions until going insane.

/resources/posts/flare/why.jpg

I then RTFM’d the git README for Zsteg and realized what I needed to do…

C:\Users\Topher\Downloads\flare-on\8>zsteg b1,rgb,msb,xy what.PNG -v
	[SNIP]
b1,rgb,msb,xy       .. file: MS-DOS executable
    00000000: 4d 5a 90 00 03 00 00 00  04 00 00 00 ff ff 00 00  |MZ..............|
    00000010: b8 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
    [SNIP]
    
C:\Users\Topher\Downloads\flare-on\8>zsteg -E b1,rgb,msb,xy what.PNG >iSeeWutYouDid.exe

C:\Users\Topher\Downloads\flare-on\8>strings iSeeWutYouDid.exe | grep flare
Im_in_ur_p1cs@flare-on.com

Challenge 9

The last challenge I managed to solve! This one was a trip considering I have IDA Pro Demo and have limited amounts of debugging I can do (it stops after n amount of steps).

The binary, you_are_very_good_at_this.exe, starts with a snarky message for us

I have evolved since the first challenge. You have not. Bring it.
Enter the password>

Not even wanting to input a password as a guess, I ran to IDA. It was instantly noticeable that the binary was going to deobfuscate itself due to the large amount of random bytes in the code section.

/resources/posts/flare/alldat.PNG

Starting up the IDA Demo debugger, I began to step through the code allowing it to fix itself as I progressed. It took me awhile, but I realized the code would build instructions on the stack and execute them. Realizing this, I came across an interesting instruction I had never seen before

Stack[00001E3C]:0018FDC0 cmpxchg bl, dl

With a quick Google I came across this slide deck.

What an interesting instruction… I’ll post the important parts of the slide deck below

– If (accumulator == destination)
{ ZF  1; destination  source; }
– If (accumulator != destination)
{ ZF  0; accumulator  destination; }

TL;DR; the value in “eax” at the time this instruction is called is important and is going to be compared to the value of a character in our string it expects. We need eax to be the proper character… more on this later.

Realizing this I then saw two more instructions,

Stack[00001A84]:0018FDCC xor     al, ah
.text:00401B14 rol     al, cl

that were also playing with that input character.

I also noticed at this time that the value in ah and cl were important to the decoding and began to make a list of their values on each loop.

My C++ code to perform the decoding (for each round of bytes) was

unsigned int __stdcall xorHexChar(int toPass)
{
	__asm
	{
		mov eax, toPass;//what the cmpxchg expects

		xor al, 0xc9; //xor al, ah; Stack[00001A84]:0018FDCC xor     al, ah

		rol al, 0xd4;//rol al, cl FROM .text:00401B14 rol     al, cl

	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	int i = 0x21; //"!" in ASCII table to start since #7 used a "$"

	int result = 0;
	int toPass = 0;
	for (i; i <= 0x7b; i++)
	{
		toPass = i & 0xff; //get bits 0-7

		result = xorHexChar(toPass);
		if (result == 0x5a) //result of .text:00401B16 mov     ebx, [esp+ebx+2Ch] use BL

		{
			printf("%c", i);
		}
	}
}

To show how this worked, I prepared a video… (Yes, I know it says FLARE8 in it… I was so excited I did not name it properly!)

/resources/posts/flare/howIDidFlare9.mp4

I know what you are thinking at this point…

/resources/posts/flare/buthow.jpg

Conclusion

Despite only being able to solve through challenge 9, I feel I accomplished a lot with these challenges! Judging from the completion rate by stage the FLARE team released

I actually performed fairly well! Being one of the 277 finishers of 9 feels great!

Not finishing is disappointing but the concepts, tools and struggles I got out of this is worth more than a sweet prize. . .

. . .

. . .

even if that prize was really, really, really cool.

Next year I am coming into the 3rd annual challenge with the urge of redemption! I say to the FLARE team

Written on September 12, 2015