Get a Pentest and security assessment of your IT network.

Cyber Security

Bypassing ASLR: Calling Program Functions

TL;DR

Address Space Layout Randomisation (ASLR) makes it harder to predict where code is loaded in memory, but doesn’t prevent calling functions. This guide shows how to find function addresses at runtime and then call them, even with ASLR enabled.

Understanding the Problem

ASLR randomises the base address of key program components (code, data, heap, stack) each time a program runs. This makes exploiting vulnerabilities like buffer overflows more difficult because attackers can’t reliably guess where to jump to execute malicious code. However, functions within the same executable remain at fixed offsets *relative* to their base address.

Solution: Finding and Calling Functions

  1. Disable ASLR (for testing/debugging): While we’re aiming to bypass it, temporarily disabling ASLR makes things easier to understand. On Linux:
    sudo sysctl -w kernel.randomize_va_space=0

    Remember to re-enable it later with sudo sysctl -w kernel.randomize_va_space=2.

  2. Identify Target Functions: Use a disassembler like Ghidra, IDA Pro or objdump to find the functions you want to call within your target program. Note their offsets from the start of the code section.
    objdump -d /path/to/your/program | less

    Look for function names and their corresponding addresses.

  3. Find the Program’s Base Address at Runtime: With ASLR enabled, you need to determine where the program is loaded in memory each time it runs. There are several ways to do this:
    • /proc/[pid]/maps (Linux): This file contains information about the process’s memory layout.
    • Debugging: Use a debugger like GDB to inspect the program’s loaded modules and their addresses.
    • Programmatically: Some programs expose this information via system calls or debugging interfaces (less common).

    Example using /proc/[pid]/maps:

    cat /proc/12345/maps

    (Replace 12345 with the actual process ID)

  4. Calculate Function Addresses: Once you have the base address, calculate the absolute addresses of your target functions by adding their offsets to the base address.

    Function Address = Base Address + Offset

  5. Calling Functions (Example in C): You’ll need a function pointer and cast it correctly. This example assumes you know the function signature.

    #include <stdio.h>
    
    int main() {
        // Assume base_address is obtained from /proc/[pid]/maps
        unsigned long base_address = 0x7f...; // Replace with actual address
        // Assume offset is the function's offset in objdump output
        unsigned long offset = 0x1234;
    
        // Calculate absolute function address
        void (*target_function)(int) = (void (*)(int))(base_address + offset);
    
        // Call the function
        if (target_function != NULL) {
            target_function(10); // Pass arguments as needed
        } else {
            printf("Function not found at calculated address.n");
        }
    
        return 0;
    }

    Important: The function signature (void (*)(int) in this example) *must* match the actual function’s signature. Incorrect signatures will lead to crashes.

  6. Dealing with Position Independent Executables (PIE): PIE adds another layer of randomisation, even within the executable itself. The base address you find from /proc/[pid]/maps is for the *entire* executable image. You still need offsets relative to that base.
  7. Consider Return-Oriented Programming (ROP): If direct function calls are blocked or difficult, ROP can be used to chain together small snippets of existing code (‘gadgets’) within the program to achieve a desired effect. This is more complex but often necessary in advanced exploitation scenarios.

Related posts
Cyber Security

Zip Codes & PII: Are They Personal Data?

Cyber Security

Zero-Day Vulnerabilities: User Defence Guide

Cyber Security

Zero Knowledge Voting with Trusted Server

Cyber Security

ZeroNet: 51% Attack Risks & Mitigation