Vulnerability Primitives
Vulnerability primitives are the fundamental components or techniques that can be exploited to create vulnerabilities, serving as the building blocks for understanding and analysing security flaws.
Vulnerability primitives like buffer overflows can serve as the initial step in an exploit chain. Discovering a buffer overflow primitive may initially result in a denial of service (crashing the program or causing it to behave unexpectedly).
However, with further development and analysis, we may be able to leverage that primitive to achieve more severe consequences, such as remote code execution.
By understanding and manipulating the vulnerability primitive, we might be able to craft and deliver payloads that exploit the vulnerability in a way that goes beyond simple denial of service and enables execution of arbitrary code on the target system.
This section provides a brief overview of the most common vulnerability primitives. We will use examples from the HackSys Extreme Vulnerable Driver project; this has been purposefully written to contain vulnerabilties.
Stack-based Buffer Overflow
Work in progress
One of the most widely recognised vulnerability primitives is the stack-based buffer overflow.
A stack-based buffer overflow is a type of software vulnerability that occurs when a program writes more data into a buffer (a temporary storage area in the computer's memory) than it can hold. The buffer is typically located on the stack, a region of memory used for local variables and function calls.
When a program exceeds the buffer's capacity, the extra data overflows into adjacent memory locations, potentially overwriting critical information such as return addresses or function pointers. This can lead to various security issues, including the ability for an attacker to execute arbitrary code, crash the program, or gain unauthorised access to the system.
Work in progress end
Heap-based Buffer Overflow
Work in progress
Type Confusion
A type confusion vulnerability occurs when a program assumes a certain data type for an object, but due to a flaw, the object's type is manipulated or misinterpreted, leading to unpredictable behavior and potential security exploits. This can allow us to manipulate memory or execute arbitrary code by confusing the program's interpretation of data types:
We will look at an example from the HackSys Extreme Vulnerable Driver project.
In C, the union
keyword is used to define a special data type that enables different variables to share the same memory space. It allows the same memory location to be interpreted in multiple ways, based on the type of data being accessed at a given time.
The purpose of using a union
is to save memory by allowing overlapping storage of different types of variables. All members of a union share the same memory space, and the size of the union is determined by the largest member within it.
Usage can lead to type-related issues, particularly type confusion vulnerabilities:
Line 8
shows the union
keyword being used.
The second variable in _KERNEL_TYPE_CONFUSION_OBJECT
can be either a ULONG_PTR
data type or a FunctionPointer
data type.
The vulnerability arises when user provided data is assigned to KernelTypeConfusionObject
.
We can provide an arbitrary address in the UserTypeConfusionObject->ObjectType
data and this will be copied to KernelTypeConfusionObject->ObjectType
which we have seen can be a FunctionPointer
data type:
Line 10
shows that an initialisation function is called:
Because Callback()
is invoked on line 3
in the TypeConfusionObjectInitializer
function our shellcode is executed.
Arbitrary Write
Arbitrary Write refers to a technique used by exploit developers to write arbitrary data to a specific memory location within a vulnerable program or system. By gaining the ability to write arbitrary values to memory, an attacker can manipulate critical data structures, overwrite function pointers, modify variables, or even inject malicious code into the target system.
Arbitrary Write vulnerabilities are valuable to us because they provide a powerful mechanism for achieving further exploitation or gaining control over a compromised system. By carefully crafting malicious inputs or leveraging existing vulnerabilities, an attacker can exploit an Arbitrary Write primitive to bypass security mechanisms, escalate privileges, or execute arbitrary code within the target environment.
If we will look at the example from the HackSys Extreme Vulnerable Driver project, interaction with the driver presents a "Write What Where" opportunity. The driver takes the following struct as input:
Because the driver is running in kernel mode we can effectively write anywhere we want. A common technique is to write over a HalDispatchTable
entry (step 1
) to point to our shellcode and then trigger the call using a user mode API (step 3
):
The Hardware Abstraction Layer Dispatch Table, is a data structure. It is a table of function pointers that provides an interface between the hardware and the kernel. The functions within the HalDispatchTable
handle low-level hardware operations and facilitate communication between the operating system and the hardware devices.
By modifying the pointer located at HalDispatchTable+0x08
, we gain control over the kernel call chain from user space and can execute our code within the kernel space.
Use-After-Free
A use-after-free vulnerability is a type of software vulnerability that occurs when a program continues to use a memory address after it has been freed or deallocated. This can lead to unpredictable behavior and security issues.
The vulnerability typically arises when a program frees a memory block but still holds a reference or pointer to that memory. If the program later attempts to access or use the freed memory, it can result in various problems, such as accessing invalid or corrupted data, causing a crash, or potentially allowing an attacker to execute arbitrary code.
Use-after-free vulnerabilities often occur in programs that manage dynamic memory allocation, such as when objects or data structures are created, modified, and freed during program execution.
In it's purest form a use-after-free vulnerability is shown below:
Work in progress
Interger Overflow/Underflow
An integer overflow vulnerability is a type of software vulnerability that occurs when an arithmetic operation on integers exceeds the maximum value that can be represented by the data type. This can lead to unexpected and potentially dangerous behavior in a program.
In programming languages, integers have a limited range of values they can represent based on their data type (e.g., 32-bit signed integer, 64-bit unsigned integer). When an arithmetic operation, such as addition or multiplication, results in a value larger than the maximum representable value, an overflow occurs.
In a 32-bit system, if an integer variable holds the value 0xFFFFFFFF
(the maximum value that can be represented by a 32-bit integer), and you add 1
to it, the result would be 0x00000000
.
This behavior occurs because the addition operation causes an integer overflow. The result of the addition exceeds the maximum representable value for a 32-bit integer (0xFFFFFFFF
), and the overflow "wraps around" to the minimum representable value (0x00000000
) due to the finite range of the data type.
An integer underflow is the opposite of an integer overflow. It occurs when an arithmetic operation on integers results in a value that is smaller than the minimum representable value for the given data type.
The example given in the HackSys Extreme Vulnerable Driver project is shown below:
Line 1
is the conditional statement that checks to ensure the size of the user buffer boes not exceed the size of the kernel buffer. However this code is flawed and is vulnerable to an integer overflow.
The integer overflow is essentially exploited to circumvent the intended logic, allowing for the injection of a sufficiently large buffer that triggers a buffer overflow within the kernel (starting on line 9
).
Format String Specifiers
Work in progress
Off-by-One
Work in progress
Last updated