[H4ckIT 2016] [REVERSE 135 – BiTwins] Write Up

Description

EN: These crazy twins are driving me crazy! They know something I don`t. They are simply playing with me!

Resolution

In this challenge we are getting not one but TWO binaries to reverse.
That was a first time for us in a CTF.

Those binaries were almost identical,
you need to input 2 arguments, “twin” is the first for both binaries, “#1st” and “#2nd” is the second one. it clearly explain who you should be running first. (detail explanation is just a few line under)
the aim was to make them talk to each other, threw a shared file (‘BiTwins.bi’), where each binary would write one letter at a time, turn by turn.

There was a lots of ways to solve this challenge, we’ll give you three.
– The dumb way
– The easy way
– The “lets finally learn IDAPython” way
you could also have tried to decode both hardcoded string inside the binaries as it was a simple reverse-lookup table in the function at 0x400CF9

As we’ll make this WriteUp about IDA Python, we’ll try to explain this in a “tutorial” way.

Let’s begin :

Part 1 – initial dissas & getting the args right

If you’ve never used IDA before, one of the great advantage of IDA is that you can run it from almost any evironment (Win,linux,osx) and debug/dissas any kind of binary (PE,ELF,Mach-O).
thanks to the “remote servers” you’ll be able to connect to another machine running the right OS, and debug it from there.
We’ll be using IDA under windows for this matter, with the linux_serverx64 to remotly connect to the elf binaries.
IDA is a great tool, please support the devs buy buying their software : https://www.hex-rays.com/index.shtml

the config is pretty basic, you need to run linux_serverx64 on your linux machine, that will open a socket (default port is 23946. you can add a password with -P)
serv

under IDA, “Debbuger” menu>”Process Options”

remote
the first two lines should be the full path to the binary on your “remote” machine
the third is the directory
the rest is pretty self explanatory.

one important thing, if your path doesn’t point to the binary, IDA will ask if you want to upload the binary currently being used in IDA.

now that we are able to run the binary, let’s start the dissas.
dissasida

openning the “main” function, we can quickly see that :
– 0x400A75 is comparing the number of arguments to 2, and will terminate if it is lower or equal (JLE 0x400D0A). So we need to provide 3 arguments, the first beeing to binary name, we have to give it 2 more.
– 0x400A92 will compare”twin” with our Arg1
– 0x400AEE will check the size of arg2 and will terminate if it is “lower or equal to 3”
-0x400AEC is were the binary will check our arg2 against a hardcoded value, byte by byte. if it is a match is will finally run the main part of the challenge.

Part 2 – Dissas of TwinIsGonnaRunHisLifeCycle

 

dissasdataxchange
So, what we have here is some kind of a conversation between the 2 binaries using a shared file “BiTwins.bi”
it first try to open and read the file (0x400B40),
if it succeed it will read a char from it (0x400BEC) and compare it to the last char he had read.
if it is the same char, he will sleep and retry, if it’s a new char he’ll go on and write his next char.
That is a very crappy way to do things, because that means that we can’t have to two binaries “talk” the same char or they will both ends up in an infinite loop. that also means that we will not have the same char twice, wich will be good for our “crappy solution” :p

Let’s solve this : The easy way

we now know how to run both binaries (with the good arguments), and we know that they will write there char inside “BiTwin.bi” and then sleep 1 sec each time.
we can make a simple reader and store those values with a one liner :

while true;do cat BiTwin.bi>>flag;sleep 1;done

you would have some char that are read twice in your final sentence, so to clean it up :

f=open('flag','rb')
myflag=f.read()
f.close()
last_char=''
full_flag='h4ckit{'
for char in flag: 
    if char!=last_char: 
        full_flag+=char
    last_char=char

We are lucky that the way they implemented it, we can have the same char twice. without it the cleaning part would have been more difficult.
that’s why we looked for an other way

Let’s solve this : The dumb way

As you can see, on 0x400C29, you’ll have passed the “it’s still the same char” conditions. so the value in EAX will be the Char that was sent by the other binary
then again, on 0x400C4D, you’ll have generated your own next char and will be ready to send it. EAX will also contain your next char value.
so by putting breakpoints to those 2 adresses, and letting the second binary run freely, you’ll break at each char and can manually collect those chars.

that’s the manually part that is dumb, and the creator made sur that it would be a pain in the ass for us if we were to chose this path, as the output is more than 50 char long.

Let’s solve this : The “lets finally learn IDAPython” way

Clearly, we already had the flag threw our easy way, but we’ve been looking at malware reverse tutorial for quite some times now and those guys use a lot of IDAPython.
we always wanted to try, but never took the time. this was the perfect opportunity.

IDAPython allow us to use python inside IDA (what a shocker ;p)
https://github.com/idapython
just download the right binary for you (right IDA and Python version – https://github.com/idapython/bin)
Copy the whole “python” directory to %IDADIR%
Copy the contents of the “plugins” directory to the %IDADIR%\plugins\
Copy “python.cfg” to %IDADIR%\cfg
After that, is was still unable to make it works. i had to add some path to the PATH environnement variable:
C:\Python27;C:\Python27\Scripts;C:\Python27\Library\bin;
now restart IDA, and in “File”>”Script Command” you should now be able to change the scripting language from IDA’s IDC to Python
what we will do here is pretty similar to the “dumb way”, but we will capture the EAX value and transforme it into our flag automatically.

import idc
import idautils
import idaapi

final=''

def BreakpointHandlerPrintEAX():
    global final
    target = GetRegValue("EAX")
    final+=chr(target)
    print final


def main():
    func_input = 0x00400c29     #Get Char from Binary1
    func_output = 0x00400c4d  #Get Char from Binary2
    
    #Lets us use python functions for breakpoint conditions
    RunPlugin("python", 3)
    
    AddBpt(func_input)
    AddBpt(func_output)
    
    SetBptCnd(func_input, "BreakpointHandlerPrintEAX()")
    print("Breakpoint at: %x" % func_input)
    
    SetBptCnd(func_output, "BreakpointHandlerPrintEAX()")
    print("Breakpoint at: %x" % func_output)

    
if __name__ == '__main__':
    main()

instead of breaking at our breakpoints, our Handler will be called, and he will add the char to our final string.

output

 

Flag was h4ckit{R3v3R53_T4k35_2_MvCh_T1m3_Wh3N_U_DrUnK}

Leave a Reply

Your email address will not be published. Required fields are marked *