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
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.
file and found out its a 64-bit executable
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.
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:
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
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:
It concatenated the strings to
iHateRustTooin the main function as mentioned before.
In the big loop, it takes a character from the
myString[len(myString)-1]concatenates them into a string
It generates sha256 of this string
Compares the the generated sha256 with a string stored in a table of sha256 strings
Part of the table:
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
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.