<-- Flare-On 2015 Index / FLARE-On 2015 Challenge #5 
FLARE-On 2015 Challenge #5

Date: Aug 14, 2015


filename:    sender    DOWNLOAD
size74 k (75,264 bytes)
typeWin32 Console (wininet.dll dependency)
Original FLARE AuthorPeter Kacherginsky
tool:    OllyDbg 2.01 / Debugger    Visit Website
tool:    Your favorite Base64 decoder
tool:    Your favorite C/C++ compiler

As explained in the e-mail that went along with the challenge:
...cyberspace commandos captured some network traffic and a program they think may be responsible...
The ZIP contents comes with the file "sender" and a file named "challenge.pcap" which appears to contain a capture of the incoming and outgoing network data, presumably originating from "sender". It looks like we have to figure out what "sender" sent.

Not worrying about the exact format of the challenge.pcap file, opening it either in a text editor or hex editor reveals several HTTP POSTs sent to a web server and the corresponding server responses. The first server/response pair is:
0000100: 7f 96 f6 90 cf 6a 84 c3 50 18 00 20 52 29 00 00 50 4f 53 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d  .....j..P.. R)..POST / HTTP/1.1
0000120: 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 4d 6f 7a 69 6c 6c 61 2f 35 2e 30 20 28 57 69 6e 64 6f 77  .User-Agent: Mozilla/5.0 (Window
0000140: 73 20 4e 54 20 36 2e 31 3b 20 57 4f 57 36 34 29 20 4b 45 59 0d 0a 48 6f 73 74 3a 20 6c 6f 63 61  s NT 6.1; WOW64) KEY..Host: loca
0000160: 6c 68 6f 73 74 0d 0a 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 34 0d 0a 43 61 63 68 65 2d  lhost..Content-Length: 4..Cache-
0000180: 43 6f 6e 74 72 6f 6c 3a 20 6e 6f 2d 63 61 63 68 65 0d 0a 0d 0a 55 44 59 73 97 53 ae 55 2e c5 0e  Control: no-cache....UDYs.S.U...
00001a0: 00 28 00 00 00 28 00 00 00 45 00 00 28 00 3f 40 00 80 06 00 00 7f 00 00 01 7f 00 00 01 00 50 c0  .(...(...E..(.?@..............P.
0000200: 19 50 18 00 1f 66 8c 00 00 48 54 54 50 2f 31 2e 30 20 32 30 30 20 4f 4b 0d 0a 97 53 ae 55 2e c5  .P...f...HTTP/1.0 200 OK...S.U..
An HTTP 1.1 POST usually consists of legible name/value pairs reflecting HTML form variables (such as that defined by the content type: application/x-www-form-urlencoded), although since the POST data is ultimately application defined, it could contain anything. The POST data (known as the entity) begins after the HTTP request headers, which are delimited by the double CRLF sequence (i.e. hex values 0D 0A 0D 0A). The first interesting request header is the "Content-Length" which tells the server how much data is is being sent (in bytes). Since this value is 4, we can see the first 4 bytes after the double CRLF sequence is the ASCII value of "UDYs". Another interesting request header catches our eye, which is the "User-Agent". This value is typically a browser-dependent identification string. In our case, this string begins with a common "Mozilla/5.0" sequence, but ends in a not-typical "KEY". Perhaps this is an indication to the server that the client is sending key data?

We can assume the rest of the surrounding data in the capture file are the network packets. Luckily, since the HTTP requests are easy to spot, we can manually pull out the data from each of the 12 HTTP POST transactions in the capture file and we end up with the following 12 data chunks that were transmitted in the order listed:
Especially due to the trailing "==" marker only occurring in the last chunk, we might guess this data is collectively one Base64 string, the data being broken into 4-byte chunks just to make it harder to piece together. Now that we know what data was sent, we still don't know what it means. Running the data as-is through a Base64 decoder doesn't seem to produce meaningful results:
0000000: 50 36 2c d4 3e db 36 67 44 d6 8d e0 e6 6b 35 57 a4 6b 60 25 6f 38 32 45 d4 3a 71 29 3c 40 27 dc  P6,.>.6gD....k5W.k`%o82E.:q)<@'.
0000020: 6e 65                                                                                            ne
A hex dump of the first few bytes of "sender" reveals that it is a Windows PE executable image. Since we know this program likely performs network communication, we'd normally want to analyze it before just running it. We'll use IDA to take our first peek into this program. When we open up sender.exe in IDA, we need to follow the branch instructions from the entry point location ("Exports" tab -> "start") to the C/C++ entrypoint at 401100. If you don't know what you're looking for, trial-and-error should lead you down this path before you see anything interesting:
.text:004016AB E9 7B FE FF FF          jmp     ___tmainCRTStartup       //app init prior to calling "WIDE" version of main() / symbol is indicator of C/C++ app
    .text:00401624 E8 17 FC FF FF          call    sub_401240
        .text:00401240 E8 BB FE FF FF          call    sub_401100
Once we are look at the tmain() function, we can see the program immediately attempts to open "key.txt" from the current directory using the CreateFileA() API. If CreateFileA() fails, as indicated by a returned INVALID_HANDLE_VALUE in EAX (i.e. -1, or 0xFFFFFFFF), we output a failure message "[!] Could not open key file: %s\n" and the program ultimately exits.

Flare-On 2015 Challenge #5 - Disassembly of C/C++ tmain()

Lets confirm this by running the program (assuming we don't have a file named key.txt in the current directory!):

Flare-On 2015 Challenge #5 - Program is looking for key.txt

We now know what the program does if it DOESN'T find the file "key.txt". Lets create "key.txt" with some test data and see how the program reacts when we step through it in the debugger. I happened to create "key.txt" with the contents: "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 0123456789". Loading sender.exe in OllyDbg and setting a breakpoint on 401100, you should find yourself at the familiar tmain() disassembly. Step over instructions until you advance past the CreateFileA() call:

Flare-On 2015 Challenge #5 - Loading key in debugger

You'll find after the file is loaded, ReadFile() is called to read the contents of "key.txt" into memory and the data is initially processed by the CALL to function 401250. This function loops through each character and adds to it the ASCII value of the current running position in the hardcoded string "flarebearstare". This results in a simple form of encryption using the hardcoded value as a "running key". That is, each time the encryption index reaches the end of "flarebearstare", it resets to the beginning, so the hardcoded key is recycled as many times as necessary until all the input data is processed. This form of encryption is slightly better than offsetting the ASCII value by the same character with each iteration. For example, the data in key.txt would be offset by the corresponding character in "flarebearstare" as follows:
Each encrypted character overwrites the original source character until the entire key buffer is encrypted. The encryption loop is shown below, having performed enough iterations to see that "THE QUICK" portion of our test data has been encrypted in-place:

Flare-On 2015 Challenge #5 - flarebearstare encryption loop

The majority of the loop seems a bit hard to follow, but it is nothing more than a roundabout way to transform the current input data position by MODULUS 14 (length of "flarebearstare") to arrive at the current running key position. The encryption operation itself is only the ADD instruction at 401287.

As you step out of this function, you'll find the program proceeds to calculate the base64 buffer size required to encode the encrypted data (approx 1.3333 times the original). The CALL at 4011E7 appears to encode the data in the Base64 scheme, returned in the memory location pointed to by EBX. Lets double-check it by saving off a copy of the encrypted key data and manually encoding it with a Base64 program. We actually find the results are identical except the case of the alphabetic characters are flipped! For example, our "flarebearstare" encrypted test data encoded as Base64 yields:
The CALL to function 4011E7 yields:
It is a common, simple and effective obfuscation technique to alter the Base64 character-lookup table to prevent decoding using standard tools without changing the algorithm and retaining the benefit of encoding binary data in the printable text range. Yet, it provides another hoop for the reverse engineer to jump through.

The remainder of the program appears to be responsible for transmitting the data. At this point we shouldn't need further analysis of the program or the capture file. We've already gathered the full KEY transmission string, we just need to work the data backwards in the proper order to deduce the original contents of "key.txt". In reverse, this is:
  • Invert the case of the full transmission string:
  • Once that is done, the result can be decoded back to binary form using standard Base64 tools:
    This 34-byte hex dump now represents the raw "flarebearstare" encoded text. I saved this in a file called key_enc.bin. We're almost there!
    0000000: b9 dc 92 d5 de c1 9c c0 de d4 ed c6 e4 c4 b5 bf aa d1 c9 cb d5 a1 d8 df d5 d3 d7 92 d5 da 8f d5 d4 cf  ..................................
  • Remove the "flarebearstare" pairing (i.e. decrypt the text):
    Line-up each byte of the encoded text with the repeating pattern "flarebearstare" and subtract the pattern's value instead of adding it.
I wrote this C++ function to load the "flarebearstare" encoded text (the binary result from our Base64 decoding above) and decode it. You could write a similar tool in just about any other language:

// // flare2015crackme5_algo() // void flare2015crackme5_algo(void) { const char* pszFile = "key_enc.bin"; const char* pszObfuscator = "flarebearstare"; FILE* f = fopen(pszFile,"rb"); if (!f) { printf("ERROR: couldn't open \"%s\"\n",pszFile); } else if (!pszObfuscator) { printf("ERROR: obfuscator cannot be NULL\n"); } else { size_t uLenObs = strlen(pszObfuscator); size_t uFileLen = 0; ResultCode iRetCode = ll::getFileSizeT(f,&uFileLen); //NOTE: file size wrapper function not included here if (CODE_FAILED(iRetCode)) { printf("ERROR: couldn't get file size of \"%s\"\n",pszFile); } else if (!uLenObs) { printf("ERROR: obfuscator string length cannot be 0 bytes!\n"); } else if (!uFileLen) { printf("ERROR: file size of \"%s\" is 0 bytes!\n",pszFile); } else { char* pKey = new char[uFileLen+1]; if (!pKey) { printf("ERROR: couldn't allocate file buffer of %u bytes!\n",uFileLen+1); } else { size_t uBytesRead = fread(pKey,1,uFileLen,f); if (uBytesRead != uFileLen) { printf("ERROR: expected to read %u bytes from file but instead read %u\n",uFileLen,uBytesRead); } else { char* p = pKey; const char* o = pszObfuscator; const char* pEnd = p+uFileLen; const char* oEnd = o+uLenObs; do { *p = *p - *o; ++p; //advance obfuscator index, wrap around if we reach the end ++o; if (o>=oEnd) o = pszObfuscator; } while(p<pEnd); *p = 0; //NULL terminate printf("the key is \"%s\"\n",pKey); } delete[] pKey; } } fclose(f); } } //flare2015crackme5_algo()

Running the program above in the same directory with key_enc.bin decoded the key sent in the original packets, which happens to be the solution:

Flare-On 2015 Challenge #5 - Decrypting original key.txt

After reading the official solution, I learned I could have used WireShark to parse that PCAP file. I didn't have any experience with WireShark at the time of the challenge, so had I not been lucky enough to have the "Content-Length" header included within the HTTP request, I would have had to spend time to investigate the PCAP file format in hopes to separate the inbound from the outbound network data. I arrived at the same basic analysis as described by the author, albeit we used different tools to decrypt the final solution.

Response from Sp1cy_7_layer_OSI_dip@flare-on.com:
Subject: FLARE-On Challenge #5 Completed! From: Sp1cy_7_layer_OSI_dip@flare-on.com To: <HIDDEN> Date: Fri, 14 Aug 2015 14:48:14 -0400 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 attachment_filename="63C64502837A89CA0147095726DF8262.zip"

<< Flare-On 2015 Index  --  Go on to Challenge #6 >>