The constant back-and-forth between hacking and prevention requires each side of the game to continually innovate, and the progressions are really very fascinating. It’s a hugely broad field with countless vectors to consider, and here I’ll execute just one proof of concept: a software attack on an “authenticated” hello world binary.
Making the Program
Step 1 is to actually get a program to crack. In this experiment, I’ll use a program that expects a password. If the password is not provided it exits with an error, if the password is wrong no action is taken, and if the password is correct it prints the string “Access granted: Hello World!”.
This source is on Github, otherwise copy the following makefile and source to play along.
To get started, run make from the directory in which the makefile and source exist. This shouldn’t require anything more than GCC and make.
Run the application a few times, notice that it does actually work, and only prints “Access granted: Hello World!” when the password is “please”.
Examining the Assembly
Beauty, now we have a compiled binary. In real life, if we were going to look for vulnerable places in an application, it’d be hugely helpful to examine the source code used to create it, but that’s not always provided. It’s crucial to realize, though, that even the binary is human readable with help from tools like objdump, albeit less friendly on the eyes than C. Now, use objdump to see what the compiled application looks like.
The output isn’t easy for anyone to read, but looking for expected markers will make it much simpler. To start, look for “main” (the only output I’ve included above). That’s the assembly code generated for the main function of our application. Looking through main, there’s one instruction that sticks out:
The strcmp operation is the comparison done between the secret password and the provided guess. If that wasn’t obvious, the move operation on something named “secret” 6 instructions earlier is a good clue.
Hacking the Assembly
Now imagine that you were handed this compiled program with no knowledge or way to learn the secret password, and your task is to get the secret message to print. How would you do it? It’d be nice for the attacker if the application didn’t accept one secret password, but instead accepted any nonsense given as proper authentication. Above, we already found what’s likely the password check (strcmp). The jump not equals (jne) instruction following close behind is surely the action taken based on the result of the check.
So, the properly working application jumps when the provided string is not equal to the secret password. It’d be really nice if the program instead did the exact same jump for any provided string. Based on our research so far, it’s a great presumption that changing that instruction from jump not equals (jne, opcode 75) to jump equals (je, opcode 74) will do exactly that (really, that will cause the program to jump on any string that isn’t the password, but it’s a fair assumption that I, the attacker, won’t randomly choose the actual password!).
To make this edit, I’ll use emacs’ built-in hex editor. Use hexl-find-file to open main in hex mode, then search for the assembly to edit. Thankfully, the surrounding opcodes “75 1c” make the location unique and easy to find.
Change the opcode 75 to a 74 and save.
Now, rerun the application and see that any nonsense passes as authentication; the program passes out “Hellos” to anyone and everyone! You can also see exactly what has changed by running objdump again.
That’s Bad! How to Prevent
A common prevention is to use hashes to ensure that the binary being run is just as the author intended. For example, if I were distributing this binary as a piece of software, I could include along with it the SHA1 hash “322a9d4f1186fab62fce0145c18e62c35533669c”. Each time someone obtains a copy, he or she would derive the hash independently and check that it matches. If not, there’s a problem! Notice that changing just the single instruction of the binary effects the SHA1 hash in a very noticeable way.
Before the assembly edit:
After the assembly edit:
Many large, professional applications achieve the same safety with code signing certificates. It’s slightly more complex and includes a verification of the author’s identity, but the general idea is the same.
Given the general abuse of the word “hack” I feel obligated to mention that not all hacking is done in the form of attacking, or even in the realm of security at all. Despite my use here, I generally refer to Stallman’s distinction between “cracking” and “hacking”.
This experiment was built and executed on Arch Linux with GCC 4.9 (Prerelease) and Emacs 24.3.1.