filename: |
|
sender |
|
DOWNLOAD |
md5 | | b39c32f2bac5609eed2b0d1f258a3f40 |
size | | 74 k (75,264 bytes) |
type | | Win32 Console (wininet.dll dependency) |
Original FLARE Author | | Peter 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:
UDYs
1D7b
NmdE
1o3g
5ms1
V6Rr
YCVv
ODJF
1Dpx
KTxA
J9xu
ZW==
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.
Lets confirm this by running the program (assuming we don't have a file named key.txt in the current
directory!):
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:
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:
THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 0123456789
flarebearstareflarebearstareflarebearstareflarebearsta
------------------------------------------------------
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:
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:
urSmkra3rqS9k7azwby0jKfBvYKvtr/Dx4HBu6u+gcatp4Wts83Ngba0rYyRo5eVmZaoqqya
The CALL to function 4011E7 yields:
URsMKRA3RQs9K7AZWBY0JkFbVykVTR/dX4hbU6U+GCATP4wTS83nGBA0RyYrO5EvMzAOQQYA
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:
UDYs1D7bNmdE1o3g5ms1V6RrYCVvODJF1DpxKTxAJ9xuZW==
BECOMES:
udyS1d7BnMDe1O3G5MS1v6rRycvVodjf1dPXktXaj9XUzw==
-
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:
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 >>