May 01, 2007

GDB hints for dancing in the stack

If, as an incident handler, you want to understand how systems are attacked, sooner or later, you will have to confront the stack. Also there is an age for script kiddies to become real hackers and the first step in that way, right after their first words in assembly language, is to understand how the stack works.

The stack is the Rosetta Stone for understanding the hidden secrets of buffer overflows and format string attacks. And it is a prerequisite for understanding heap overflows.

Fortunately for those of you that have to take that path, some really helpful documents, like the deservedly famous "Smashing The Stack For Fun And Profit", will take you by the hand through that hostile way.

However, if you want to go through some additional examples or fight against your first buffer overflow exploit with GDB, you start feeling clumsy. Running individual processor instructions and then printing the esp, ebp, and eip registers, show the contents of the top of the stack, and displaying the next instruction to be executed while singing the GDB song is, to say the least, tedious. But GDB has been designed with those tasks in mind and it includes some functionality to make our lives easier. Namely the user defined commands (a.k.a. canned sequences of commands).

The purpose of this post was to make your life easier with a couple of user defined commands that I use within GDB, but let me please remind you first that if you want to dance in the stack in recent versions of Linux there is something you need to do first. One of the countermeasures applied to reduce the effectiveness of memory attacks (buffer overflows, format strings, and heap overflows) is to introduce some amount of randomization in the virtual addresses every time a program is run, so it becomes harder to predict the right address to jump to.

To disable this feature you just have to (as root):
echo 0 > /proc/sys/kernel/randomize_va_space
Having done that, you have your stack ready to dance, so let's go back to the GDB user defined commands.

The first thing you need to know about the GDB user defined commands is that they should be stored in your .gdbinit file that can be either in your home directory (if you want GDB to process it before the command line options) or in the current working directory (allowing you to have different .gdbinit files for different projects).

In order to avoid the tedious routine that I mentioned before, the most useful thing that a defined command can do is show you the contents of the stack. This can be achieved with the following command:
define stack
info reg ebp esp eip
echo * -- Stack -- *\n
if $argc == 0
x/16w $esp-16
end
if $argc == 1
x/$arg0w $esp-16
end
echo * -- Stack -- *\n
echo Next instruction:\
x/i $eip
end

This newly defined stack command does all these tasks:

  • Print the registers ebp (base or frame pointer), esp (stack pointer), and eip (instruction pointer).
  • Print a string to show the beginning of the stack information.
  • If it is invoked with no arguments it prints twelve words of the top stack (32 bits each) preceded by 4 words that aren't part of the stack. If an argument is provided it is used as the number of total words. Notice that in any case, the top of the stack will be located at the beginning of your 2 line of stack information.
  • Print a string to show the end of the stack information.
  • Show the next processor instruction in assembly language.
Since this is usually done after having run one processor instruction with si or ni, it would also be interesting to have a modified version of those instructions that run the instruction and print the desired information. I will call these commands sis (step instruction and stack) and nis (next instruction and stack) and they are defined with this code:
define sis
si
stack 20
end
define nis
ni
stack 20
end

I hope that with these three new commands for GDB it will be easier for you to play with the stack. Enjoy!

Labels:

4 Comments:

Anonymous Anonymous said...

sorry for the long comment, but i'm drunk. here's what my .gdbinit reads:

define show_regs
printf "\n-=[registers]=-\n"
printf "[eax: 0x%08x] [ebx: 0x%08x] [ecx: 0x%08x] [edx: 0x%08x]\n", $eax, $e
bx, $ecx, $edx
printf "[esi: 0x%08x] [edi: 0x%08x] [esp: 0x%08x] [ebp: 0x%08x]\n", $esi, $e
di, $esp, $ebp
end

define show_stack
printf "\n-=[stack]=-\n"
x/8wx $esp
end

define show_code
printf "\n-=[disassembly]=-\n"
x/10i $pc
end

define show_flags
set variable $_cf = ($eflags& 1)? "CF" : "NC"
set variable $_pf = ($eflags& 2)? "PF" : "NP"
set variable $_af = ($eflags& 4)? "AF" : "NA"
set variable $_zf = ($eflags& 8)? "ZF" : "NZ"
set variable $_sf = ($eflags& 16)? "SF" : "NS"
# set variable $_tf = ($eflags& 32)? "TF" : "NT"
set variable $_if = ($eflags& 64)? "IF" : "NI"
set variable $_df = ($eflags&128)? "DF" : "ND"
set variable $_of = ($eflags&256)? "OF" : "NO"
# set variable $_ipol = ($eflags&512)? "IOPL"
# set variable $_nt = ($eflags&1024)? "NT"
# set variable $_nothing = ($eflags&2048)
# set variable $_rf = ($eflags&4096)? "RF"
# set variable $_vm = ($eflags&8192)? "VM"
printf "[eflags: %s %s %s %s %s %s]\n", $_zf, $_sf, $_of, $_cf, $_df, $_if
end

define h
show_regs
show_flags
show_stack
show_code
end

this is obviously tailored for the intel architecture. might want to
"catch exec" also which is useful
for djb type apps.
if you want to be cool, you can also:

define n
ni
h
end

define s
si
h
end

sorry, i've been spoiled by softice since '93. :)

5:39 AM  
Blogger Jorge Ortiz said...

Cool addition, "drunk anonymous" :-)
The idea was to give some hints on how GDB can be adapted to better suit your needs for dancing in the stack and as you mentioned they are tailored for the i386. I'm quite sure that different people will want to see different things / different level of detail.
You really embraced and extended the idea :-)

10:03 AM  
Anonymous Anonymous said...

Great stuff. Thanks to RaDaJo for the article and anonymous for the detailed comment. As this is an article for beginners I think it should also be pointed out that some systems might not dump the memory when a program performs an illegal instruction or has a segmentation fault. Unless the system produces a core file (http://en.wikipedia.org/wiki/Core_dump) then there can be no analysis. To get a Unix system to dump the memory use the following command.

# ulimit -c unlimited

Make sure you are doing this in the shell you will be running the program or you can set it in your profile if you always want to dump memory when there is a problem (not recommended for productions systems). A good guide to controlling core files can be found at: http://aplawrence.com/Linux/limit_core_files.html


Of course, doing this also might make you sing the "Core Dump Blues" - http://www.pvv.ntnu.no/~steinl/vitser/core.html.

This has already helped speed up some of my analysis.

Thanks again,
Cutaway
http://blog.cutawaysecurity.com

8:36 AM  
Blogger Jorge Ortiz said...

Yes, that is something that you also should keep in mind. Core dumps can also be very helpful.

Thank you.

2:53 PM  

Post a Comment

<< Home