Okay. Super late write up right here.
Anyway, as you, dear reader, may not be aware of, me and some friends participated in the Hack the Vote CTF 2016 this last November. I wasn’t able to work on the puzzles as much as I wanted to because of work obligations, but I was able to solve a really interesting reverse engineering problem that I think you might find interesting.
Reversing 100 (Consul)
As a greenhorn in the magnificent field of reverse engineering, this was the only one that I was able to manage in the short amount of time that the challenge was open, but it’s better than nothing I guess, and I believe that other greenhorns such as myself would learn very much from this very simple example of reverse engineering. And so, off we go!
- REC Decompiler (a decompiler obviously)
- gdb (a debugger)
- Download the ELF binary by clicking the “consul” link the description of the challenge and try running it. We want to be able to observe what the binary does normally before we break it down into tiny little pieces.
- Nothing useful from there. Go ahead and decompile the ELF binary with the REC Decompiler, and then locate the main function.
- Alright. Now let’s look at the other interesting functions above the
- These, aside from the main function, are the only ones whose names are not gibberish, so it may be possible that the solution to the problem might indeed be in one of these functions, but if you look at the main function again, it doesn’t seem to access any of the functions above.
- Luckily for us though, we can arbitrarily call functions from ELF binaries by using the wonderful tool called “gdb” – also known as the GNU Debugger. So just go pass the ELF as a parameter to the tool in question, and we’ll be doing the magic along the way.
- So, first, we’d want to be able to get a hold of the application in it’s initial state, so we are going to have to stop at the
mainfunction once the application execution gets there. To do this, just do a
break mainafter entering gdb in order to schedule the suspension of the application’s execution immediately after entering the
mainfunction, and then do a
runto start the program’s execution.
- Now, let’s try calling some functions. You can arbitrarily call functions in the ELF binary using a really neat command called
real_helpseems to work. I think it was trying to give people a clue. Something about the Fibonacci sequence, but I never really understood the purpose, and I was able to finish the challenge even while ignoring it.
- Let’s just take a closer look at the other functions and see where they fail.
- Let’s do the
dont_call_mefunction first. Since we already called the other functions, let’s start gdb again and call the
dont_call_mefunction so we’d avoid any of the possible unintended modifications that the other functions may have made to the environment when we tested them previously.
dont_call_mefunction failed as expected. By observing the back trace using the
btcommand, we can see that the program stopped inside a function that is inside the
sub_41F2function. Now, the
sub_41F2function only contains a handful of calls to other functions, so it would only be one of the following.
- Let’s see if it’s the
strlenfunction. To check, simply add a
strlenand then call the
- As you see, the program stopped at the start of
strlen. We then entered the
finishcommand so that the program would execute until the
strlenfunction finished. There doesn’t seem to have been any problems with the
strlenfunction because the segmentation fault occurred AFTER it finished (as indicated by the segmentation fault message). That means, the problem occurs either on the
m0function, or the other
- Let’s check the
m0function next. As you can see in the code below,
m0is actually a variable meant to contain a pointer to a function which is supposed to be called on the
- However, upon inspecting the contents of the
m0variable using the
pcommand, it seems that it contains a reference to
NULLso naturally the program WILL throw a segmentation fault for attempting to call
NULLas a function.
- A quick search in the source code reveals that the
m0variable is initialised inside the
- However, the
c55function is never actually called anywhere and directly calling the
c55function causes and infinite loop.
- Let’s restart gdb to get rid of the breakpoints and then let’s try setting the
- Now let’s try calling the
- Looks like it’s still broken. Buuuuuuut… look carefully at the decompiled code. There’s another
mallocfunction there residing on a different address. This might be the
mallocfunction that’s being set to the
- Now let’s set
m0to that address. Good thing the decompiler outputs the addresses of decompiled functions (as indicated in the comment below the declaration of the
- Now let’s call
- It works. But there doesn’t seem to be anything useful in the
dont_call_mefunction. Let’s try the other functions.
- It seems the other functions started working once we set the
m0variable to the right value. Now search for
m0in the source code and you will find that the functions that reference
sub_198A. The functions that, in turn, reference these functions are
fake_help. We haven’t tried
c8yet, so we just might find the answer there.
- Gibberish. But to be sure, let’s do it again.
- Different gibberish. I wonder what will happen if we keep doing it over and over again.
- Suddenly… [insert x-files theme music]
- That’s not the flag though. We need to go deeper.
- And finally, we have our flag, which is
And that’s it for now folks. I hope you enjoyed this CTF write up despite it being a month late. I surely did enjoy writing it. Anyway, if you need the binary and the decompiled source code, I have attached them onto the section below.