FLARE On Challenge (2015) #5

This challenge is an easy one. It contains two files, one is a Windows Portable Executable file and another one is a PCAP file. Let’s look at the PCAP file at first:
ch5_pcap1

In the PCAP file we can see a series of HTTP traffics, each of them only POST 4 characters to a localhost server and the server will response with “1”:
ch5_pcap2

There is no more information we can find from the PCAP file, so let’s move to the executable.

The functionality of the executable file whose name is sender is really simple. Firstly, it will read some data from a file named key.txt:
ch5_disass1

Next, the data read from the key.txt will be encrypted by a function located at address 0x00401250:
ch5_disass2

The encryption algorithm is easy to understand: it adds the string “flarebearstare” to the data read from key.txt byte by byte.

After the encryption, the encrypted data will be encoded by Base64 algorithm with a custom character set:
ch5_disass4

And finally, the data will be split into 4 bytes strings and send out to the server:
ch5_disass5

So our task is easy, just assemble the 4 bytes strings in the PCAP file and then do a reverse calculation on the assembled string, the following Python script can help with the reversing:

import string
import base64

def base64_decode(s):
    table = string.maketrans(
      string.lowercase + string.uppercase + string.digits + "+/",
      string.uppercase + string.lowercase + string.digits + "+/"
    )
    s = s.translate(table)
    return base64.b64decode(s)

enc_data = 'UDYs1D7bNmdE1o3g5ms1V6RrYCVvODJF1DpxKTxAJ9xuZW=='
key_str = 'flarebearstare'
key_len = len(key_str)

b64_str = base64_decode(enc_data)
out_str = ''
i = 0

for ch in b64_str:
    out_str += chr((ord(ch) - ord(key_str[i % key_len])) & 0xff)
    i += 1

print out_str

And the answer is:
ch5_ans

Posted in CTF | Tagged , , , | Comments Off on FLARE On Challenge (2015) #5

FLARE On Challenge (2015) #4

File youPecks is a 32 bit Windows Portable Executable file packed with UPX (according to the section name). And it can be unpacked with the UPX utility:
ch4_upx

When executes the unpacked file, it only prints out a strange expression “2 + 2 = 5”, and then exits:
ch4_output

Both the file structure and the resource show no suspicious, so I move on to the disassembly code in IDA. The APIs calls of this program suggest that it is probably written in C++. And at the beginning of the mian() function, here is a strange logic:
ch4_disass1

From above picture you can see that this program calls the function atoi() to convert a string “5” to an integer and stores it to register esi, and later it compares the esi with number 5 and jumps if it does not match. This conditional jump makes no sense because the esi should always equal to 5! However, if the jump does not take place, the program will just terminate itself. So let’s ignore this logic and just see what will happen if it jumps to the unreachable branch.

Firstly, it will check if the number of the argument is equal to 2:
ch4_disass2

If so, it will convert the second argument to an integer and calculate the MD5 hash of the least significant byte of this integer:
ch4_disass3

Next, a branch of bas64-like strings will be initialized and “appended” to a list. Please notice that the Append() function here (in the picture below) may not correct because I did not analyze it in details, the only thing I saw while debugging is that this function will put those bas64-like strings into an array:
ch4_disass4

After above Initializations, one of the Base64 string will be chosen and decoded, then the decoded data will compare with the MD5 hash calculated before:
ch4_disass5

Once the MD5 hash matches the Base64 decoded data, the program will continue executing, or it will exit. Now that we know the MD5 hash is only calculated based on one byte, we can brute-force all the possible value with following Python script:

import base64
import hashlib

for i in range(0, 256):
    print str(i) + ': ' + base64.b64encode(hashlib.md5(chr(i)).digest())

By observing the output of the script, we can find that those Base64 strings in this program are actually Base64 encoded MD5 hash of byte 0x00 to byte 0x17 (0 to 23), with all the uppercase letters changed to lowercase letters and all the lowercase letters changed to uppercase letters. That is to say, those Base64 strings are encoded with a different character set than the standard Base64 character set.

Now we have only one question left: which Base64 string will be picked out to compare with the hash calculated from our input? If you look back to the previous code, it is easy to answer: it is determined by the time, or more precise, the current hour (and that’s why it only need value from 0 to 23).

Knowing this, the things become easy, we can just patch the “jnz” at address 0x0040147F to “jz” so it can run into the unreachable branch and patch the “jnz” at address 0x00401BBC with “nops” so that no matter the hash matches or not the file can continue executing. Then we can execute this file with different time settings to see what will happen. The following Python code can help with this task:

import sys
import time
import win32api
import datetime
import subprocess

def set_hour(hour):
    time_tuple = ( 2015,    # Year
                      1,    # Month
                      1,    # Day
                      hour, # Hour
                      0,    # Minute
                      0,    # Second
                      0,    # Millisecond
                  )
    
    dayOfWeek = datetime.datetime(*time_tuple).weekday()
    time_tuple = time_tuple[:2] + (dayOfWeek,) + time_tuple[2:]
    win32api.SetSystemTime(*time_tuple)

for i in range(0, 24):
    set_hour(i)
    local_hour = time.localtime().tm_hour
    p = subprocess.Popen(['youPecks_patch.exe', str(local_hour)])
    time.sleep(1)

Unfortunately, things seems not that easy, the output of this script is as below:
ch4_pyout

Obviously, there is nothing useful. So I have to continue with the disassembly code to see what is going on. After compares the MD5 hash, another branch of Base64 strings are initialized and “appended” to another list. Then one of the Base64 strings will be picked out and decoded, and then XORed with the previous Base64 decoded data. Finally, the XOR result will be printed out.

Wait, the final output depends on the previous Base64 data, and the previous Base64 decoded data should match the MD5 hash of our input (from 0 to 23). However, according to current analysis, the first set of the Base64 strings seems uses a different character set than the standard one, if we decoded it using the standard Base64 character set which used by this program, the MD5 hash will never match!

What is the problem here? I must missed something!

It is time to go back to the original file (before unpack), let’s try to execute it in a CMD:
ch4_output1

What?! Instead of printing out “2 + 2 = 5”, it prints “2 + 2 = 4”!!! So how about we pass the current hour to it? Oops, we have the email address:
ch4_ans

Now I realize the secret of this challenge, the file unpacked by the unpack stub of itself is different from the file unpacked using the UPX tool! Here is how the unpacked file looks like in the debugger:
ch4_dbg1

And the Base64 character set also different from the file unpacked by the UPX tool:
ch4_b64_2
ch4_b64_1

Posted in CTF | Tagged , , , | Comments Off on FLARE On Challenge (2015) #4

FLARE On Challenge (2015) #3

The first thing you may noticed for this challenge is that the file size is much larger than the previous challenges, it is about 12 MB! However, from the section table we can find that the size of the PE image is only 0x30600 bytes (198,144 bytes), far less than 12 MB:
ch3_cff

That is to say, there is a lot of data appended to the end of this PE file, let’s see what are they:
ch3_append

From offset 0x30600, we can find that the data here is begin with a magic string “PYZ”, which suggests that the append data probably contains compressed Python code. Furthermore, the executable itself may be created by tools like py2exe and may be used to interpret the Python code inside its append data.

If the program indeed works like a Python interpreter, it is not worth to analyze the entire file because it may wastes a lot of time, so let’s look at the strings of this file at first:
ch3_strings

The above strings indicate that our previous guess (the program works as a Python interpreter) should be correct. And let’s move on to see if we can find where it interprets the Python code. If we can find such a place, we may read the Python code from the memory.

According to the strings, we can find the following piece of code which retrieves an interesting API named PyRun_SimpleString:
ch3_disass1

This API is probably used to execute the Python code and if we can put a breakpoint on this API after it was loaded, we may know what Python code will be executed. So firstly, I put a breakpoint on address 0x004026DE, right after the GetProcAddress() call, and then I ran this program in the Immunity Debugger. Unfortunately, the program executed without hitting the breakpoint. A window pops up and presents us the following picture:
ch3_pic

What is happened here? The process explorer tells us the story: a child process of the program itself was created!
ch3_process

So why it creates a child process of itself? Or more precise: why the child process can perform different behaviors since its executable is same as the parent process? The answer for this question could be: it receives different augments or it running in a different environment. From the Properties Panel of the parent process, I found a suspicious environment variable named “_MEIPASS2”, which point to a fold in the %TEMP% directory:
ch3_properties

To verify if this environment variable can affect the behaviors of this program, I added a system wide environment variable with the same name and value, then ran the program again in the debugger, and finally, the breakpoint was hit!

Now we can know the address of the PyRun_SimpleString() and we can put a breakpoint on this API. If we continue the process, the breakpoint on PyRun_SimpleString() will hit as expect:
ch3_dbg1

From the stack we can know that the first argument passed to the PyRun_SimpleString() is the Python code to be executed, and from the information in the Python code we can know that this program is probably created by the PyInstaller.

After several hits on this API, I found a suspicious Python code:
ch3_py1

This Python code concatenates a lot of strings and then decodes and executes those strings. By replacing the function exec() with print() we can get the code to be executed:
ch3_py2

In the above code we can easily find what we want:
ch3_ans

Now that we know the file is created by PyInstaller, I found a great tool which can decompress all the files in the append data:
http://sourceforge.net/projects/pyinstallerextractor/

With this tool we may solve this challenge within one minute because it can extract the Python code immediately.

Posted in CTF | Tagged , , , | Comments Off on FLARE On Challenge (2015) #3

FLARE On Challenge (2015) #2

The file of challenge 2 named very_success, it is a 32 bit Windows Portable Executable file which probably written in Assembly Language. The workflow of this program is very similar to the i_am_happy_you_are_to_playing_the_flareon_challenge.exe in challenge 1. It will read a password and then verify it. Obviously, the verification algorithm is different.

In additional to the verification algorithm, the code itself looks a bit weird:
ch2_disass1

There is a call at the Entry Point, followed by a piece of code which looks strange. Let’s see what will happen inside that call:
ch2_disass2

If you are familiar with shellcode, you should be very sensitive about the “pop” instruction at the beginning of a call. So what is being popped out here? Yes, the return address. In this code the return address (0x004010E4) is popped to register eax, and stores to stack [ebp-10], and then it will be passed to the Verify() function located at address 0x0040105F. This explains why the code after the call at the Entry Point looks strange, it should be regarded as data instead of code!

Let’s see what will the Verify() function do: it receives three arguments, the data located at the return address (0x004010E4, I call it Key), your input (the password) and the length of the input. At the beginning of the Verify() function, it checks if the length of the input is greater than 0x25, if not it will fail. Then it verifies the input data based on the Key. The verification code here seems being “optimized” so it looks not very straightforward:
ch2_disass3

Fortunately, the code is very short so there should be no difficulties for you to understand the algorithm. The following IDA python script describes the algorithm and can be used to calculate the correct password:

from idaapi import *


def rol_byte(n, c):
    if c == 0:
        return n
    t1 = (n << c) & 0xff
    t2 = (n >> (8 - c)) & 0xff
    return (t1 | t2)


begin_addr = 0x004010E4
length = 0x25
out = ''
c = 0
addr = begin_addr + length - 1

for i in range(0, length):
    out += chr(((Byte(addr) - rol_byte(1, c) - 1) & 0xff) ^ 0xC7)
    c += Byte(addr)
    c &= 3
    addr -= 1
    
print out

So the answer we want is:
ch2_ans

There is a same bug as challenge 1 which may lead to many other valid passwords:
ch2_poc

Posted in CTF | Tagged , , , | Comments Off on FLARE On Challenge (2015) #2

FLARE On Challenge (2015) #1

When you analyze a file, the first thing is probably to understand the type of the file. If you are familiar with Windows, I believe you can recognize the file type of the first challenge just by looking at its icon:
ch1_icon

Yes, it looks very much like a Windows Installer. That is to say, this file is a Windows Portable Executable file. And this can be easily confirmed by opening the file with a text/hex editor and find the magic strings “MZ” and “PE”:

ch1_pe

After knowing the file type, we may look at more file attributes with the help of a file format analyzer. For Windows PE files, I prefer the CFF Explorer, because it has a good support for .NET file format.

From the PE header of this file, one important thing I observed is that this file is a 64 bit Windows Portable Executable file:
ch1_cff

This means that the file only can be executed on a 64 bit Windows system. If we have a 64 bit Windows system, that’s good, we can just run it and see what will happen (I suggest to run all the unknown files in a Virtual Machine because it can be a malware). But what if we don’t have one?

For installers, it usually embeds or compresses the target files into its resource section or its append data. Keep this principal in mind, you may easily find an unusual resource inside this file:
ch1_resource

As the picture shows, this resource is actually a Cabinet file (according to the magic string “MSCF” at the beginning of  the resource) which contains a suspicious executable named:
i_am_happy_you_are_to_playing_the_flareon_challenge.exe

You can extract the resource with any resource editor you like, and here I use the Resource Hacker to do this. Once we extracted the Cabinet file, we can decompress the file inside with a decompression tool like 7-zip. Finally, we can get the following file:

File Name: i_am_happy_you_are_to_playing_the_flareon_challenge.exe
Size: 641 bytes
MD5: 7e425436d50fd31d65e0dcf5506bf726
SHA1: 96ae5020d933de95f1327b8fc216e6a8b1cdb5c1
SHA256: eec103857458279f9fe0f3c962f12b33d5251710fc0f87100df5ab139fbb4141

File i_am_happy_you_are_to_playing_the_flareon_challenge.exe is also a Windows Portable Executable file, but not like the previous one, this one is a 32 bit program probably written in Assembly Language.

When you execute this file, it will ask for a password. And if a wrong password is provided, it will print out “You are failure” and exit:
ch1_output

So it is time to look at the disassembly code in IDA:
ch1_disass1

This part of the code dose the follow works:
1. Retrieves a handle of standard input and standard output by calling API GetStdHandle().
2. Prints out following string to the output device:
Let's start out easy
Enter the password>

3. Read from the input device for at most 0x32 bytes.

The second part of the code looks like below:
ch1_disass2

There is a magic jump located at 0x0040105B which determines whether we success or fail. To make it not to jump to the failure branch, we should make sure the first 24 bytes of the input XOR 0x7D equals to a sequence located at 0x00402140:
ch1_disass3

So we can do a simple reverse calculation by XORing each bytes from 0x00402140 with 0x7D to get the correct password, here is an IDA python script that can help:

from idaapi import *

addr = 0x00402140
length = 24
out = ''

for i in range(0, length):
    out += chr(Byte(addr) ^ 0x7D)
    addr += 1

print out

And the output of this script is:
ch1_ans

Obviously, there is a bug in this program because it allows us to enter more than 24 bytes data but only verify the first 24 bytes, here is a proof of concept:
ch1_poc

Posted in CTF | Tagged , , , | Comments Off on FLARE On Challenge (2015) #1