POSTED BY: Patroklos Argyroudis / 26.04.2010

FreeBSD kernel exploitation mitigations

In my recent Black Hat Europe 2010 talk I gave an overview of the kernel exploitation prevention mechanisms that exist on FreeBSD. A few people at the conference have subsequently asked me to elaborate on the subject. In this post I will collect all the information from my talk and the various discussions I had in the Black Hat conference hallways.

Userland memory corruption protections (also known as exploitation mitigations) have made most of the generic exploitation approaches obsolete. This is true both on Windows and Unix-like operating systems. In order to successfully achieve arbitrary code execution from a vulnerable application nowadays a researcher needs to look to the memory layout and the code structure of the particular application.

On the other hand, exploitation mitigation mechanisms for kernel code have not seen the same level of adoption mostly due to the performance penalty they introduce. This has increased the interest in viewing the operating system kernel as part of the attack surface targeted in a penetration test. Therefore, many operating systems have started to introduce kernel exploitation mitigations. The recent CanSecWest talk by Tavis Ormandy and Julien Tinnes titled “There’s a party at Ring0, and you’re invited” presented an overview of such mitigations on Windows and Linux.

FreeBSD also has a number of memory corruption protections for kernel code. Not all of these were developed with the goal of undermining attacks, but primarily as debugging mechanisms. Some are enabled by default in the latest stable version (8.0-RELEASE) and some are not.

Stack-smashing

Kernel stack-smashing protection for FreeBSD was introduced in version 8.0 via ProPolice/SSP. Specifically, the file src/sys/kern/stack_protector.c is compiled with gcc’s -fstack-protector option and registers an event handler called __stack_chk_init that generates a random canary value (the “guard” variable in SSP terminology) placed between the local variables and the saved frame pointer of a kernel process’s stack during a function’s prologue. Below is the relevant part of the stack_protector.c file:

10: __stack_chk_guard[8] = {};
    ...
20: #define __arraycount(__x)       (sizeof(__x) / sizeof(__x[0]))
21: static void
22: __stack_chk_init(void *dummy __unused)
23: {
24:         size_t i;
25:         long guard[__arraycount(__stack_chk_guard)];
26: 
27:         arc4rand(guard, sizeof(guard), 0);
28:         for (i = 0; i < __arraycount(guard); i++)
29:                 __stack_chk_guard[i] = guard[i];
30: }

During the protected function’s epilogue the canary is checked against its original value. If it has been altered the kernel calls panic(9) bringing down the whole system, but also stopping any execution flow redirection caused by manipulation of the function’s saved frame pointer or saved return address (again from the stack_protector.c file):

13: void
14: __stack_chk_fail(void)
15: {
16: 
17:         panic("stack overflow detected; backtrace may be corrupted");
18: }

ProPolice/SSP also performs local variable and pointer reordering in order to protect against the corruption of variables and pointers due to stack buffer overflow vulnerabilities.

NULL page mappings

Also in version 8.0, FreeBSD has introduced a protection against user mappings at address 0 (NULL). This exploitation mitigation mechanism is exposed through the sysctl(8) variable security.bsd.map_at_zero and is enabled by default (i.e. the variable has the value 0). When a user request is made for the NULL page and the feature is enabled an error occurs and the mapping fails. Obviously this protection is ineffective in vulnerabilities which the attacker can (directly or indirectly) control the kernel dereference offset. For an applicable example see the exploit for vulnerability CVE-2008-3531 I have previously published.

Heap-smashing

FreeBSD has introduced kernel heap-smashing detection in 8.0-RELEASE via an implementation called RedZone. RedZone is oriented more towards debugging the kernel memory allocator rather than detecting and stopping deliberate attacks against it. If enabled (it is disabled by default) RedZone places a static canary value of 16 bytes above and below each buffer allocated on the heap. The canary value consists of the hexadecimal value 0x42 repeated in these 16 bytes.

During a heap buffer's deallocation the canary value is checked and if it has been corrupted the details of the corruption (address of the offending buffer and stack traces of the buffer's allocation and deallocation) are logged. The code that performs the check for a heap overflow is the following (from file src/sys/vm/redzone.c):

166: ncorruptions = 0;
167: for (i = 0; i < REDZONE_CFSIZE; i++, faddr++) {
168:       if (*(u_char *)faddr != 0x42)
169:               ncorruptions++;
170: }

This protection mechanism can obviously be easily bypassed.

Use-after-free

MemGuard is a replacement kernel memory allocator introduced in FreeBSD version 6.0 and is designed to detect use-after-free bugs in kernel code. Similarly to RedZone, MemGuard mainly targets debugging scenarios and does not constitute a mechanism to mitigate deliberate attacks. However, MemGuard is not compatible and cannot replace the Universal Memory Allocator's (UMA — which is the default kernel allocator in FreeBSD) calls. Therefore (and also due to the overhead it introduced even before UMA was developed), it is not enabled by default.