Process and Memory:
When a program is executed, the OS assigns it a process.
Process = Instance of a program executed by CPU, consists of:
Memory allocation
Processor state
Resources associated with execution
Memory Segmentation:
Each process has memory divided into segments:
Text Segment (Code Segment):
Contains executable instructions.
Typically read-only and shareable across processes.
Data Segment:
Stores global/static variables initialized by the program.
Stack:
Stores temporary variables and function call information (return addresses, local variables).
Operates in a Last-In-First-Out (LIFO) manner.
Heap:
For dynamic memory allocation during runtime.
Memory must be explicitly requested and deallocated.
#include <stdio.h>
#include <stdlib.h>
int global_var = 10; // Stored in Data Segment
int main() {
int local_var = 5; // Stored in Stack
int *ptr = malloc(sizeof(int)); // Allocated in Heap
*ptr = 20;
printf("Sum = %d\n", local_var + global_var + *ptr);
free(ptr); // Free Heap memory
return 0;
}
Definition:
Attacker exploits program/system vulnerabilities to inject malicious code into the memory space of a running process.
Common types: Array indexing, pointer subterfuge, stack smashing, heap smashing, etc.
Array Access Vulnerabilities:
Provides arbitrary access to memory.
Risk of Buffer Overflow:
An index larger than the buffer size or less than zero can lead to buffer overflow.
int array[4];
bool authenticated = false;
int index;
cin >> index;
array[index] = -1; // If index == 5, it's a problem!
Implement bounds checking to prevent vulnerabilities.
#include <iostream>
#include <stdexcept>
using namespace std;
int main() {
int array[4];
bool authenticated = false;
int index;
cin >> index;
// Implementing bounds checking
if (index < 0 || index >= 4) {
throw out_of_range("Index is out of bounds!");
}
array[index] = -1;
}
Definition:
Overwriting a pointer to refer to different data than intended.
Risks:
Attacker may input values to alter pointers and display sensitive data.
long buffer[1];
char *p1 = "Safe";
char *p2 = "Rosebud";
cin >> buffer[1];
cout << p1;
#include <iostream>
using namespace std;
int main() {
long buffer[1];
char *p1 = "Safe";
char *p2 = "Rosebud";
cin >> buffer[0]; // Restricting input to only buffer[0]
// Check for valid inputs before use\ // This prevents exploitation
if (buffer[0] < 0 || buffer[0] >= 1) {
cout << "Invalid input!";
return 1;
}
cout << p1;
}
Definition:
Overwriting a function pointer to execute unintended functions.
long buffer[1];
void (*pointerFunction)() = safe;
cin >> buffer[1];
pointerFunction();
#include <iostream>
using namespace std;
void safe() {
cout << "Safe Function Executed";
}
int main() {
long buffer[1];
void (*pointerFunction)() = safe;
cin >> buffer[0]; // Restricting function pointer modifications
// Check if buffer is within safe limits before allowing any assignment
if (buffer[0] == 0) {
pointerFunction(); // Call safe function
}
}
Stack Overview:
Abstract data type for ordered storage and retrieval (LIFO).
Operations: push() adds, pop() removes the top element.
void bar() {}
void foo() { bar(); }
int main() { foo(); }
Definition:
Exploiting stack buffer overruns to overwrite return pointers, redirecting control flow.
Example Attacker Input:
Entering carefully crafted input to manipulate the stack.
void prompt()
char text[8];
cin >> text; //No size check (overfLows possible)
}
void prompt (){
char text[8];
cin.getline(text, sizeof(text)); //Safe input with size check
}
Aspect | Stack | Heap |
---|---|---|
Allocation Mechanism | Static at compile-time | Dynamic at runtime |
Memory Management | LIFO | Not constrained by access order |
Memory Usage | Limited size | Larger size |
Access Speed | Faster | Slower compared to stack |
Deallocation | Automatic upon function return | Manual deallocation required |
Data Structure | Frame-based, smaller structures | Suitable for larger, dynamic variables |
Definition:
Exploiting vulnerabilities in dynamic memory allocation to overwrite critical heap metadata, leading to arbitrary code execution.
Logic of the Attack:
An attacker can manipulate data in the heap, causing overflow and modifying the control structures that manage memory. This could allow them to redirect execution flow to malicious code.
Example Vulnerable Code:
#include <stdlib.h>
void vulnerableFunction() {
int *ptr = malloc(10 * sizeof(int));
ptr[10] = 42; // Overflowing heap memory, writing beyond allocated space.
}
Mitigation:
Implement proper sizing and bounds checking during memory allocation and deallocation.
Heap Smashing Mitigation Code:
#include <stdlib.h>
#include <iostream>
void safeFunction() {
int *ptr = (int*)malloc(10 * sizeof(int));
if (ptr == NULL) {
std::cerr << "Memory allocation failed" << std::endl;
return;
}
// Proper bounds checking
if (10 < 10) {
ptr[10] = 42; // No overflow
}
// Free allocated memory
free(ptr);
}
https://eecs280staff.github.io/notes/02ProceduralAbstractionT
Helfrich, J. N. (2018). Security for Software Engineers. CRC Press