write-up for dont_panic
- Event: hxp CTF 2017
- Category: RE
- Points: 100
- Solves: 19
The goal of the task is to reverse engineer given binary and find a valid flag. The binary can be downloaded here
Reverse engineering the program
By inspecting strings of the binary it can be noted that the binary has been written in Go
language.
Googling some of the strings was enough to discover that.
A .gopclntab
section can be another indicator.
Programs compiled in Go are big and linked statically with go library. The first stage of our task is to find main function.
The binary is stripped so things are more complicated, but hopefully not much.
Function names in programs compiled in Go are additionally saved in .gopclntab
section. It means that function names can be recovered.
At this point I encourage you to read the entire article about how to recover stripped function names. It can be found here.
We can grab this script and paste it to the idapython window in IDA Pro
Now, when all functions have their appropriate names, we can navigate to main_main
(which in reality is main.main
, but IDA cannot into dots in function names) function - this is our main.
We can spot the messages which are shown when typing bad or good flag:
We can find out that the length of our flag
is greater or equal to 42
:
Below I have commented out the most interesting part of the code:
This can be represented as the following C code:
A fancy way to solve the task
The standard way of solving this would be to dig deeper and reverse the main_mapanic
function.
Instead of this, we can do it in some unusual way.
One can notice that, the longer the password is, the more instructions inside of main will be executed.
We can find out a way to count these instructions and then brute-force byte-by-byte the flag.
We can do this by instrumenting the binary. I used Intel’s Pin tool for this.
Counting executed instructions with Pin
Pin comes with a set of example modules that can be simply used.
After downloading Pin to folder /home/a/pintool
, the example modules can be found here:
There are several modules to count executed instructions. Their names start with inscount
.
We can compile them with command make all
. After this, the compiled modules can be found in a folder named obj-intel64
.
Then, we can use Pin to count our instructions:
Oh no! It looks like the binary is nondeterministic.
Hopefully the nondeterminism comes from library functions, not from the main()
itself.
The solution is simple.
We can modify our inscount0.cpp
module to count only instructions inside of main()
.
Or simpler.
It is enough to count only the instructions at address 0x47B96E
.
Now, I encourage you to see this presentation about Pin. It teaches how to write modules.
We can modify original script a bit. Here is a modified version.
The differences are following:
has been changed to:
and:
has been changed to:
Let’s check it:
It looks like it works.
Now, we need to wrap this in a nice python script:
The script took around 1 hour
to run on my laptop and gave me the flag: hxp{k3eP_C4lM_AnD_D0n't_P4n1c__G0_i5_S4F3}