*Note: This post is for education purposes only.*
I’m always eager to learn new things. I came across a really amazing resource online: SecurityTube. Within their trove of great videos is a series on the GNU disassembler (gdb). Those 14 videos are really detailed and fascinating. By the end of it I was wanting to do a write up and show a bit of what I learned.
If you’re unfamiliar with gdb, here’s some basic info on it… this post will give a super brief intro to it, and go over some of the content taught in the course listed above.
An example application is provided, and we’ll make use of gdb to find a way to trick the application into thinking we’ve been authenticated.
What is GDB?
GDB (command: gdb) is a Linux based decompiler. It can attach to any currently running process (by its PID, although that requires root privileges), as well as local binaries on the system.
What benefit is it?
Much like IDA or Immunity Debugger, gdb allows us to take a peak into the memory registers and follow the flow of an application. Some practical uses could be software forensics, to get an idea of a software’s nature and see if it has malware characteristics.
An application (even if closed source) could be toyed with and hacked by setting our own values in the available registers – allowing us to access parts of an application otherwise unaccessible.
What platform is this for?
While gdb is a linux application, it can also be used with emulators to run ARM based binaries – this would allow us to disassemble and monitor mobile apps using a mobile emulator.
Now for the practical aspect of it. I put together my own version of Vivek Ramachandran (the author of the video series) example C app. This app is not designed for anything other than showing the usefulness of gdb. Please keep in mind that I do not natively write C, and it was a slight learning curve in doing things the C way (as opposed to the Python/Ruby way.)
My Highly Unrealistic App
Below is some sample code of a C application I wrote. As you can see it does many bad things.. including storing a plain text value for the variable password. Then it takes user input and does a string comparison. The string comparison spits out a 0 if it’s a match. The If condition checks for this value and then outputs some little message to the screen telling us the password was correct.
However, if the user input string is different from the expected password we return an error message.
Try not to judge me by this poorly written code. It’s merely an example of using a disassembler to mitigate the flow of an executable.
Running the Example App
Running my example code will present the user with a prompt:
Enter your password:
If you enter “abc” and hit enter it will respond with:
Wrong password, try again.
However, if you rerun the app and enter 3v1lp@ss as the user input, it will return a success message. So we know the application works. It’s simple, it’s made poorly, but it functions in this manner.
We could of course just run strings on the binary and get back enough information to crack it… but this is a gdb exercise, so let’s try using the debugger/disassembler.
We attach to the binary with: gdb [binary]
If the binary was compile with a -ggdb param, you’ll get access to a ton of symbols used. This, however, requires one to have the source code. What I like about the final chapter on this gdb video series (Video 14), is that Vivek uses a binary compiled with no debug symbols. So we’ll do the same…
Either copy the code above, or write your own simple app that prompts for input and does a string comparison on the input. If it matches, then a special phrase is passed back. If it doesn’t match we throw an error message. Compile this with no ggdb (i.e. gcc [source] -o [output filename].)
Let’s attach to our binary: gdb [filename]
Disassembly and its Duo Flavors
GDB has two flavors of disassembly output – ATT and Intel. This is covered in the really good book Hacking the Art of Exploration by Jon Erickson. I personally feel more into the Intel output format… but gdb defaults to ATT… so to change this we can do the following from the (gdb) prompt:
(gdb) set disassembly-flavor intel
If you’re like me, you might think “crap I have to remember all these commands?” Well… not so much, you can tab out the choices… so typing: set dis and then a few [TAB] will display all avail options, including disassembly-flavor. If you type that in, and TAB again, you’ll see the next options are only two: ATT and Intel.
To disassemble the application, we need to pass it a frame/method. If you type in disassemble and hit enter you’ll get an error saying you didn’t supply a frame.
At the (gdb) prompt run the following:
(gdb) info functions
You’ll get back a list of functions and look at the promising one – main.
Now, if we run:
(gdb) disassemble main
We’ll get something interesting:
What we’re seeing here is the operation of the executable. These opcodes tell us how the app works, even if we didn’t have source code the workings of the code is displayed.
There are codes to help us understand what’s going on:
jmp (jump to)
Looking at my little C application in gdb, there are some interesting things that stick out:
Here we have something being moved to the EAX register, after which we have CMP (a comparison being made.) That’s pretty interesting… it also looks like it’s looking if the comparison result is 0 (0x0.)
The next line tells us that there’s a result post comparison. See the bit below about main + 103?
What’s going on here is a result after that comparison is jumping to main + 103 … Further down the operations there’s +103:
…it’s moving to an edi register at 0x400739.
Examining Memory Registers
Now we can check this value by examining this in memory. GDB has an examine command:
Specifically x/s will return any strings… like so:
(gdb) x/s 0x400739
Look at that!
We have now discovered that after the comparison, one flow is to send a user to this output.
Going back now to the comparison, there’s another flow:
This other flow is moving to a EDI register of 0x40071b. Let’s see what’s there:
(gdb) x/s 0x40071b
Ah, there we go. Now that we’ve examined this register we get back a string stating we passed authentication.
There’s some logic to check a value.
If the comparison on the value returns 0, than we are sent to 0x40071b, which tells us we passed authentication.
If the comparison has a value other than 0, than we are sent to an error message.
Bypassing the Flow Without the Password
Now that we are armed with this information, we can attempt to run the application, pause it right before the comparison and set the value to 0 – which would (in theory) pass us through, even though our password is wrong!
So let’s do it.
Back in the operation codes we have that comparison:
We’ll set a breakpoint on 0x0000000000400653 <+77>
(gdb) break *0x0000000000400653
Note the asterisk that’s used. Now when we run the app we will pause here. So let’s run:
In the above screenshot, I ran the app and was prompted for user input. As I pretend I don’t know the password, I just type: idontknowit. Hitting enter, we reach our break point.
Now that we have paused the application at our breakpoint, we can dig a bit deeper into the registers using the command:
(gdb) info registers
This will output the current values in the available registers. Check it out:
Back to the earlier exercise, we noticed that right before the comparison the user input was getting moved into the EAX register. So let’s find the EAX register and it’s current value…
At first glance it’s not there… why? Because I’ve compiled this binary for a 64bit platform. In this platform RAX is the 64bit equivalent… it holds both the 32bit EAX register as well as it’s corresponding 64bit version.
Now that we know that RAX is our target, we will want it to be equal to 0. Remember, if it’s 0, it passes us to the flow that treats us like we entered the right password. Right now it’s value is 0x36, so let’s change it:
(gdb) set $rax=0
Once done, we can continue by typing:
There it is….
We have basically tricked the application into thinking we sent the appropriate input in order to get through this part of the application. Even though “idontknowthepass” wasn’t the right password, we faked the string comparison result it was looking for.