Tuesday, September 8, 2015

FLARE On 2015 Walkthrough

Introduction


This year the FLARE On challenge came much longer and harder than the last year's with a lot of pitfalls that slow your progress. The challenge this year is composed of eleven levels, starting with easy ones and then getting harder as you move on. In this walkthrough I will try to keep it short as possible while highlighting the pitfalls of every level.

I recommend that you download the attached files (includes the challenge files) before reading the walkthrough from the following link,
http://www.mediafire.com/download/uadix5eq88rylon/Flare_On_2015_Solution.zip

I hope you will have fun reading the walkthrough!

Level 1



First Level is simply XORing the input string with 0x7D then compare it with constant 24 bytes.
XORing the constant 24 bytes will yield our first email bunny_sl0pe@flare-on.com


Level 2


Level two is pretty straight forward too. The input length must be equal to 37 then the following algorithm is executed,



The algorithm is simple. I had commented some lines to guide you through and the following is an equivalent code

a=0;
for( int i=0;i<0x25;i++)
{
unsigned char c = ((Email[i]^0xC7) + (1 << (a&3)) + 1) & 0xFF;
a = (a+c);
if (c != Table[0x25-i])
{
printf ("Wrong Input");
}
}

The reverse of this algorithm is simply by reversing the addition first by using subtraction then reversing the XOR with a XOR.
The reverse/decryption function is as follows

unsigned char Table[] = {0xFF,0xAF,0xAA,0xAD,0xEB,0xAE,0xAA,0xEC,0xA4,0xBA,0xAF,0xAE,0xAA,0x8A,0xC0,0xA7,0xB0,0xBC,0x9A,0xBA,0xA5,0xA5,0xBA,0xAF,0xB8,0x9D,0xB8,0xF9,0xAE,0x9D,0xAB,0xB4,0xBC,0xB6,0xB3,0x90,0x9A,0xA8};
unsigned int a = 0;
//Reverse
for( int i=0;i<0x25;i++)
{
Email[i] = (Table[0x25-i] - (1 << (a&3)) - 1) ^ 0xC7;
a = (a+Table[0x25-i]); //& 0xFFFF;
}
Finally the email is a_Little_b1t_harder_plez@flare-on.com

Level 3




I got that cute animal's image after running level three. The challenge file size is 12 MB which is weird.




It drops the above files to temp folder, as you can see it's a python executable. The challenge used PyInstaller to create a python executable, that's why the challenge file size is large.
To unpack the the executable I used pyinstxtractor.py by extremecoders.

The following files has been extracted,



We will be interested in elfie.



As you can see the elfie file is obfuscated, but fortunately we can deobfuscated it easily by replacing the last line with

Stage1Deobfuscated = base64.b64decode(OOO0OOOOOOOO0000O000O00O0OOOO00O + O0O00OO0OO00OO00OO00O000OOO0O000 + O00OO0000OO0OO0OOO00O00000OO0OO0 + O00OO00000O0OOO0OO0O0O0OO0OOO0O0 + O0O00OO0O0O0O00OOO0OOOOOO00OO0O0 + O0OO00O0000OOOO00OOO0OO0000O0OO0 + O0O0OO000OOO00000OO0OOOO0OO00000 + OO0OOO0O00000O00OOOOO0OO0OO00OOO + O000000000OOO00OO00000OOOO00OOOO + OOO0OOO00O0OO0O0OOOO00OO00OO0O00 + O0OO0OOO00O0OOO0O00OOO0O0OOOO00O + OOOO000O0OOOOO0O0O0000O0O0OO00O0 + O00O0O00OO0O0OO00O0OO0O00O0O00OO + OOOOOOO0O0O00OOO0OO00O0O00OOOOO0 + O00O000O0O00000O00OO0OO0OOO000O0 + O0O000O0O0O0OO0000O0O0OOO0OOOOO0 + O000000OOO0O00OO00OO00OO0OO00O0O + OOOOOO000OOO0O000O0O00OO0OOOO0O0 + O00OOOO0OO0O000OO0OOOOO0OOOO0000 + O0OO00000OOOOO0OOO000O00000OOO00 + OOO00O0OOOOO00OO0OOOOO0O0O00O0O0 + OOO000O0OOO0OOO0OOOO0OOOOO0O0O00 + OO0OOOO0O00OOOO0OOOO0O0OOO0OO0O0 + O0OO0OO00000OOOO0OOOOO0O00O0O0O0 + OO00OOO00OO0OOO000O000000O0O0000 + OO0O0000OO0000OO000OO0O0OO0O00OO + OO00000O0OO000O0O000OOOOOOO0O0OO + O0O00O000OOOO000O0OOOOO0O000000O + O000OOOOOOOOOO0O0OO0OO000OO000O0 + O0OO00OOO0O0OOOO0O0O0000000O0OOO + O0000O0OOOO00OO0OOO00OO000O0000O + OOOOO0OO0O00OOOOOO00O00000O0OO0O + OO0O0OOOO0O000OOOO00O0O00O0O0O0O + O0OOO00OOOO0OO0OO0O000O0OO0OO000 + O0OOOO0O00OOOOO0OOO000O00O00OO00 + OO0OO00000O00O0000000O00O0OO0O00 + OOO0000OO00000OO00O0OO0000OOOO00 + O00O000O00O0O00OO0OO0O000000OOOO + O000O00O000O00O000O0OO00O0000O0O + O0O0OO0OO0O0OOO0O0OOO00O0OOOOO00 + O0OOO0OO00OO0OOO00OO0O0O0O0O00OO + OO0OOO00000OO000O0OOOO00O0O0O00O + O00OO00OOO0OOOO0OOOO0OO00000OOO0 + OOOOOO0OO0O0OO0O0000OOO0O00O0O0O + O0O0O00O0000O00OOOO000O00OO00O00 + OOOOO0000OO000O0O0000OOOOO0000OO + OO00OOO0O00OO0OO0OOOO000OO0000OO + O00000O0OO00O0OO00000O000OOO00O0 + O0OOO00O0O0O0OO00000OO0OO00O00OO + O0000OO00000000OO000O0OOO000OO00 + OO0OO0O00000O0O000OOO0O0O0O000O0 + OOOOO0O00OOOOO0O0OOOOOOO0OO0OO00 + OOOOO00O0O0O0O0O0OO00O0OOOO00O0O + O0OOOO0OO000OOOOOO0O0OO0OOO0O000 + OOOO000OOOOO00000O000OO0O00O0O0O + O00OOO000O0O0OOOO00O0O00O0OO00OO + OO0O00OO0OO00O0O000O0000O0OOOOO0 + OO0O0O00OO00OOOOOO0O0O0OOO0OOO0O + OO0OOO00OOO00OOOOOOOOOOOO00OO00O + OOO0000O0OO0OOOOO000O00O0OO0O00O + OOO0O00O00OOOOOOO00OOOO0000O0O00 + O0O00OO00O0O00O0O00O0OOO00O0O0OO + O00OOOOO000O00O0O00000OOO0000OOO + O0O0OOO000O000OO0O0O0OOOOO0OO000)
f = open('Stage1Deobfuscated', 'w')
f.write(Stage1Deobfuscated)

The Base64 decoded string will be written to Stage1Deobfuscated file



The script is still obfuscated but the email is clear visible, you can just copy it and continue to level four or keep deobfuscating the script. I didn't find a reason to continue deobfuscating the script since I'm interested in the email only so here comes the end of level three.


Level 4

Level four is an executable packed with UPX and prints 2 + 2 = 4 when runs!
This one was straight forward for me as I unpacked UPX manually, but others seem to get stuck at this one.
The code that causes problem with using automated unpacker is the following



Four is written instead of five and the Alphabet array which will be used for Base64 decoding is XORed with 0x20. So that's why you get 2 + 2 = 5 when you use automated unpacker, your unpacker misses this piece of code.

A command line argument is expected, atoi is then used to convert the argument from string to integer then calculates its MD5 hash.

The argument hash is compared with different hashes depending on the hour when the challenge was executed. Each hash corresponds to a certain hour from 0 to 23. So basically the argument should be equal to the current hour, e.g it's now 5 pm then the argument should be 17.

The challenge then continues to decode the email and then display it to us "Uhr1thm3tic@flare-on.com"

 Level 5

Level five contains two files, an executable and PCAP file. lets start analyzing the executable.

The challenge tries to read data from a file called key.txt, then the data are passed to this encryption algorithm

 

This algorithm is quite simple, It adds the text "flarebearstare" to key.txt file data.

The encrypted data are then encoded using modified Base64, the capital letters and small letters are switched in the Base64 index table.

A HTTP Post request is used to send the encoded data in chunks of four bytes.

POST / HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) KEY
Host: localhost
Content-Length: 4
Cache-Control: no-cache
UDYs
HTTP/1.0 200 OK
Server: BaseHTTP/0.3 Python/2.7.9
Date: Tue, 21 Jul 2015 21:13:43 GMT
Content-type: text/html


This is the first HTTP request and response from the PCAP file.

As you can see UDYs is the first four bytes of the encoded data, Continue scrapping the data from the packet and we will get the encoded data as the following
UDYs1D7bNmdE1o3g5ms1V6RrYCVvODJF1DpxKTxAJ9xuZW==

Remember that the challenge switches the capital letters with the small letters, so we have to switch back the letters to decode the data
udyS1d7BnMDe1O3G5MS1v6rRycvVodjf1dPXktXaj9XUzw==
unsigned char Base64_Decoded[] = {0xb9,0xdc,0x92,0xd5,0xde,0xc1,0x9c,0xc0,0xde,0xd4,0xed,0xc6,0xe4,0xc4,0xb5,0xbf,0xaa,0xd1,0xc9,0xcb,0xd5,0xa1,0xd8,0xdf,0xd5,0xd3,0xd7,0x92,0xd5,0xda,0x8f,0xd5,0xd4,0xcf};

Now we have everything we need to reverse the process

unsigned char Base64_Decoded[] = {0xb9,0xdc,0x92,0xd5,0xde,0xc1,0x9c,0xc0,0xde,0xd4,0xed,0xc6,0xe4,0xc4,0xb5,0xbf,0xaa,0xd1,0xc9,0xcb,0xd5,0xa1,0xd8,0xdf,0xd5,0xd3,0xd7,0x92,0xd5,0xda,0x8f,0xd5,0xd4,0xcf};
unsigned char Decryption_Key[] = "flarebearstare";
unsigned char Email[sizeof(Base64_Decoded)];
int _tmain(int argc, _TCHAR* argv[])
{
for( int i=0;i<sizeof(Base64_Decoded);i++)
{
Email[i] = Base64_Decoded[i] - Decryption_Key[i%(sizeof(Decryption_Key)-1)];
}
printf("%s",Email);
return 0;
}

And we got the email Sp1cy_7_layer_OSI_dip@flare-on.com


Level 6

In this level we have an APK file. I installed the APK file through android emulator and a classic crackme window appeared after running the app.



Entering random chars I got the message "No"

Time to see what is going inside. As known, APK files are just zip file so I unzipped the APK and used dex2jar to convert classes.dex to jar.

>d2j-dex2jar classes.dex

classes-dex2jar.jar will be generated in the same folder, quickly grab JD-GUI to take a look inside this jar.



A library named "validate" is loaded and then the input string gets passed to the library and the output string is displayed on the screen.

The library can be found where you unpacked the APK at the distention "lib\armeabi\libvalidate.so"
If you own IDA Pro then you are lucky and can load the library into it and decompile it using Hex-Rays Decompiler. Unfortunately I don't own IDA so I took the free alternative "OnlineDisassembler.com". Its a good disassembler that supports wide range of processors and no need to download anything, All the work is done server side.
Remember to select "force-thumb" or you will get a wrong disassembly.
You can check my analysis on OnlineDisassembler on the following link,

https://www.onlinedisassembler.com/odaweb/XsMtxFVZ/0

We have three tables, Table1, Table2 and OutputTable. Table2 which is located at 0x2214 contains a list of the first 0xD94 prime numbers. Table1 contains 0x17 * 0x1b28 byte. Each 0x1b28 byte is used to check two chars of the input string.

The challenge divides each two chars of the input by each prime number, and if it's divisible by any of them it adds a one to OutputTable with index corresponds to the prime number index in Table2. Then divides the two chars by the prime number and continue operating on the result till the result of the two chars is equal to a prime number.
The OutputTable is compared with the corresponding 0x1b28 bytes at Table1 to check if we have entered the correct two chars.
I know my explanation is not clear so I hope the following implementation of the code will make it clear.

int Email_Int[100];
unsigned char Email_Char[100];
unsigned char* Table1 = OpenTable("Table1");
unsigned char* Table2 = OpenTable("Table2");
unsigned char OutputTable[0x1b28];
memset(Email_Int,0,400);
int j=0;
//Reverse Algorithm
    for(int i=0;i<0x17;i++)
{
Email_Int[i] = 1;
for (int k=0;k<0x1b28;k+=2)
{
for(int M=0;M<(*((WORD*)(Table1+((0x16-i)*0x1b28)+k)));M++)
{
Email_Int[i] = Email_Int[i]* (*((WORD*)(Table2+k))) ;
}
}
}
//Convert to chars
for(int i=0;i<0x17;i++)
{
Email_Char[i*2] = Email_Int[i]>>8;
Email_Char[i*2+1] = Email_Int[i]&0xFF;
}
//CheckingAlgorithm
for(int i=0;i<0x17;i++)
{
memset(OutputTable,0,0x1b28);
int t = Email_Int[i];
for (int k=0;k<=0x1b28;)
{
int c = (*((WORD*)(Table2+k)));
if (t%c ==0)
{
(*((WORD*)(OutputTable+k)))++;
t = t/c;
if(t <= 1)
{
break;
}
}
else
{
k+=2;
}
}
if(memcmp(OutputTable,Table1+((0x16-i)*0x1b28),0x1b28) != 0)
{
printf("Wrong Input");
break;
}
    }
I started with the reverse algorithm then the checking algorithm which is equivalent to the one implemented in the library.

Finally the email is Should_have_g0ne_to_tashi_$tation@flare-on.com 

Level 7



Well this one starts with a scary message:) "100% tamper-proof"



Protection iD shows that it's protected with SmartAssembly, So it's time for using De4dot to deobfuscate it.




The entry point looks like this after deobfuscating.
At line 28 the input string is compared with the result of Class3.smethod_0 and Class3.smethod_3. If they are equal it proceeds to line 34 where the input string is used as a key to decrypt the email.

Now it's time to see what smethod_0 and smethod_3 do to get the decryption key.



The MSIL of the method with token 100663297 are passed to an algorithm at line 45 to decrypt the first part of the key.
The method's token at the deobfuscated file is different than the original file. so we have to get back to the obfuscated file and find the method with token 100663297.
If you tried to open the obfuscated file with ildasm you will get an error message "Protected module -- cannot disassemble" as the SuppressIldasm Attribute is applied. I bypassed that by patching ildasm on the fly (patching a conditional jump).
Ildasm doesn't has a search option so I had to do a dump from the file menu and checked Token Values and Actual Bytes. I attached the dump file "dump.il".



Searching for 6000001 ( hexdicmal of 100663297), The method has been found and the MSIL bytes are 0x72, 0x01, 0x00, 0x00, 0x70, 0x26, 0x2a.
And thus we got the first part of the key which is "metaprogrammingisherd"



The CustomAttributes are MD5 hashed and the hash will be part two of the key. We have to know the order of the CustomAttributes to generate the same MD5 as the obfuscated file.



I used Reflexil to get the order of the CustomAttributes. The final string will be
"[System.Reflection.AssemblyProductAttribute(""FLARE-On Challenge"")][System.Runtime.CompilerServices.CompilationRelaxationsAttribute((Int32)8)][System.Runtime.CompilerServices.RuntimeCompatibilityAttribute(WrapNonExceptionThrows = True)][System.Reflection.AssemblyDescriptionAttribute("""")][System.Reflection.AssemblyConfigurationAttribute("""")][System.Reflection.AssemblyCompanyAttribute("""")][System.Reflection.AssemblyCopyrightAttribute(""Copyright ©  2015, FireEye Inc."")][System.Reflection.AssemblyTitleAttribute(""Yo dawg, I heard you were meta"")][System.Reflection.AssemblyTrademarkAttribute("""")][System.Runtime.InteropServices.ComVisibleAttribute((Boolean)False)][System.Runtime.InteropServices.GuidAttribute(""deadbeef-1337-beef-babe-f33dc00ffeee"")][System.Reflection.AssemblyFileVersionAttribute(""12.34.56.78"")][System.Runtime.CompilerServices.SuppressIldasmAttribute()][SmartAssembly.Attributes.PoweredByAttribute(""Powered by SmartAssembly 6.9.0.114"")]"

Entering the decryption key "metaprogrammingisherd_DD9BE1704C690FB422F1509A46ABC988" we get out the email "Justr3adth3sourc3@flare-on.com"

Level 8


The challenge displays the message "the on who seeks finds..." then exits.
Loading it into Ollydbg, I noticed that there is a large array of printable chars. following them into the hex dump window, the array appears to be Base64 encoded.
I coded a small tool to decode the array and I got this peaceful PNG image :)



Oh great what then!
It's obviously a steganography , but there are many ways to hide an information in an image. How can I know which one was used?!
I thought that maybe the guy at FLARE who coded this challenge may have faced a malware in the wild that used the same steganography , and my guess was right.
Googling " Flare steganography " I landed here
https://www2.fireeye.com/FLAREWebinar.html

A webinar about Steganogram Shellcode Backdoor, Quoted from the site
"The malware is a downloader and launcher that uses steganography to extract shellcode, commands, and data from PNG images. This results in advanced modular backdoor capable of collecting wide range information related to the compromised system and executing even more shellcode!"

You can either hear the webinar or download the slides from here as I did
http://public.brighttalk.com/resource/core/64591/tales-from-the-crypt---reversing-malware-with-the-flare-team_95113.pdf

The interesting slide is number 33



A 600x480 pixels, 24-bit color PNG file, the same specs as our PNG file and uses the LSB to hide the data.

Although being the both PNG files have the same specs doesn't mean that they have the same stegnography, However both being related to FLARE in some way made LSB appears as the appropriate approach. 

I coded a small tool to extract the LSB from each of the pixels data, and I got this,




Garbage?! no, take a closer look. It has the same pattern as a very familiar header.



Comparing with challenge 8 file header, It became obvious that it's an executable header, But messed up in some way.
It appears the the zero bytes survived the "messing" operation. What about bits switching? zero bytes will survive those too.
I tried to switch the Green bytes with the Red bytes and now the header seems more friendly :)



Absolutely Great! 

The executable will just display the email Im_in_ur_p1cs@flare-on.com and we are off to the next one :)

Level 9


The challenge file name is "you_are_very_good_at_this", I wonder what I should be good at :)



A sarcastic message! lets bring it into the debugger and see why it's that confident.



Code obfuscation! that's why it's that confident :)

I used a small tool I coded sometime ago to convert this mess into some adequate shape.

You can find the deobfuscated file attached with the name "Deobfuscated.exe"

N.B: I didn't fix the stack executed instructions.

Now we have it almost deobfuscated, the challenge became straight forward. The algorithm XOR the input with certain bytes in the constant array then compares the result.

Xor Eax,Eax
@Loop:
Push Eax
Mov Ecx,DWORD PTR SS:[EBP-14]
Mov Edx,1
MOVSX EBX,BYTE PTR SS:[ESP+ECX+58]
MOVSX EBX,BYTE PTR SS:[ESP+EBX+58]
MOV AH,BYTE PTR SS:[ESP+EBX+B0]
XOR AL,AH
MOV CL,BYTE PTR SS:[ESP+EBX+84]
ROL AL,CL
MOV EBX,DWORD PTR SS:[ESP+EBX+2C]
CMPXCHG BL,DL // Sets Bl = DL if BL == AL
CMOVNE ESI,EDX // Sets ESI = EDX if Zero flag is false
MOV AH,AL
XCHG EAX,ESI
CMPXCHG BL,DL
CMOVE EAX,EDX // Sets EAX = EDX if Zero flag is true
TEST EAX,EAX
JE A:
MOV EAX,EBX
A:
INC DWORD PTR SS:[EBP-14]
MOV EBX,DWORD PTR FS:[30]
MOV ECX,0D
SHL ECX,3
MOV BL,BYTE PTR DS:[ECX+EBX] //PEB+0x68 NtGlobalflag
SHR EBX,3
AND EBX,0E
SHR EBX,1
CMP EBX,7
SETE BL
SUB AL,BL
POP EBX
ADD EAX,EBX
CMP DWORD PTR SS:[EBP-14],29
JNZ @Loop
CMP EAX,29
JNZ @FAILURE
MOV DWORD PTR SS:[ESP+10],402162 // YOU ARE SUCCESS
JMP @END
@FAILURE:
MOV DWORD PTR SS:[ESP+10],402174 // YOU ARE FAILURE
@END:
RET

NtGlobalflag is used to detect a debugger presence and if detected, the challenge will display "You are failure" even if you entered the correct email.

The following code reverse the above algorithm to calculate the correct email.

unsigned char Table[] = {0xCC,0xA9,0x6D,0x77,0x0A,0xF2,0x4E,0x58,0xC1,0x8A,0x2F,0x9F,0xD8,0x6C,0x58,0x43,0xF9,0x1D,0xC8,0x92,0x1F,0x5C,0xC3,0x98,0xC7,0xF8,0xD7,0x03,0xB0,0x8A,0x3C,0xE,0x71F,0x1A,0xCD,0x02,0x13,0xF6,0x07,0x73,0xF2,0x33,0xC8,0xF0,0xBA,0xC2,0xCC,0xB8,0x80,0xF0,0x48,0x77,0xF9,0x9A,0x50,0x55,0xDC,0x01,0xE1,0xEB,0x16,0x5A,0x6B,0xC6,0xC3,0x0E,0x8E,0x27,0x5D,0xC3,0xF4,0x4E,0x06,0x42,0xBC,0xF2,0x5D,0x56,0x30,0x2E,0x19,0x53,0x53,0x66,0xD2,0xF9,0x03,0xCF,0x12,0x06,0x17,0x1A,0x0A,0x0D,0x02,0x1E,0x16,0x10,0x1F,0x09,0x23,0x0F,0x14,0x25,0x1D,0x03,0x19,0x21,0x0B,0x28,0x13,0x00,0x26,0x08,0x1B,0x0C,0x04,0x27,0x24,0x1C,0x01,0x22,0x18,0x20,0x11,0x15,0x05,0x0E,0x07,0x2A,0x2B,0x2C,0xAC,0xBE,0xF5,0x4E,0x75,0xBC,0x87,0xB8,0x16,0x67,0x6B,0x5C,0xFA,0xF1,0xF9,0x93,0xF2,0xD4,0xF8,0x23,0xB9,0xC8,0x11,0x7E,0xCA,0x56,0xD6,0x1B,0x0A,0xDA,0x6E,0xB5,0x01,0xC1,0x55,0x9B,0xB8,0x61,0xCE,0x4C,0x6E,0xBC,0xEE,0x08,0xF4,0x64,0x15,0x8C,0x65,0x60,0xE2,0x1B,0x8E,0x40,0x4A,0x34,0x45,0xE3,0x96,0x4C,0xEB,0xC9,0x0D,0xEB,0x8E,0x67,0x26,0xEF,0x32,0x46,0xB5,0xBD,0xB2,0xE6,0x9F,0xFF,0xF1,0x74,0xEF,0xA8,0x46,0xC4,0x60,0x39,0x65,0x31,0xAB,0x9F};
unsigned char Email[0x29];
int _tmain(int argc, _TCHAR* argv[])
{
for (int i=0;i<0x29;i++)
{
unsigned char k = Table[Table[Table[i+0x58]+0x58]+0x2C];
unsigned char temp = Table[Table[Table[i+0x58]+0x58]+0x84];
__asm
{
mov al,k
mov cl,temp
ror al,cl
mov k,al
}
k ^= Table[Table[Table[i+0x58]+0x58]+0xB0];
Email[i] = k;
}
printf("%s",Email);
return 0;
}

And finally we got the email "Is_th1s_3v3n_mai_finul_foarm@flare-on.com"

Level 10

This level we have an AutoIt file. Using Exe2Aut we can extract the AutoIt script and the attached files (Two Drivers and ioctl.exe)

Lets have a look at part of the script

If @OSArch <> "X86" Then
MsgBox(0, "Unsupported architecture", "Must be run on x86 architecture")
Exit
EndIf
If @OSVersion = "WIN_7" Then
FileInstall("challenge-7.sys", @SystemDir & "\challenge.sys")
ElseIf @OSVersion = "WIN_XP" Then
FileInstall("challenge-xp.sys", @SystemDir & "\challenge.sys")
Else
MsgBox(0, "Unsupported OS", "Must be run on Windows XP or Windows 7")
Exit
EndIf
FileInstall("ioctl.exe", @SystemDir & "\ioctl.exe")
$nret = dothis("0x96c581bc009905e76931875a583f97a738b764eb67f35c802194bf86123b943d1907619488a31a26cf29ba5f5e57ed5c5a37cb5d67dc2020a7e6d55cadefba32aba3ed77f0e18e41a571e74a8a7614a895d7c8827c46028761994543bf449138c65a6e7b5039792c85be5b4998c9950d2497f73cd88d186a6bffe3634bd250ec59e2", "flarebearstare") //_CreateService("", "challenge", "challenge", @SystemDir & "\challenge.sys", "", "", $SERVICE_KERNEL_DRIVER, $SERVICE_DEMAND_START)
If $nret Then
If dothis("0x96d587b8139933d17e3598505e729da736bb66aa6cfa5180289fb6845530", "flarebearstare") Then // _StartService("", "challenge")
dothis("0x9aee96b50da818d16f368556131aecfc69ef21a440f24fcc6bd1f3bd1e76db69574a6c8d81ed53688a7eaa364e53fd0700", "flarebearstare") //ShellExecute(@SystemDir & "\ioctl.exe", "22E0DC")
EndIf
EndIf
Func decrypt($data, $key)
Local $opcode = "0xC81001006A006A005356578B551031C989C84989D7F2AE484829C88945F085C00F84DC000000B90001000088C82C0188840DEFFEFFFFE2F38365F4008365FC00817DFC000100007D478B45FC31D2F775F0920345100FB6008B4DFC0FB68C0DF0FEFFFF01C80345F425FF0000008945F48B75FC8A8435F0FEFFFF8B7DF486843DF0FEFFFF888435F0FEFFFFFF45FCEBB08D9DF0FEFFFF31FF89FA39550C76638B85ECFEFFFF4025FF0000008985ECFEFFFF89D80385ECFEFFFF0FB6000385E8FEFFFF25FF0000008985E8FEFFFF89DE03B5ECFEFFFF8A0689DF03BDE8FEFFFF860788060FB60E0FB60701C181E1FF0000008A840DF0FEFFFF8B750801D6300642EB985F5E5BC9C21000"
Local $codebuffer = DllStructCreate("byte[" & BinaryLen($opcode) & "]")
DllStructSetData($codebuffer, 1, $opcode)
Local $buffer = DllStructCreate("byte[" & BinaryLen($data) & "]")
DllStructSetData($buffer, 1, $data)
DllCall("user32.dll", "none", "CallWindowProc", "ptr", DllStructGetPtr($codebuffer), "ptr", DllStructGetPtr($buffer), "int", BinaryLen($data), "str", $key, "int", 0)
Local $ret = DllStructGetData($buffer, 1)
$buffer = 0
$codebuffer = 0
Return $ret
EndFunc
Func dothis($data, $key)
$exe = decrypt($data, $key)
$exe = BinaryToString($exe)
Return Execute($exe)
EndFunc

The decrypt function uses RC4 algorithm to decrypt the input data. I wrote the decrypted text next to each line.

The challenge first check whether it's operating on windows 7 or windows XP and then deploy the appropriate driver. Drops ioctl.exe then lunches ioctl.exe with command line argument "22E0DC" which is an IOControlCode.

Load the driver into IDA and let's check the handler for "22E0DC" 



We have a triangle function :) if you solved level eight you will be familiar with the numbers 0x1,0x2,0x4,0x8,0x10,0x20,0x40 and 0x80. Each of them corresponds to a bit in a byte. And then we have a bunch of conditional jumps (JNZ and JE). Of course you have to reach the end of the triangle to have AL equals 1.
So we have to find the bits that satisfy this sequence and see what they represent.

01110100
01110010
01111001
00100000
01110100
01101000
01101001
01110011
00100000
01101001
01101111
01101100
01110100
01101100
00111010
00100000
00110010
00110010
01000101
00110000
00110110
00111000

The Hidden Message is "try this ioltl: 22E068"
Oh great, I will try it!




A huge function that had nothing meaningful except it's last part. A TEA decryption routine that gets called with decryption key "0123456789ABCDEF" and length of 0x28. 

The encrypted data are unfortunately a garbage. I kept wondering for sometime what the right data should be, and be pure guessing I checked the cross reference for each of the encrypted bytes and found that they are set by procedures that never get executed!



For example, the first encrypted byte gets assigned here with the value 0x56.
Doing the same procedure for each of the 0x28 encrypted bytes you will get the following encrypted bytes

567FDCFAAA2799c46c7Cfc926161471a19b963FD0CF2B620C02D5CFDD97154964F43F7FFBB4C5D31

And by decrypting them using TEA and key "0123456789ABCDEF" we got our email "unconditional_conditions@flare-on.com"

Level 11


The final beast .. or level eleven!

This level implements heavy crypto that will get you lost if you dove too far into it. In cases like this I always focus on my target and while achieving my target I try to know some about the crypto implemented.
Upon starting the executable we get an error message "The number of parameters passed in is incorrect". Running again with a command line argument "5", We get nothing. The challenge seems to be doing some calculations as it consumes some CPU resources, but it's not looking that it will finish somewhere near.
It's time to have a closer look.



It writes "secret.jpg", pass the parameter "5" to atoi then never comes back from 0x401910.
Digging into 0x401910.



Resources with ID 0x78 and 0x79 are loaded then the parameters of the function 0x004015D0 are passed as commented above.



At 0x401000 the challenge checks that the resource 0x79 isn't tampered by using MD5 hash.
The key that we entered through the command line gets used for the first time at 0x40163D. An OR operation is performed between byte located at RC_0x79+0x10 and the key.
At 0x4014E0 a hash loop is executed and uses RC_0x79+0x10 and (RC_0x78 Xor RC_0x78+10 Xor Rc_0x78+20) to generate the hash. I don't want to get into that HashLoop as it's tedious.
The generated hash is used as the decryption key to decrypt RC_0x79+0x30 using RC5 algorithm for length of 0x30.
At the end at 0x4016D4 the first decrypted dword gets compared with zero. Using parameter "5" the dword isn't zero and we took the jump. Later on there is a large loop that starts at 0x401750 and it does the same as previous for each of the next 0x30 bytes of resource 0x79. However it will loop for a very long time since dword  at RC_0x79+0x34, which we decrypted earlier, is used as the number of iterations for the HashLoop. In our case it was 0x4E4F8474. So it's obvious that we have a wrong decryption.

Replicating the crypto in C and then bruteforce it to find the correct parameter is a long road and I had to analyze the HashLoop more in depth. I took an alternative short road by patching the address 0x4016D9 with INT3, So that when I get the correct parameter a breakpoint exception is raised. And I coded a tiny debugger to debug the challenge to see if breakpoint exception is raised and if not it tries another parameter.
The bruteforcer code is as follows,


char process[] = "CryptoGraph_Patched.exe";
char Argument[100];
char output[100];
STARTUPINFO si;
PROCESS_INFORMATION pi;
int _tmain(int argc, _TCHAR* argv[])
{
for (int i=0;i<0xFF;i++)
{
   itoa(i,Argument,10);
   printf("%s\n",Argument);
   sprintf(output,"%s %s",process,Argument);
if( !CreateProcess( process,output,NULL,NULL,FALSE,DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,&pi))
        {
   printf( "CreateProcess failed (%d).\n", GetLastError() );
            return 0;
        }
DEBUG_EVENT debug_event = {0};
        for(;;)
        {
            if (!WaitForDebugEvent(&debug_event, 100))
            {
DebugActiveProcessStop(pi.dwProcessId);
TerminateProcess(pi.hProcess,0);
break;
}
if (debug_event.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT && debug_event.u.Exception.ExceptionRecord.ExceptionAddress == (LPVOID)0x4016D9)
{
printf(" Key is %s", Argument);
return 0;
}
else
{
ContinueDebugEvent(debug_event.dwProcessId,debug_event.dwThreadId,DBG_CONTINUE);
}
        }
}
return 0;
}

In less than a minute the correct parameter has been found to be "205".
Running the challenge again with the correct parameter I got a reasonable number of iterations for the HashLoop which is 0x10000. The problem is that every time a new block is decrypted, the number of iterations gets doubled and considering that we have 0x20 blocks to decrypt, We will need a lot of time to decrypt the whole resource. which isn't reasonable.
I kind of reached a dead end at this procedure so It's time to move on and see what the decrypted resource will be used to. Maybe we don't need to decrypt the whole resource to get the email.

Later on at 0x401B60 the last number of iterations is used to decide which block will be used in the decryption of the image. If the last iterations number is less than 0x1000000, block number 7 will be used else block 9 will be used. So apparently decrypting blocks after block number 9 is useless so I patched the address 0x4018C0 with CMP EAX,0A.

And finally we got the email.



After a long journey, it's time to claim the prize :)


12 comments:

  1. Thanks for this blog , I have question in challenge 10 this step not clear
    "Load the driver into IDA and let's check the handler for "22E0DC" "
    how I can check for this handler ?

    Regards

    ReplyDelete
    Replies
    1. At 0x29CD6E, the IOControlCode is loaded into EDX and then used to drive the switch case at 0x29CD91.

      Delete
    2. did you analyze it static or dynamic ?

      Delete
    3. please can you explain it in more detail

      Delete
    4. Starting from the DriverEntry, At 0x29D87D a pointer to DRIVER_OBJECT is loaded into ECX.
      DRIVER_OBJECT+0x38 is the start of MajorFunction, Check this link https://msdn.microsoft.com/en-us/library/windows/hardware/ff544174(v=vs.85).aspx
      0x29D880 sets all the IRP Handlers to the function at 0x29CD20.
      0x29CD44 calls IoGetCurrentIrpStackLocation that returns a pointer to the caller's I/O stack location.
      0x29CD62 [IO_STACK_LOCATION+0xC] which is the IOControlCode, is moved to EAX.
      0x29CD71 IOControlCode - 0x22E004
      0x29CD8A [0x29D614+ IOControlCode - 0x22E004]
      0x29CD91 [0x29D480+ECX*4] In the case of IOControlCode 0x22E0DC, ECX will be 0x36 which will leads to 0x29D558 that has the address of 0x29D180 and here we arrive to the handler.

      Delete
    5. جزاك الله خير .. ماقصرت وصلت المعلومه

      Delete
  2. @Mohamed: You use hexray in IDA, find "case 0x22e0dc" in switch, you'll see it.

    ReplyDelete
    Replies
    1. what is hexray? is it plugins or what ? I can't find it .

      Delete
    2. A decompiler, Check this link https://www.hex-rays.com/products/decompiler/

      Delete
    3. yes I already use IDA , but how I can find this case , how I can search I stuck in switch function

      Delete
    4. In DriverEntry function, double click in sub_29D810. In sub_29D810 function, find 0x29d185 address and there is case 0x22E0DC in switch. You can press f5 to see it in hexray.

      Delete