You are on page 1of 16

CAP6135: Malware and Software

Vulnerability Analysis

Buffer Overflow : Example of Using GDB to


Check Stack Memory
Cliff Zou
Spring 2011
A Stack Frame
Parameters
BP
Return Address
Calling Stack Pointer
SP+offset
Local Variables
SP

Addresses

00000000

SP: stack pointer BP: base/frame pointer


Calling stack pointer: previous functions SP
2
Using GDB to Check Stack
GDB tutorial:
https://developer.apple.com/library/mac/#documentation/developertools/gdb/gdb/gdb_toc.html
http://www.yolinux.com/TUTORIALS/GDB-Commands.html#GDB_COMMAND_LINE_ARGS
When compile the c code, use gcc g .. so that Gdb can match
source code line number with code
Some knowledge: http://en.wikipedia.org/wiki/X86_assembly_language
Register eip: instruction pointer, the current position of next executable instruction
Register ebp: stack pointer, the top of the current stack, used for addressing local
variable

3
Related Gdb Commands:
List: list the source code and each executions corresponding line
number
Break linenumber: set breakpoint at the linenumber
Run argv: run the execution code with the parameter argv
Next: execute the next line of code
Backtrace: show trace of all function calls in stack
Info frame: List address, language, address of arguments/local
variables and which registers were saved in frame.
This will show where the return address is saved
Return address is in Register EIP
Calling stack pointer is in Register EBP
x &variable: show the address and value of a local variable (in hex
format)
x address: print binary representation of 4 bytes of memory pointed to
by address.

4
Example of Using GDB
#include <stdio.h>
void foo(char * input){
int a1=11;
int a2=22;
char buf[7];
strcpy(buf, input);
}
void main(int argc, char **argv){
foo(argv[1]);
}

Question: What does the stack look like before strcpy()?

5
czou@eustis:~/buffer-code$ setarch i686 R gdb ./gdb-example
(gdb) list
1 #include <stdio.h>
Remove address randomization
2 void foo(char * input){
3 int a1=11; used in Unix (will talk in next lecture)
4 int a2=22;
5 char buf[7];
6 strcpy(buf, input);
7 }
8 void main(int argc, char **argv){
9 foo(argv[1]);
10 }
(gdb) break 6
Breakpoint 1 at 0x8048388: file gdb-example.c, line 6.
(gdb) run whatever
Starting program: /home/czou/buffer-code/gdb-example "whatever"

Breakpoint 1, foo (input=0xbffff92e "whatever") at gdb-example.c:


6 strcpy(buf, input);

6
(gdb) info frame
Stack level 0, frame at 0xbffff750:
eip = 0x8048388 in foo (gdb-example.c:6); saved eip 0x80483bd
called by frame at 0xbffff760
source language c.
Arglist at 0xbffff748, args: input=0xbffff92e "whatever"
Locals at 0xbffff748, Previous frame's sp is 0xbffff750
Saved registers:
ebp at 0xbffff748, eip at 0xbffff74c
(gdb) x &a1
0xbffff744: 0x0000000b
(gdb) x &a2
0xbffff740: 0x00000016
(gdb) x buf
0xbffff739: 0xa4bffff7
(gdb) x 0xbffff740
0xbffff740: 0x00000016

7
Two Techniques for
Generating Stack Overflow
Codes
NOPs
Most CPUs have a No-Operation
instruction it does nothing but advance
the instruction pointer.
Usually we can put a bunch of these
ahead of our program (in the string).
As long as the new return-address points
to a NOP we are OK.
Using NOPs
new return address

Real program
(exec /bin/ls or whatever)

nop instructions
Estimating the stack size
We can also guess at the location of the
return address relative to the overflowed
buffer.

Put in a bunch of new return addresses!


Estimating the Location
new return address
new return address
new return address
new return address
new return address
new return address
Real program

nop instructions
Explanation of Project 1
Target.c code vulnerability:
int foo(char* arg, short arglen)
{
char buf[100];
int i, maxlen = 100;
int len;

if (arglen < maxlen)


{
len = strlen(arg);
strncpy(buf, arg, len);
If input to foo(*arg, Big_Value) where Big_Value overflows short, then arglen could
be negative value and passes the if() security check.

13
Explanation of Project 1
In the exploit.c code:
#define TARGET "/home/jobert/cap6135/1/proj1-fa08/targets/target
Need to be changed to point to your own target code
Change args[1] = "hi there";
args[1] needs to point to a large buffer that can cause overflow to
target code
You can define such a large buffer here and make args[1] points
to it.
Your main task is to:
Find out the where in stack stores the return address
Find out where is the starting point of buf in foo() in target code
Fill the shellcode[] into the large buffer in your exploit code
(which will fill the buf variable in target code)
Assign the starting address of buf to the right place in the large
buffer in your exploit code in order to overwrite the return
address, then CPU will run the shellcode you put at the start of
buf variable.

14
Several Tips on Project 1
1. Be sure to use the Makefile to generate executable of both
exploit program and target program

2. Be sure to use setarch i686 -R in front of every execution,


including both Gdb and ./exploit

3. You can use break foo to set breakpoint upon entering


foo() function.

4. Fill the shell executable code (in the string array


shellcode[]) byte-by-byte into the buffer for your modified
return address to execute, do not use strcpy() because
shellcode[] is not an ASCII string.
Several Tips on Project 1
Given that:
1. We know the address of buf in target.c is: 0xbfff0000
2. We know the address of the functions return address (eip)
is 0xbfff0100
3. We put the shellcode[] at the beginning of buf.

How to Overwrite the return address to execute shellcode?

1. 0xbfff0100 0xbfff0000 = 0x100 = 256 in decimal


2. Since address in 32-bit machine is 4 bytes and Eustis is a
little-endian machine:
buf[256] = 0x00; buf[257] = 0x00;
buf[258] = 0xff; buf[259] = 0xbf;

In this way, we have changed the flow to the beginning of


shellcode!
16

You might also like