Introduction
As we have seen in the previous parts of this article, numerous exploitation methods exist. In this part, we will analyze some of them and means to protect again them. Basic understanding of the C/C++ language and of the x86 assembly instruction set architecture is required to fully grasp the content of this article. This article is written with Windows in mind, so some of its content is not applicable to other operating systems. Furthermore, it’s intended for intermediary skills level audience, so expect some vulgarization. In this section we’ll have exploits explained, from the technical standpoint.
Exploits Explained: PE Format and Memory
Before learning about exploit protections, we need to state some elements about the « PE » Portable Executable format, the executable format introduced in Windows 95/NT. A PE file contains multiple parts, but we will focus here on only one of them, the image section header format, represented by the IMAGE_SECTION_HEADER structures. Sections headers are used to organise code and data in an efficient way. In this representation, only interesting members are show :
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
Name : Name of the section, usually automatically added by the compiler
VirtualAddress : Offset in virtual memory where to load the section (RVA)
SizeOfRawData : Size of the section
PointerToRawData : Pointer to the “code” part of the section
Characteriscs : Section attributes to set when loading in memory
Let’s take a look at some common sections:
- .text
- .bss
- .data
The .text section is where all the program instructions emitted by the compiler ends up. Its attributes are Executable and Writable.
The .bss section is where all uninitialized data are stored. Its attributes are usually Readable and Writable.
The .data section is where all the initialized data goes, except local variables that are located on the thread’s stack. Its attributes are Readable and Writable.
When a PE file is loaded into memory, each section is instantiated with its following attributes. The memory layout of a PE file has the following pattern.
We can notice the presence of two additional fields, the heap and the stack. The heap segment is a block of memory where dynamic memory allocation takes place. Global variables are also stored on the heap. Its attributes are Readable and Writable and, on legacy systems, Executable.
The stack segment handles allocation of all non-static variables and functions call data (parameters, return address, registers contexts, etc.) and as such, is responsible for the control-flow of the program. Its attributes are Readable and Writable and, on legacy systems, Executable.
Exploits Explained: Exploitation methods
The goal of an attacker is to manipulate the target program control-flow (shellcode) in order to execute some code of its own (payload). Usually the attack is used to gain access to some system or data.
Buffer overflow exploitation
Let’s consider the following C vulnerable code:
#include <stdio.h>
void foo() {
char buffer[10];
printf("Input:\n");
scanf("%s", buffer);
printf("Output: %s\n", buffer);
}
int main() {
foo();
return 0;
}
This program calls the foo() function that allocates a space of 10 bytes on the stack and asks the user for input. It then displays the input characters and exits. However, no verification of the input is made, so it’s possible for the user to write data outside the allocated space: a stack buffer overflow can occur.
The approximate disassembly of the foo() function is the following (x86 CDECL calling convention):
; _foo
push ebp; ; Save the return address to _main into the stack
mov ebp, esp ; New call frame
sub esp, D4h ; Allocate memory on the stack to store variables content
; printf() and stack cleaning
push "Input:\n"
call _printf
add esp, 4h
; user input and stack cleaning
lea eax, [ebp+buffer]
push eax
push "%s"
call _scanf
add esp, 8h
; user output and stack cleaning
lea eax, [ebp+buffer]
push eax
push "Output: %s\n"
call _printf
add esp, 8h
; Stack unwinding and exit
mov ebp, esp
pop ebp ; Recover the return address to _main from the stack
ret ; Return to _main
As we can see, it’s possible when inputting enough characters to overwrite the stack frame designed to store the variable content. The goal of an attacker is to overwrite the return address to _main stored into the stack (line 2) by an address pointing to its own code using a shellcode as input sequence.
Usually this won’t work because of Data Execution Prevention (see Protection mechanisms below) forcefully terminating the process if code is executed on the stack or heap.
ROP Chaining exploitation
A trick to bypass such protection is known as Return-oriented programming (ROP). This technique makes use of executable instructions found within the program code (.text section) and shared libraries.
The attacker will look for code sequences (gadget) containing the POP assembly instruction to load suitable values in the processor registers then calling a function with these values as parameters (return-to-libc). This process can be repeated multiple time to find an exploitable function, hence the name ROP Chaining.
Exploits Explained: Protection methods
Many protection mechanisms have been developed to protect users against exploitation of insecurely written software (see part I). Each software has its own way to implement such mechanisms. Since only closed source anti-exploit software is currently available, we will refer to the protections methods implemented in the Enhanced Mitigation Experience Toolkit (EMET):
- Canaries
- Data Execution Prevention (DEP)
- Address Space Layout Randomization (ASLR)
- Structured Exception Handler Overwrite Protection (SEHOP)
- Null Page Protection
- Heap Spray Protection
- Export Address Table Access Filtering (EAF)
- Stack Pivot Protection
Canaries
Canaries are known values placed between an allocated buffer and control data on the stack or heap. If a buffer overflow occurs, the first data to be corrupted will be the canary one. Functions that will check data integrity will fail and an error or exception will be raised.
In the example above, the magic value represented by the third bloc is likely to be overwritten if a buffer overflow occurs.
Executable space protection / Data Execution Prevention (DEP)
Data execution prevention is a system-level memory protection feature that marks one or more pages of memory as non-executable, usually heap and stack sections. If a program attempts to run code from a page memory where execution is disabled a memory access violation exception occurs. A hardware solution marketed as the NX bit is present on modern CPUs. Any operating system with support for the NX bit can mark areas of memory as non-executable. The processor will not execute any code residing in these specific areas.
Address Space Layout Randomization (ASLR)
When the linker creates an executable, it assumes it will be placed in a specific memory-mapped area, by default at address 0x400000. When the process is loaded, its instructions and resources are always present at the same address. An exploit writer can use this fact to write buffer overflow or ROP exploits more easily. Address Space Layout Randomization (ASLR) is a technique which randomizes the base addresses (ImageBase) of executable code, heap and stack in a process’s address space on each boot, which really complicate exploitation and shellcode development.
The image above, represents the base value of process smss.exe (Session Manager Subsystem) of a Windows 7 x64 system with Data Execution Prevention and Address Space Load Randomization features enabled.
Structured Exception Handler Overwrite Protection (SEHOP)
On Windows OS, hardware and software exceptions are handled through the use of the structured exception handling mechanism (SEH). This mechanism makes use of the exception registration record structure, which is composed of a pointer to the next structure and an exception handler function pointer.
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
When an exception occurs, the program control-flow will be redirected to the address of the exception handler function. Using buffer overflow, it may be possible to replace this address with anything, which may force the application to jump to a shellcode.
SEHOP complicate the use of the technique by verifying that a thread’s exception handler recorded list is not altered, using a symbolic record, before allowing any of the registered exception handlers to be called. If the symbolic record cannot be reached, the system assumes SEH overwrite may have occurred and terminate the thread.
Null Page Protection
By default, a null pointer point at virtual address 0x00000000, which content may be overwritten (kernel mode) with any address. A software vulnerability may arise when a null pointer is used, allowing a shellcoder to execute any code. The Null Page Protection pre-allocates memory at the virtual address 0x00000000 and enable memory protection on this specific memory page. Dangling or wild pointers, pointers that do not point to a valid destination, can be exploited in a similar way, allocating memory at the address they point to.
Heap Spray Protection
Heap spray attack involves allocating the thread’s heap memory at predetermined (the heap is deterministic) addresses with the right values in order to store a shellcode at a predictable address. The Heap Spray Protection works by pre-allocating certain regions in the heap, breaking the possibility to insert a shellcode using heap spray.
Export Address Table Access Filtering (EAF)
Usually a program relies on external functions provided in other executables or DLLs files. These functions are listed on the PE files Export Address Table (EAT). In order to do something useful, an exploit usually needs to import functions exported by Windows internal modules (kernel32.dll, ntdll.dll, etc.). Because of ASLR, the exploit must first find where it is loaded.
This mitigation method works by terminating any thread which tries to access the export table of these modules.
Stack Pivot Protection
Stack pivoting may be used to facilitate ROP Chaining. This method alters the address contained within the special register ESP, altering the stack frame, even replacing it with a fake stack containing more ROP gadgets.
This protection is able to detect if the thread’s stack has been pivoted, and also monitor stack registers of some Windows API functions.
Conclusion
Many exploits mitigations techniques are now available for systems administrators to use, to protect their clients and infrastructures. None of them are 100% reliable and may be bypass-able but implementing them makes software exploitation many more difficult.
Since the huge majority of exploits are based on overflow, applications developers must take action, like adopting defensive programming style. They are in the front line in this field and producing secure code is the only way it won’t be exploitable nor exploited. We hope you enjoyed this exploits explained technical article !