Solutions

Here are some video (no audio) solutions for this lab:

Overview

In this lab we will:

A. Exploiting a stack-based buffer overflow

Take a copy of the C source code in stack.c. Build an executable as follows:

$ gcc -g -mpreferred-stack-boundary=2 -o stack stack.c

The foo function contains code rendering the program susceptible to a buffer overflow attack. Construct a string which when passed to the stack program will cause the bar function to execute. For this and the other exploits below you can use gdb or insert printf statements to find the address of the bar function, work out the gap between the overflowing buffer and return address etc. It may help you to draw a diagram of the stack you are attempting to attack. You can pass your attack string to the stack program using Perl as follows:

$ perl -e 'system "./stack", "aaaa\x23\x5f\x78\x12"'

Should you wish to debug the program and pass it arbitrary input at the same time you can use something like:

$ gdb --args ./stack `perl -e 'print "aaaa\x23\x5f\x78\x12"'`

B. Exploiting a bss-based buffer overflow

Take a copy of the source code in bss.c. Build an executable as above. Construct a string which when passed to the bss program will cause it to print out the secret string. (Currently it will not since the call to geteuid only returns zero for the superuser.) To get this working you need only overwrite the address pointed to by the function pointer with the address of printf.

C. Exploiting an off-by-one error

Take a copy of the source code in obo.c. The foo function exhibits an off-by-one error. Describe how and under what circumstances such an error might enable an attacker to cause the obo program to execute the bar function. Try it. Here is an example of how we can use perl to fill up the buffer with some address:

$ perl -e '$arg = pack(A1024, "\xbe\x84\x04\x08"x256); system "./obo", $arg'

D. Exploiting a format string error

Take a copy of the source code in fsa.c. Looking at the snprintf man page we see the following:

int snprintf(char *s, size_t n, const char *format, /* args */ ...);

So snprintf takes a variable number of parameters. For each conversion specifier in *format, snprintf expects to find a corresponding argument on the stack. It should be clear that by passing only conversion specifiers to snprint (without any corresponding arguments) we can examine stack contents. snprintf will assume its arguments to be whatever it finds on the stack. Looking at the man pages we find the following description of the %n specifier:

The argument must be a pointer to an integer into which is written the number of bytes written to the output standard I/O stream so far by this call to one of the printf() functions. No argument is converted.

Looking at the source code we see the following line of code:

(void) snprintf(buf, sizeof (buf), argv[1]);

Our programmer has not specified a format string. However, snprintf will interpret argv[1] as a format string which takes zero arguments. By specifying our own format string we can at the very least examine stack contents and using the %n specifier overwrite stack contents. We need to look take a look at the stack at the time snprintf is called:

+24 buf
+20 x
+16 argv[1]
+12 sizeof(buf)
+08 &buf
+04 Return address
+0 Caller's EBP

Construct a format string which will change the value of y to 999. Note we can modify the number of characters that would be formatted by the snprintf call by specifying field widths, e.g.

snprintf(buf, sizeof (buf), "%12d", y);

E. Preventing buffer overflows

Write a C program that takes two strings as input parameters, uses snprintf to concatenate them and writes the result to stdout. Your code must be safe from buffer overruns and print a warning should there be insufficient room in the buffer to store the concatenated strings.