Packed Exception

Hope you all enjoyed playing InCTF 2018. We had a variety of challenges this year.

this year I made the challenge Packed Exception along with @k4iz3n (I think I should have added an Avengers spoiler alert!! tag in the description :P) .

Source code of the challenge can be downloaded from this link. : Packed Exception

FilePackedException

The challenge was ELF 64-bit binary which was statically linked and packed using a  custom packer.

On opening the packed binary in IDA, in the start function we can see that the address  dword_400000 is getting stored in edi register and the length 0x1b012b is getting stored in esi register and are passed as argument to sys_mprotect.

packedStart

We can write a simple python script to automate the unpacking and dump the unpacked binary.


import sys
import lief
import time
import subprocess
from tqdm import tqdm

def unpack_bin(org_bin, text_offset, text_size, oep):
    print "Unpacking " + org_bin
    binary = lief.parse(org_bin)
    header = binary.header
    header.entrypoint = oep
    binary.write(org_bin)
    print "Offset reset"

    with open(org_bin, "rb") as f:
        check_pack = bytearray(f.read())

    for i in tqdm(xrange(text_size)):
        check_pack[text_offset + i] ^= 0xa5

    with open(org_bin + ".unpacked", "wb") as f:
        f.write(check_pack)

def main(argv):
    org_bin = argv[1]
    unpack_bin(org_bin,0x5b0,0x15eeec,0x400ea0)

if __name__ == "__main__":
    sys.exit(main(sys.argv))

 

When we analyze the unpacked binary in a disassembler, In the main function input is taken from user, the length of the input is calculated and compared in  the function htgnel(0x401c67), if length check is passed then return value is 0 and if it fails then the return value is 1 and two messages (“Oops You Didn’t think properly” and “You Lose”) are printed out.

After going through the length function one can find out that length of the input must satisfy the below equation:

((length <<10 ) % 1000) == 432

After solving the above equation we find out that length of the input must be 18.

After the length check, etaluclac function is called in a loop which takes pointer which points at the starting address of the input array and an integer (the value changes from 1-6 based on the iteration of the loop) as argument, if the return value of etaluclac is non zero then the counter is incremented by 1, otherwise a naag message is printed out.

calcreturn.png

Inside the etaluclac function there 6 functions each checking a part of the input. each of the function checks 3 characters of the input and a function is called based on integer argument which is passed to etaluclac function.

 

First check function is called IamGroot() it just compares the first 3 character of the input with “T#@” using strncmp().

 

Second function is called TonyStark() it’s also a simple check which compares 7th, 8th and 9th character in reverse using strncmp() with “!k_” it means that the correct input should be “_k!

Third check function is called  WarInTitan(), it checks for 4th, 5th and 6th character of the input by xoring each character with 0x12345 and comparing each character with a constant value. the check function looks similar to the below code.

flag = 1
constant = 0x12345
#input contains 4-6 character of the actual input
for i in xrange(len(input)):
    temp = (ord(input[i]) ^ constant)
    if i == 1:
        if(temp != 74539):
            flag = 0
    if i == 2:
        if(temp != 74613):
            flag = 0
    else:
        if(temp != 74593):
            flag = 0

after reversing the code we can find out that the input characters should be “n0$” .

 

Fourth function is called WakandaForever(), it checks for the last 3(16, 17 and 18) characters of the input by xoring each character by 4 and comparing it with the string “gt]” after reversing the xor operation we find that the correct input should be “cpY“. So currently we know that our input looks like this:

 "T#@n0$_K!------cpY"

where ‘-‘ denotes the character that we still need to find.

 

Now we move to the fifth check the function is called VisionDies() (I apologize for giving spoilers :P) it adds 2 to every character of the input and compares it with the string “w5z”, therefore the correct input should be “u3x”. Once we give that 5th check is passed.

 

The Sixth and final check is called TheFinalBattle(), in this check each character is xored with 30 and and 3 is added to the result and it is compared to the string “elm” .

it can easily be reversed using a basic script

a = 30
constantString = "elm"
input = ""

for i in constantString:
    input = input + chr((ord(i) -3) ^ 30) 

print input

we get “|wT” as input and the final check is passed.

 

now we have passed all the 6 check and hence have the correct input:

T#@n0$_K!|wtu3xcpY

giving it as the input gives us the correct flag:

screenshot-from-2018-10-16-15-10-49.png

Flag: inctf{T#@n0$_K!||$_w!t#0uT_@nY_3xc3pT!0n}

 

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a free website or blog at WordPress.com.

Up ↑

%d bloggers like this: