Hack the Vote CTF 2016 Write Up

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)

ctf-hackthevote-2016-rev-100
Behold! The reverse engineering 100 challenge!

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!

Requirements

Process

  1. 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.

    ctf-hackthevote-2016-rev-100-fig-1
    Sad
  2. Nothing useful from there. Go ahead and decompile the ELF binary with the REC Decompiler, and then locate the main function.
    ctf-hackthevote-2016-rev-100-fig-2
  3. Alright. Now let’s look at the other interesting functions above the main function.
    ctf-hackthevote-2016-rev-100-fig-3ctf-hackthevote-2016-rev-100-fig-4ctf-hackthevote-2016-rev-100-fig-5ctf-hackthevote-2016-rev-100-fig-6
  4. 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.
    ctf-hackthevote-2016-rev-100-fig-2
  5. 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.
  6. 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 main function once the application execution gets there. To do this, just do a break main after entering gdb in order to schedule the suspension of the application’s execution immediately after entering the main function, and then do a run to start the program’s execution.
    ctf-hackthevote-2016-rev-100-fig-7
  7. Now, let’s try calling some functions. You can arbitrarily call functions in the ELF binary using a really neat command called call.
    ctf-hackthevote-2016-rev-100-fig-8
  8. Only real_help seems 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.
  9. Let’s just take a closer look at the other functions and see where they fail.
  10. Let’s do the dont_call_me function first. Since we already called the other functions, let’s start gdb again and call the dont_call_me function 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.
    ctf-hackthevote-2016-rev-100-fig-9
  11. The dont_call_me function failed as expected. By observing the back trace using the bt command, we can see that the program stopped inside a function that is inside the sub_41F2 function. Now, the sub_41F2 function only contains a handful of calls to other functions, so it would only be one of the following.
    ctf-hackthevote-2016-rev-100-fig-10
  12. Let’s see if it’s the strlen function. To check, simply add a break to strlen and then call the sub_41F2 function.
    ctf-hackthevote-2016-rev-100-fig-11ctf-hackthevote-2016-rev-100-fig-12
  13. As you see, the program stopped at the start of strlen. We then entered the finish command so that the program would execute until the strlen function finished. There doesn’t seem to have been any problems with the strlen function because the segmentation fault occurred AFTER it finished (as indicated by the segmentation fault message). That means, the problem occurs either on the m0 function, or the other strlen function.
  14. Let’s check the m0 function next. As you can see in the code below, m0 is actually a variable meant to contain a pointer to a function which is supposed to be called on the *m0(); line below.
    ctf-hackthevote-2016-rev-100-fig-10
  15. However, upon inspecting the contents of the m0 variable using the p command, it seems that it contains a reference to NULL so naturally the program WILL throw a segmentation fault for attempting to call NULL as a function.
    ctf-hackthevote-2016-rev-100-fig-13
  16. A quick search in the source code reveals that the m0 variable is initialised inside the c55 function.
    ctf-hackthevote-2016-rev-100-fig-27
  17. However, the c55 function is never actually called anywhere and directly calling the c55 function causes and infinite loop.
    ctf-hackthevote-2016-rev-100-fig-14
  18. Let’s restart gdb to get rid of the breakpoints and then let’s try setting the m0 variable directly.
    ctf-hackthevote-2016-rev-100-fig-15
  19. Now let’s try calling the dont_call_me function again.
    ctf-hackthevote-2016-rev-100-fig-16
  20. Looks like it’s still broken. Buuuuuuut… look carefully at the decompiled code. There’s another malloc function there residing on a different address. This might be the malloc function that’s being set to the m0 variable.
    ctf-hackthevote-2016-rev-100-fig-17
  21. Now let’s set m0 to that address. Good thing the decompiler outputs the addresses of decompiled functions (as indicated in the comment below the declaration of the malloc function).
    ctf-hackthevote-2016-rev-100-fig-18
  22. Now let’s call dont_call_me function again.ctf-hackthevote-2016-rev-100-fig-19
  23. >mfw

    ctf-hackthevote-2016-rev-100-fig-20
    MOM, GET THE CAMERA! #MLG #360NOSCOPE #GITREKT
  24. It works. But there doesn’t seem to be anything useful in the dont_call_me function. Let’s try the other functions.
    ctf-hackthevote-2016-rev-100-fig-21
    Illuminati Confirmed? [Insert X-Files Theme Music]
  25. It seems the other functions started working once we set the m0 variable to the right value. Now search for m0 in the source code and you will find that the functions that reference m0 are sub_41F2, sub_9F36, and sub_198A. The functions that, in turn, reference these functions are dont_call_me, help, c8, and fake_help. We haven’t tried c8 yet, so we just might find the answer there.

    ctf-hackthevote-2016-rev-100-fig-22
    What’s that? “Jet fuel can’t melt steel beams”?
  26. Gibberish. But to be sure, let’s do it again.

    ctf-hackthevote-2016-rev-100-fig-23
    “Harambe was an inside job”?
  27. Different gibberish. I wonder what will happen if we keep doing it over and over again.

    ctf-hackthevote-2016-rev-100-fig-24
    “Lizard people are running the White House”!?
  28. Suddenly… [insert x-files theme music]

    ctf-hackthevote-2016-rev-100-fig-25
    The plot thickens…
  29. That’s not the flag though. We need to go deeper.

    ctf-hackthevote-2016-rev-100-fig-26
    Voila!
  30. And finally, we have our flag, which is flag{write_in_bernie!}.

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.

Attachments

Binary
Decompiled Source Code

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s