SGX-Shield: Enabling Address Space Layout Randomization for SGX programs

Introduction

The traditional memory corruption vulnerabilities, such as buffer overflow, exist inside SGX programs, which not only nullifies the security guarantee that SGX claims to provide, but also, perhaps more critically, allows attacker to exploit isolation and confidentiality to lurk. For example, by exploiting a stack overflow vulnerability in a trusted web server or database running in a enclave, an adversarial client can launch traditional return-orient programming (ROP) attacks to disclose security-sensitive data in an enclave.

To defeat such attack, Intel includes a simple ASLR scheme for SGX in Intel SDKs for Linux and Windows. However, we find that Intel’s ASLR’s design has several critical limitations that invalidate the security guarantees of ASLR.

Challenges:

  1. The strong, unique attack model of SGX exposes the enclave memory layout to untrusted system software, leaving SGX programs completely unprotected by ASLR.

  2. SGX provides the limited memory to an enclave typically 64 MB or 128 MB in total can be protected. Thus, the degree of randomness and the security of ASLR has been significantly limited.

  3. ASLR requires a dynamic relocation scheme that updates relative addresses in the code and data section, which comflict with the attestation process of SGX; specifically, SGX finalizes the integrity measurement before an enclave execution starts, but the relocation for ASLR must be performed afterwards. The inherent design disagreement results in writable code pages, invalidating executable space protection.

  4. The SGX specification forces the use of a fixed address for security-critical data in an enclave.

To address these issues, this paper proposes SGX-Shield. It introduces the concept of a multistage loader, which pushes back all ASLR-related operations to its secure in-enclave loader, hiding all security-sensitive operations from adversaries. SGX-Shield employs fine-grained randomization by splitting the code into a set of randomization units. SGX-Shield also enforces a software data execution protection to guarantee W⨁X in enclave’s code pages.

Design

Threat model

SGX-Shield assumes the same attack model as SGX, as our ASLR scheme is designed for SGX program. Our attack model consideration focuses on an attacker who wishes to exploit a vulnerability in the target program running in the enclave.

Multistage loader

SGX-Shield consists of three phases: preparation, bootstrapping, and secure in-enclave loading.

First, the preparation phase builds the target SGX program that a user wants to deploy. This built executable contains a secure in-enclave loader in its code section and the target SGX program in its data section.

Second, in the bootstrapping phase, SGX-Shield performs the first part of multistage loading. The primary role of the bootstrapping phase is to create an enclave and initialize the secure in-enclave loader with the help of the untrusted kernel. Because the memory layout of an enclave is assumed to be visible to the non-trusted party in this phase, it is designed to make as minimal decision on resource provisioning as possible and defer all security-sensitive decisions to the secure in-enclave loader. This phase allocates code pages with read, write and execute permissions, and data pages with read and write permissions. The read/write permissions granted the code pages can be written with target SGX program (performing relocation as well), and then execute it. While the code pages can be writable and executable, SGX-Shield removes read and write permissions from these pages using a software-level enforcement.

Finally, the secure in-enclave loader loads the target SGX program into the memory space from its data pages. The secure in-enclave loader randomly picks the base address using the RDRAND instruction, which relies on the non-deterministic on-processor entropy. Then, it loads each section of the target program, where the address of each section is further adjusted independently at random. Before finishing the loading, SGX-Shield resolves all relocation information, which includes global variables, static variables, and the destination of all branches. At last step, SGX-Shield wipes out the secure in-enclave loader from the memory space, and the jumps to the entry point of the target SGX program.

Fine-grained randomization for enclaves

Preparation. SGX-Shield relocates code at smaller granularity, called a randomization unit. Our implementation supports 32- and 64- byte units. During the compilation, SGX-Shield ensures that the terminating instructions of randomization unit are not fall-through cases. This is because fall-through assumes that randomization units are placed consecutively, which is not true when they are relocated for ASLR. Thus, for each fall-through case, SGX-Shield appends an unconditional branch instruction that points to the entry point of the next randomization unit.

Note that this instruction pass cannot be done naively at the intermediate language (IR) level. Even when IR does not have conditional branch instructions with fall-through features, the compiler backend may automatically introduce this.

Finally, the size of the randomization unit introduces a trade off between security and performance.

Stage 1: Bootstrapping. We let the loading scheme in the bootstrapping phase over-estimate the memory space (both code and data pages are 32 MB) required to load the target program, as the size is directly related to the ASLR entropy.

Secure in-enclave loading. Using the target SGX program in data pages, the secure in-enclave loader starts to place each randomization unit into previously allocated memory space. SGX-Shield randomizes all data objects as well, which includes stack, heap, and global variables.

Since SGX-Shield randomizes all code and data objects, all reference to memory objects including the absolute address and the PC-relative address must be determined after placing them. The secure in-enclave loader conducts the relocation for all memory objects after loading them.

Software DEP in enclaves

SGX-Shield enforces the NRW boundary, which is a virtual barrier between code and data pages. SGX-Shield guarantees this by (1) shepherding all memory access instructions and (2) ensuring only aligned control transfer are made.

Shepherding memory access. In general, there are two types of memory access instruction: (1) explicit memory accessing instructions (e.g., mov, inc, add with memory operands in x86) and (2) stack accessing instructions (e.g., push, pop, ret or sub with an explicit stack register operand).

In order to prevent read or write attempts through the first type of instruction, SGX-Shield makes sure that a memory address to be accessd is always higher tha the NRW boundary. SGX-Shield reserves the register r15 to hold the boundary, and transform the original instruction such that it accesses memory using a positive offset from the NRW boundary. We then enfoce that the maximum positive offset is smaller than 2^32 to ensure that the instruction never accesses memory beyond the NRW boundary.

1
2
3
4
5
6
7
8
9
10
11
; Before enforcing non-writable code
mov [rdx + 0x10], rax

; After enforcing non-writable code
; (r15 is initialized to hold the NRW boundary)
; (enfoce rdx >= r15)

lea r13, [rdx + 0x10] ; r13 = rdx + 0x10
sub r13, r15 ; r13 = r15 - r13
mov r13d, r13d ; r13 = r13 & 0xffff'ffff
mov [r15 + r13], rax ; *(r15 + r13) = rax

To enfoce no-writable code pages on stack accessing instruction, SGX-Shield makes sure that a stack pointer (i.e., rsp) never points to code pages. To handle instructions that adjust stack pointer implictly, we simply map a guard page (i.e, no permission is granted) at the top and buttom of the stack area.

1
2
3
4
5
6
7
8
9
10
; Before enforcing non-writable code
sub rsp, 0x40

; After enforcing non-writable code
; (r15 is initialized to hold the NRW boundary)
; (enfoce rdx >= r15)
sub rsp, r15 ; rsp = rsp - r15
sub rsp, 0x40 ; rsp = rsp - 0x40
mov esp, esp ; rsp = rsp & 0xffff'ffff
lea rsp, [rsp + r15] ; rsp = rsp + r15

Ensuring aligned control transfer. SGX-Shield restricts the control transfers only to the entry point of the randomization unit. It enforces that there is only one way to decode instructions, ensuring that only shepherded memory access takes place. This enforcement is performed for all control transfer instructions, including indirect branches as well as return instruction.

1
2
3
4
5
6
7
; Before enforcing aligned indirected branch
jmp rax

; After enforcing aligned indirected branch
; (enforce rax % [random unit size] = 0)
and rax, $-0x20 ; rax = rax && 0xffff'ffff'ffff'ffe0
jmp rax ; jump to the address pointed by rax

Isolating access to security-critical data

SGX places the page for the State Save Area (SSA) at a known location and does not permit its relocation. We using software DEP mechanism implements SFI to isolate SSA.

Implemenataion

Preparation. The preparation phase includes an LLVM compiler 4.0, a tatic linker, and a sign tool of Intel SGX SDK for Linux. By modifying the backend of LLVM, we insert two kinds of instructions: (1) unconditional jump instructions (instead of fall-through) at the end of randomization unit and (2) instructions to enforce the software DEP. In addition, the LLVM emits each randomization unit as a symbol. The fine-grained symbol information is used in the secure in-enclave loading.

The current version of SGX-Shield supports only static linking. We modify Intel SDK for Linux to provide the enclave program with sufficient code and data pages for shuffling. We emebed the binary of enclave program into the binary of secure in-enclave loader as a section using the objcopy command.

Bootstrapping. The bootstrapping simply creates an enclave and loads the secure in-enclave loader to the enclave.

Secure in-enclave loader. The secure in-enclave loader is a dynamic loader that conducts the randomization unit-level memory object loading and relocations. It resolves the relocation information for all the memory reference including the absolute address and the PC-relative address. We implements this (parsing the ELF file, randomly loading and relocation ) with a single C file.