Friday, May 31, 2019

Solving my first (Ghidra) reverse engineering challenge

I was pointed to this challenge by this article, and had heard about the NSA's new Ghidra reverse engineering tool. I was able to solve it without reading much of the article! Here's what I did.

Setup: They give you a compiled ELF binary that runs a program that prompts you for a password. It will tell you if you guessed correctly.

I was going to use this as a change to learn the Ghidra tool, with help from the article.

First problem: It's compiled to run on Linux Debian x86. I was doing it on a MacBook. So, it wouldn't run.

I noticed that Ghidra offers you the option to export the binary as C code (which makes sense, as part of Ghidra's reverse engineering is to convert a binary into assembly and slightly-more-readable C-like code). So, I figured I could just compile that to run on my Mac.

It didn't work though, since Ghidra exports an incomplete version of the code that uses some invalid types, and doesn't define all of its labels properly, which required a lot of manual work that was increasingly appearing like it would take too long.

So I figured I'd just run the binary in a Docker container. I found the Debian i386 version and pulled it down. (I had a long side diversion here getting a setup so I could edit files on my machine that would appear as I want in the Docker container, but the details are uninteresting besides this clever hack for getting the "copy file" command into your docker one-liner.)

So, I was able to run the binary.

Second problem: Somehow, it thought I was using a "debuguer" -- which I would be, at some point, but is strange, because I wasn't using one yet:


Don't use a debuguer !


I looked through the decompiled code to see where it might be doing this and found:

lVar1 = ptrace(PTRACE_TRACEME,0,1,0);
if (lVar1 < 0) {
    puts("Don\'t use a debuguer !");
    /* WARNING: Subroutine does not return */
  abort();

Hm, okay, well that "if" statement maps to this part of the assembly:


0804868c 79 11 JNS LAB_0804869f

That is, according to this handy reference, "Jump if not sign." Well, whatever "sign" is, I want it to do the opposite -- change "JNS" to "JS", so it "jumps if sign" and skips that block -- the one that gives the mean message and exits the program.

I haven't figured out a good way to format that line, but 0804868c is the (hexadecimal) location within the binary, 79 11 is the hex version of the binary command itself, JNS the assembly term (just a mnemonic device) for that command, and LAB_0804869f is the argument passed to the command -- in this case, a label for where to jump to in the program.

According to that spec, the JNS command maps to 79, and if I want it to be JS, I need to replace it with 78. But...

Third problem: I don't have an easy way to edit binaries.

A convenient way to look at them is as a hex(adecimal) dump in a "hex editor", but I hadn't done that for a few months. Hex editors are useful because they show you the raw hex, the offset where it appears in the file, and, off to the side, the ASCII/text equivalent of that hex. Fortunately, I found this comment, showing how to repurpose my text editor, vim, to double as an easy hex editor. I can just open it up, edit the hex values, save, and it updates everything.

You can then run a utility, "gobjdump", to see the code as assembly and verify that e.g. JNS changed to JS as expected.

Fortunately, when I ran that edited binary, it did what I wanted: not accuse me of using a debugger, and proceed with the rest of the program.

The rest of the hurdles are similar: there's some if-test that can send the execution into a block that you don't want it to. Fortunately, this binary is structured ... stupidly, from a security perspective. It has that same kind of if-block for validating whether you entered the right password. Right afterward, if the check succeeded, it prints the password. Not your input, no -- it prints the correct password, which you can later validate on an unmodified run.

So, it's just a matter of tweaking the code so that the you always enter the block that outputs the password. (They were smart enough not to have the password itself appear as an obvious string in the binary, at least.)

Once I saw the output, I could submit it at the challenge site and very it was correct.

Now for something harder!