ZD

SWE210 Software Security Week 3

Memory Management in Operating Systems

  • 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.

Example of Memory Segments

#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;
}

Memory Injection Attacks

  • 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 Indexing Attacks

  • 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.

Example Vulnerable Code:
int array[4];
bool authenticated = false;
int index;
cin >> index;
array[index] = -1; // If index == 5, it's a problem!
Mitigations:

Implement bounds checking to prevent vulnerabilities.

Array Indexing Attacks Mitigation Code:
#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;
}

Pointer Subterfuge

  • Definition:

    • Overwriting a pointer to refer to different data than intended.

  • Risks:

    • Attacker may input values to alter pointers and display sensitive data.

Example Code:
long buffer[1];
char *p1 = "Safe";
char *p2 = "Rosebud";
cin >> buffer[1];
cout << p1;
Pointer Subterfuge Mitigation Code:
#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;
}

ARC Injection

  • Definition:

    • Overwriting a function pointer to execute unintended functions.

Example Code:
long buffer[1];
void (*pointerFunction)() = safe;
cin >> buffer[1];
pointerFunction();
ARC Injection Mitigation Code:
#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 Management

  • Stack Overview:

    • Abstract data type for ordered storage and retrieval (LIFO).

    • Operations: push() adds, pop() removes the top element.

Example Function Call:
void bar() {}
void foo() { bar(); }
int main() { foo(); }

Stack Smashing

  • Definition:

    • Exploiting stack buffer overruns to overwrite return pointers, redirecting control flow.

  • Example Attacker Input:

Entering carefully crafted input to manipulate the stack.

Example Vulnerable Code

void prompt()
    char text[8];
    cin >> text; //No size check (overfLows possible)
}

Mitigated Code

void prompt (){
    char text[8];
    cin.getline(text, sizeof(text)); //Safe input with size check
}

Comparison: Stack vs Heap

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

Heap Smashing

  • 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);
}

References

  • https://eecs280staff.github.io/notes/02ProceduralAbstractionT

  • Helfrich, J. N. (2018). Security for Software Engineers. CRC Press