The-FLARE-On-Challenge-2015/Challenge-2

From aldeid
Jump to navigation Jump to search
You are here
Challenge 2

File

Uncompress 599EA8F84AD975CFB07E0E5732C9BA14.zip (password is "flare") and you will get a file named very_success with following properties:

MD5 d88dafdaefe27e7083ef16d241187d31
SHA1 9dfe72f31fa8fa131e5f75fe99adcc1f9b3deca2
SHA256 9852afb172bc03a50d291c70faa724c69a10af9e6ee88457185ce5e0705216f0
File type PE32 executable (console) Intel 80386, for MS Windows

Code analysis

sub_401000

A cross references to the strings "You are success" and "You are failure" leads to the sub_401000 function where we can start the analysis.

The user buffer is read and saved to a buffer (unk_402159):

.text:00401038 lea     eax, [ebp-4]
.text:0040103B push    eax             ; lpNumberOfBytesRead
.text:0040103C push    32h             ; nNumberOfBytesToRead
.text:0040103E push    offset unk_402159 ; lpBuffer
.text:00401043 push    dword ptr [ebp-0Ch] ; hFile
.text:00401046 call    ReadFile

The function sub_401084 is then called with the following 3 parameters:

.text:0040104C push    0               ; lpOverlapped
.text:0040104E lea     eax, [ebp-4]
.text:00401051 push    eax             ; lpNumberOfBytesWritten
.text:00401052 push    11h             ; nNumberOfBytesToWrite
.text:00401054 push    dword ptr [ebp-4] ; length
.text:00401057 push    offset unk_402159 ; userBuffer
.text:0040105C push    dword ptr [ebp-10h] ; encryptedBuffer
.text:0040105F call    sub_401084

sub_401084

This function is manipulating the user input with a variety of transformations applied to the characters and then compares the result to a buffer. You will be equally successful by reversing the encryption algorithm or by brute forcing it. My script is brute forcing the algorithm to solve this challenge. Refer to the comments in the script for more information.

Script and solution

Here is the script I wrote to solve this challenge:

#!/usr/bin/env python
def split_reg(reg):
    #reg16 = reg & 0xFFFF
    #reg8h = reg16 >> 8
    #reg8l = reg16 & 0xFF
    #return (reg16, reg8h, reg8l)
    reg8h = reg >> 8
    reg8l = reg & 0xFF
    return (reg8h, reg8l)

def make_reg16_from_hl(reg8h, reg8l):
    return reg8h*256 | reg8l

rol = lambda val, r_bits, max_bits=8: \
    (val << r_bits%max_bits) & (2**max_bits-1) | \
    ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))


# .text:0040108C xor     ebx, ebx        ; EBX = 0
bx = 0
# .text:0040108E mov     ecx, 25h        ; ECX = 0x25 (37 chars)
cx = 0x25
# .text:00401093 cmp     [ebp+length], ecx
# .text:00401096
# .text:00401096 loc_401096:             ; if len(userBuffer)<37: fail
# .text:00401096 jl      short loc_4010D7

# .text:00401098 mov     esi, [ebp+userBuff] ; ESI = userBuffer
userBuffer = []

# .text:0040109B mov     edi, [ebp+encryptedBuff] ; EDI = encryptedBuffer
# encrypted buffer is read in reverse order. It has already been reversed here
encryptedBuffer = [0xa8, 0x9a, 0x90, 0xb3, 0xb6, 0xbc, 0xb4, 0xab, 0x9d, 0xae, 0xf9, 0xb8, 0x9d, 0xb8, 0xaf, 0xba, 0xa5, 0xa5, 0xba, 0x9a, 0xbc, 0xb0, 0xa7, 0xc0, 0x8a, 0xaa, 0xae, 0xaf, 0xba, 0xa4, 0xec, 0xaa, 0xae, 0xeb, 0xad, 0xaa, 0xaf]

stack = []

# initial value for AX provided by OllyDbg
ax=0xff84
dx = 0

for c, i in enumerate(encryptedBuffer):
    # backup registers
    saved_ax = ax
    saved_bx = bx
    saved_cx = cx
    saved_dx = dx

    # Brute force
    for probed_char in range(255):

        # .text:004010A2 mov     dx, bx          ; DX = BX
        dx = bx
        (dh, dl) = split_reg(dx)

        # .text:004010A5 and     dx, 3           ; DX = DX & 0x3
        dx = dx & 0x3
        (dh, dl) = split_reg(dx)

        # .text:004010A9 mov     ax, 1C7h        ; AX = 0x1CF
        ax = 0x1C7
        (ah, al) = split_reg(ax)

        # .text:004010AD push    eax             ; push EAX to stack
        stack.append(ax)

        # .text:004010AE sahf                    ; set CF to 1 because AH = 1
        cf = 1

        # .text:004010AF lodsb                   ; EAX = userBuffer[i]
        al = probed_char
        ax = make_reg16_from_hl(ah, al)

        # .text:004010B0 pushf                   ; Store flags register to stack
        # .text:004010B1 xor     al, [esp+4]     ; al = userBuffer[i] ^ 0xC7
        al = al ^ 0xC7
        ax = make_reg16_from_hl(ah, al)

        # .text:004010B5 xchg    cl, dl          ; (ECX <-> EDX)
        saved_dx = dx
        dx = cx
        cx = saved_dx
        (ch, cl) = split_reg(cx)
        (dh, dl) = split_reg(dx)

        # .text:004010B7 rol     ah, cl          ; EAX = ROL(EAX, ECX)    
        ah = rol(ah, cl)
        ax = make_reg16_from_hl(ah, al)
    
        # .text:004010B9 popf                    ; Retrieve flags register from stack
        # .text:004010BA adc     al, ah          ; EAX = AL + AH + CF
        al = al + ah + cf
        ax = make_reg16_from_hl(ah, al)

        # .text:004010BC xchg    cl, dl          ; ECX <-> EDX
        saved_dx = dx
        dx = cx
        cx = saved_dx
        (ch, cl) = split_reg(cx)
        (dh, dl) = split_reg(dx)

        # .text:004010BE xor     edx, edx        ; EDX = 0
        dx = 0
        (dh, dl) = split_reg(dx)
    
        # .text:004010C0 and     eax, 0FFh       ; EAX = EAX & 0xFF
        ax = ax & 0xFF
        (ah, al) = split_reg(ax)    
    
        # .text:004010C5 add     bx, ax          ; BX = BX + AX
        bx = bx + ax
        (bh, bl) = split_reg(bx)    
    
        ### Testing
        if ax == encryptedBuffer[c]:
            #print "found letter for index %s: %s" % (c, hex(probed_char))
            userBuffer.append(probed_char)

            # .text:004010C8 scasb                   ; ZF = AL == BYTE PTR[EDI]
            # .text:004010C9 cmovnz  cx, dx          ; IF ZF == 0:DEST = SOURCE
            # .text:004010CD pop     eax             ; EAX = value pushed to stack at 0x4010AD
            ax = stack.pop()
    
            # .text:004010CE jecxz   short loc_4010D7 ; if ECX=0; goto fail

            # .text:004010D0 sub     edi, 2          ; EDI = EDI - 2
            # .text:004010D3 loop    loc_4010A2
            cx -= 1

            break
        else:
            #print "[KO], trying next letter..."
            # Roll back registers
            ax = saved_ax
            bx = saved_bx
            cx = saved_cx
            dx = saved_dx

print ''.join([chr(i) for i in userBuffer])

Below is the output of my script:

$ ./script2-bf.py 
[email protected]

You will receive the following answer along with a 15M file attached:

Great job, you're really knocking these out! Here's the next binary for your goaty enjoyment. The password to the zip archive is "flare" again.

Keep up the good work, and good luck!

-FLARE

Comments

Keywords: reverse-engineering challenge flare fireeye