Skip to content

Instantly share code, notes, and snippets.

@yzgyyang
Last active January 13, 2021 06:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yzgyyang/62b1dea345e5e2cbad1ba2693e507e95 to your computer and use it in GitHub Desktop.
Save yzgyyang/62b1dea345e5e2cbad1ba2693e507e95 to your computer and use it in GitHub Desktop.

Understanding FreeBSD UMA from a Safety Prospective

Author: wzt @ kernsec https://mp.weixin.qq.com/s/20ACZFyQiUWZf5cIm_ZW-w
Translated by: ygy

Disclaimer: I translated this at 1 am in 15 minutes, hopefully it is still readable...

1.1 Introduction

FreeBSD's kernel object allocator is called UMA (Universal Memory Allocator). This article only discusses its security features, please refer to other articles for general functionality. Its security feature set is a lot smaller than that in XNU, NT and Linux, along with some security flaws from the design and architecture, as we will discuss below.

1.2 Architecture

The overall architecture of UMA is based of Solaris Slab, so let's just take a look at the slab structure at the lowest level - the size of a slab is PAGE_SIZE; the struct of slab is determined by the size of each item in the slab: for a small item, slab header is in the slab, after PAGE_SIZE all the way at the back; for a larger utem, the slab header is stored separately in the memory and is not included in the slab.

<---------------Page (UMA_SLAB_SIZE) ---------->

__________________________________________

| _  _  _  _  _  _  _  _  _  _  _  _  _  _  _   ___________ |

||i||i||i||i||i||i||i||i||i||i||i||i| |slab header||

||_||_||_||_||_||_||_||_||_||_||_||_||_||_||

|_________________________________________|



This is an OFFPAGE slab. These can be larger than UMA_SLAB_SIZE.

______________________________________

| _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _  _   |

||i||i||i||i||i||i||i||i||i||i||i||i||i||i||i|  |

||_||_||_||_||_||_||_||_||_||_||_||_||_||

|_____________________________________-|

___________    ^

|slab header|   |

|___________|---*

For a small item, the design of slab is a severe security flaw - since the slab header is after all the items as the last element, if the last item overflows, it is possible to overwrite the data structure of slab header.

struct uma_slab {
        uma_keg_t       us_keg;                 /* Keg we live in */
...
}

The struct of slab header is struct Uma_slab, with its first member being us_keg.

struct uma_keg {
 LIST_HEAD(,uma_zone)    uk_zones;       /* Keg's zones */
...
}

The struct of Uk_zones is:

struct uma_zone { uma_ctor uz_ctor; /* Constructor for each allocation */ uma_dtor uz_dtor; }

Struct members uz_ctor and uz_dtor are pointers to ctor and dtor that are called for constructing and destructing each zone. An exploit program usually replaces these two pointers to point to the address of shellcode. Since the slab header is at the end, performing a heap overflow attack is easier compared to Linux's implementation, because Linux has it's slab header at the front of the struct. When we design a memory allocator, we need to keep in mind to avoid this terrible design, and at the same time, keeping the definitions of function pointer to a minimum, to avoid being used by an exploit program.

1.3 The Missing Security Features

1.3.1 Overflow Detection

Being able to detect overflows is a basic safety requirement of every memory allocator. A common practice is to add redzone before and after the memory block, filling in fixed values, and detect overflow by checking if these fixed values changed.

UMA's redzone has the following struct:

| struct stack | size |0x42|0x42|...|    data       |0x42|0x42|...|

Before data, struct stack is for info of current stack, size is the size of data, 0x42 is the fixed value of redzone, for 16 bytes in total. After data, there is also 16-bytes of fixed values.

Since setting redzone will cause an increase of memory use and make the allcation and free process complicated (that affects the performance), overflow detection is usually enabled via a debug flag/option. Memory allocators of nearly all major OSes use fixed values to set redzone, I personally believe that this is an unsafe design, since the author of an exploit program can carefully construct the memory to have its shellcode address pointing to 0x42424242 to bypass the detection.

1.3.2 UAF Detection

The detection of UAF (Use After Free) is not implemented in FreeBSD. A general algorithm is to fill in a fixed value to the data area when freeing a slab item, and check if this fixed value is polluted when allocating, to determine if UAF occured.

1.3.3 Thread-safe Doubly Linked List

During a deletion operation of a doubly linked list, we need to check both ends to see if they are valid addresses. This is not implemented in FreeBSD.

1.3.4 Item Address Randomization

For the simplicity of initialization, item saved in the slab is linked in-order. This made it a lot easier for exploit programs. Linux kernel used a shuffle algorithm to randomize the link order of items to avoid such attacks. This is not implemented in FreeBSD.

1.3.5 Cookie

Also for detection if there is memory corruption, it is common to add a randomized cookie value, and check if the cookie is polluted when the memory is being freed. This is not implemented in FreeBSD.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment