I read the challenge name, I act like its nothing, I download the challenge, use strings on it to make sure, please don't be it, find that it is what I think it is, its a Go binary. shoots self

string

I have been struggling with Go compiled binary challenges for a while until the qualification round for this CTF that had a Go binary challenge, which made me take the decision that I should study specifically for it, I will put the resources that helped me most in the references.

Used file and found out its a 64-bit executable

file

Opened the executable in IDA, and it took sometime to analyze the binary since Go binaries are very large compared to C binaries, as Go compiler generates a single ready to run binary by packing everything inside it, so using any library (packages in this case), you will find all the functions inside it.

After waiting for around 3 minutes, IDA has finished analyzing the binary, and to my surprise the function names and everything was there, usually in Go challenges, the names are stripped and you had to rename them by using the symbol table in the file.

Looking at the functions you will notice a pattern, there is always a something_functionName, the something here is the package name and then the function name. In Go, everything should be in a package, and there is the main package, that's what runs first.

functions

You can see there are 3 functions in the main package, main_main is the main function, it runs after everything has been initialized, so we start by analyzing it.

It first checks for the number of arguments you enter and prints some strings if you inputted a wrong number of parameters:

main

0 args: y no args
1 arg: one more pls
3 args: thats too much !!
4 args: Stoooop it !!!!!

You can keep increasing number of arguments, but as you see its just making the binary angrier, so we deduce it requires two arguments.


It takes your two arguments and concatenates them together, and then stores the generated string, so looks like that we are entering the flag? lets see.

The binary then starts a go-routine with some process, which we find out is main_not_log

Looking into that function, and it contains some logic and I guess that it must be operating on the strings inputted by me.

I found out it loops for 73 iterations operating on my inputted strings, and the process is as follows:
Assuming we inputted two args: iHate, RustToo

  1. It concatenated the strings to iHateRustToo in the main function as mentioned before.

  2. In the big loop, it takes a character from the myString[0] and myString[len(myString)-1] concatenates them into a string

  3. It generates sha256 of this string

  4. Compares the the generated sha256 with a string stored in a table of sha256 strings

    The check:

    Part of the table:

  5. Sleeps for a random number of seconds and this must be here to prevent tools like angr

Now every think looks clear, we have to bruteforce the characters that are equivalent to those hashes. I couldn't extract the hashes table from the binary statically as I didn't know how the code indexed them, and I spent some time trying to know how it was indexed but with no success, so I decided to just dump them dynamically.

First I had to patch the time_sleep function:

And then I wrote this ida script to extract them:

hashes=[]
ea=0x637E26 #The address where it checks 
while True :
    idc.RunTo(ea)
    GetDebuggerEvent(WFNE_SUSP, -1)
    time.sleep(2)
    address= idc.GetRegValue("RSI") #RSI has the address of the required string
    hashVal=idc.GetManyBytes(add,0x40)
    hashes.append(val)

It simply runs to the address, extracts the string and puts into an array. A little thing should be mentioned here, when you run this script you have to input 2 arguments where length of each one ≥ 37, in order to get all the hashes as there are 74 hashes. I later found out that was dumb from my side, since I only needed only half the hashes since it check from both ends

After extracting the hashes, I bruteforced for the required input:

import string
import hashlib
def generateSHA256(hash_string):
    sha_signature = \
        hashlib.sha256(hash_string.encode()).hexdigest()
    return sha_signature
chars=string.printable
firstPart=""
secondPart=""
index=0
found=False
while index<35:
    for i in chars:
        for j in chars:
            string=i+j
            shaVal=generateSHA256(string)
            if(shaVal==hashes[index]):
                index+=1
                found=True
                firstPart+=i
                secondPart=j+secondPart
                break
        if found== True:
            found=False
            break
print firstPart+secondPart

For all the printable characters, it tries all permutation of size 2, generates a sha256 hash, checks it with the hashes table, if found then we found two chars, the script could be optimized a lot, but there was no need as the complexity was just 100*100*35=350000, which is nothing.

Running the second part of the script generates the required input to pass the checks, which as I guessed from the beginning, was the flag:

That's the story of how I solved my first Go challenge.