CPU-Hardware nicht konfiguriert zur Unterstützung der Exklusivität von Schreib- und Ausführungsoperationen

Beschreibung

CPU-Hardware nicht konfiguriert zur Unterstützung der Exklusivität von Schreib- und Ausführungsoperationen tritt auf, wenn der CPU Hardware-Unterstützung fehlt oder Funktionen deaktiviert sind, die gleichzeitigen Schreib- und Ausführungszugriff auf Speicherbereiche verhindern. Moderne CPUs enthalten typischerweise ein spezielles Bit (oft NX/XD-Bit genannt), das Speicher in Code- (ausführbar) und Daten- (nicht ausführbar) Regionen trennt. Einige Prozessoren implementieren dies über Memory Protection Units (MPU) oder Memory Management Units (MMU). CPUs ohne diese Funktionen -- oder mit deaktiviertem Schutz -- können jedoch keine Schreib-/Ausführungsexklusivität erzwingen, wodurch Angreifer bösartigen Code einschleusen und ausführen können.

Risiko

Fehlende Schreib-/Ausführungsexklusivität hat schwerwiegende Sicherheitsauswirkungen. Angreifer können bösartigen Code in beschreibbaren Speicher einschleusen. Eingeschleuster Code kann ohne Einschränkungen ausgeführt werden. Buffer-Overflow-Exploits werden trivial. Code-Injection-Angriffe gelingen ohne zusätzliche Umgehungen. Return-Oriented Programming ist möglicherweise nicht erforderlich. Shellcode-Injection ist unkompliziert. Speicherbeschädigung führt direkt zur Codeausführung. Standard-Sicherheitsmaßnahmen sind unwirksam.

Lösung

Implementieren Sie ein dediziertes Bit zur Markierung von Daten als nicht-ausführbar oder integrieren Sie MMU/MPU-Unterstützung falls nicht verfügbar. Setzen Sie SoC-Interconnect-Firewalls ein, um Schreibexklusivität zu emulieren, wenn Hardware-Unterstützung fehlt. Aktivieren Sie DEP/NX-Bit in der Betriebssystemkonfiguration. Verwenden Sie Memory Protection Units auf Mikrocontrollern. Implementieren Sie Software-basierte W^X-Durchsetzung, wenn Hardware-Unterstützung begrenzt ist. Erwägen Sie Hardware-Upgrades für sicherheitskritische Anwendungen.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitUmfang: Vertraulichkeit

Unbefugte Codeausführung - Angreifer können beliebigen Code einschleusen und ausführen, was potenziell Zugriff auf sensible Daten ermöglicht.
IntegritätUmfang: Integrität

Unbefugte Codeausführung - Bösartige Codeausführung ermöglicht die Änderung von Systemdaten und -verhalten.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: System ohne W^X-Durchsetzung

#include <stdint.h>
#include <string.h>

// Beispiel: Mikrocontroller ohne MPU
// Gesamter Speicher ist sowohl beschreibbar als auch ausführbar

char data_buffer[256];  // Dieser Puffer ist ausführbar!

void vulnerable_input_handler(const char* input, size_t len) {
    // VERWUNDBAR: Kopiert Benutzereingabe in ausführbaren Speicher
    if (len < sizeof(data_buffer)) {
        memcpy(data_buffer, input, len);
    }
    // Angreifer kann Shellcode in data_buffer einschleusen
    // und dorthin zur Ausführung springen
}

// VERWUNDBAR: Funktionszeiger in beschreibbarem Speicher
typedef void (*callback_t)(void);
callback_t user_callback = NULL;

void vulnerable_set_callback(callback_t cb) {
    // VERWUNDBAR: Angreifer kann dies mit Shellcode-Adresse überschreiben
    user_callback = cb;
}

void vulnerable_execute_callback(void) {
    if (user_callback) {
        // VERWUNDBAR: Führt jede gespeicherte Adresse aus
        user_callback();
    }
}

// Angriffsszenario
void exploit_no_wxe(void) {
    // Auszuführender Shellcode
    unsigned char shellcode[] = {
        0x31, 0xc0,             // xor eax, eax
        0x50,                   // push eax
        0x68, 0x2f, 0x2f, 0x73, 0x68, // push "//sh"
        0x68, 0x2f, 0x62, 0x69, 0x6e, // push "/bin"
        0x89, 0xe3,             // mov ebx, esp
        0x50,                   // push eax
        0x53,                   // push ebx
        0x89, 0xe1,             // mov ecx, esp
        0xb0, 0x0b,             // mov al, 0x0b
        0xcd, 0x80              // int 0x80
    };

    // Shellcode in beschreibbaren Puffer kopieren
    memcpy(data_buffer, shellcode, sizeof(shellcode));

    // Zum Shellcode springen - funktioniert weil Puffer ausführbar ist
    ((void(*)())data_buffer)();
}
// VERWUNDBAR: Speichercontroller ohne Ausführungsschutz

module vulnerable_memory_controller (
    input wire clk,
    input wire reset_n,
    input wire [31:0] addr,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    input wire fetch_enable,  // Instruktionsabruf
    output reg [31:0] read_data,
    output reg operation_complete
);

    // Speicher-Array
    reg [31:0] memory [0:16383];

    // VERWUNDBAR: Keine Trennung zwischen Code und Daten
    // Alle Speicherbereiche können sowohl beschrieben als auch ausgeführt werden

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            operation_complete <= 1'b0;
        end
        else begin
            if (write_enable) begin
                // VERWUNDBAR: Schreiben an jede Adresse erlaubt
                // Einschließlich Code-Regionen
                memory[addr[15:2]] <= write_data;
                operation_complete <= 1'b1;
            end
            else if (read_enable || fetch_enable) begin
                // VERWUNDBAR: Ausführung (Abruf) von jeder Adresse erlaubt
                // Einschließlich Datenregionen
                read_data <= memory[addr[15:2]];
                operation_complete <= 1'b1;
            end
            else begin
                operation_complete <= 1'b0;
            end
        end
    end

    // Keine Zugriffskontrolle basierend auf:
    // - Speicherregion-Attributen
    // - Ausführungsberechtigungs-Bits
    // - Schreibberechtigung für Code-Regionen

endmodule

// VERWUNDBAR: MPU nicht konfiguriert
module vulnerable_mpu_unused (
    input wire clk,
    input wire reset_n,
    // MPU-Konfigurationsports vorhanden aber ungenutzt
    input wire mpu_enable,
    input wire [31:0] mpu_region_base,
    input wire [31:0] mpu_region_size,
    input wire [7:0] mpu_region_attr,
    // Speicherzugriff
    input wire [31:0] access_addr,
    input wire access_write,
    input wire access_execute,
    output reg access_allowed
);

    // VERWUNDBAR: MPU existiert aber ist nicht aktiviert
    // Alle Zugriffe werden unabhängig von Attributen erlaubt

    always @(*) begin
        // Sollte MPU-Regionen und Berechtigungen prüfen
        // Aber MPU ist deaktiviert, daher immer erlauben
        access_allowed = 1'b1;
    end

endmodule

Sichere Lösung

// SICHER: System mit W^X-Durchsetzung

#include <stdint.h>
#include <string.h>
#include <sys/mman.h>

// SICHER: Datenpuffer als nicht-ausführbar markiert
__attribute__((section(".data.noexec")))
char data_buffer[256];

void secure_init_memory_protection(void) {
    // SICHER: Datensektionen als nicht-ausführbar markieren
    extern char __data_start, __data_end;
    size_t data_size = &__data_end - &__data_start;

    // Datensegment als lesen-schreiben aber NICHT ausführbar setzen
    if (mprotect(&__data_start, data_size, PROT_READ | PROT_WRITE) < 0) {
        // Fehler behandeln - ausfallsicher
        abort();
    }

    // SICHER: Code-Sektionen als nicht-beschreibbar markieren
    extern char __text_start, __text_end;
    size_t text_size = &__text_end - &__text_start;

    if (mprotect(&__text_start, text_size, PROT_READ | PROT_EXEC) < 0) {
        abort();
    }
}

void secure_input_handler(const char* input, size_t len) {
    // SICHER: Datenpuffer ist nicht ausführbar
    // Selbst wenn Angreifer Code einschleust, kann er nicht ausgeführt werden
    if (len < sizeof(data_buffer)) {
        memcpy(data_buffer, input, len);
    }
    // Versuch data_buffer auszuführen wird einen Fehler verursachen
}

// SICHER: Funktionszeiger mit Validierung
typedef void (*callback_t)(void);
static callback_t registered_callbacks[16] = {0};
static size_t num_callbacks = 0;

int secure_register_callback(callback_t cb) {
    // SICHER: Validieren, dass Callback im Code-Bereich liegt
    extern char __text_start, __text_end;

    if ((char*)cb < &__text_start || (char*)cb >= &__text_end) {
        // Callback nicht im Code-Bereich - ablehnen
        return -1;
    }

    if (num_callbacks < 16) {
        registered_callbacks[num_callbacks++] = cb;
        return 0;
    }
    return -1;
}

void secure_execute_callback(size_t index) {
    if (index < num_callbacks && registered_callbacks[index]) {
        // SICHER: Nur validierte Callbacks ausführen
        registered_callbacks[index]();
    }
}

// SICHER: MPU-Konfiguration für Cortex-M
void secure_configure_mpu(void) {
    // MPU während Konfiguration deaktivieren
    MPU->CTRL = 0;

    // Region 0: Flash (Code) - Nur-Lesen, Ausführbar
    MPU->RNR = 0;
    MPU->RBAR = FLASH_BASE;
    MPU->RASR = MPU_RASR_ENABLE_Msk |
                (0x11 << MPU_RASR_SIZE_Pos) |    // 256KB
                (0x6 << MPU_RASR_AP_Pos) |       // Nur-Lesen
                (0 << MPU_RASR_XN_Pos);          // Ausführbar

    // Region 1: RAM (Daten) - Lesen-Schreiben, Nicht-Ausführbar
    MPU->RNR = 1;
    MPU->RBAR = SRAM_BASE;
    MPU->RASR = MPU_RASR_ENABLE_Msk |
                (0x10 << MPU_RASR_SIZE_Pos) |    // 128KB
                (0x3 << MPU_RASR_AP_Pos) |       // Lesen-Schreiben
                (1 << MPU_RASR_XN_Pos);          // SICHER: Nicht-Ausführbar

    // Region 2: Peripherie - Lesen-Schreiben, Nicht-Ausführbar
    MPU->RNR = 2;
    MPU->RBAR = PERIPH_BASE;
    MPU->RASR = MPU_RASR_ENABLE_Msk |
                (0x1C << MPU_RASR_SIZE_Pos) |    // 512MB
                (0x3 << MPU_RASR_AP_Pos) |
                (1 << MPU_RASR_XN_Pos);          // Nicht-Ausführbar

    // MPU mit Standard-Speicherkarte für privilegierten Zugriff aktivieren
    MPU->CTRL = MPU_CTRL_ENABLE_Msk | MPU_CTRL_PRIVDEFENA_Msk;

    // Speicherbarriere
    __DSB();
    __ISB();
}
// SICHER: Speichercontroller mit W^X-Durchsetzung

module secure_memory_controller (
    input wire clk,
    input wire reset_n,
    input wire [31:0] addr,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    input wire fetch_enable,
    output reg [31:0] read_data,
    output reg operation_complete,
    output reg access_violation
);

    // Speicher-Array
    reg [31:0] memory [0:16383];

    // SICHER: Speicherregion-Attribute
    // Bit 0: Beschreibbar
    // Bit 1: Ausführbar
    // W^X: Diese Bits sollten niemals beide gesetzt sein
    reg [1:0] region_attr [0:15];  // 16 Regionen je 4KB

    // Regionsberechnung
    wire [3:0] region_index = addr[15:12];
    wire region_writable = region_attr[region_index][0];
    wire region_executable = region_attr[region_index][1];

    // SICHER: Regionen mit W^X-Durchsetzung initialisieren
    initial begin
        // Code-Region (0-3): Ausführbar, NICHT beschreibbar
        region_attr[0] = 2'b10;
        region_attr[1] = 2'b10;
        region_attr[2] = 2'b10;
        region_attr[3] = 2'b10;

        // Daten-Region (4-11): Beschreibbar, NICHT ausführbar
        region_attr[4] = 2'b01;
        region_attr[5] = 2'b01;
        region_attr[6] = 2'b01;
        region_attr[7] = 2'b01;
        region_attr[8] = 2'b01;
        region_attr[9] = 2'b01;
        region_attr[10] = 2'b01;
        region_attr[11] = 2'b01;

        // Stack-Region (12-13): Beschreibbar, NICHT ausführbar
        region_attr[12] = 2'b01;
        region_attr[13] = 2'b01;

        // Reserviert (14-15): Weder beschreibbar noch ausführbar
        region_attr[14] = 2'b00;
        region_attr[15] = 2'b00;
    end

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            operation_complete <= 1'b0;
            access_violation <= 1'b0;
        end
        else begin
            access_violation <= 1'b0;

            if (write_enable) begin
                // SICHER: Schreibberechtigung prüfen
                if (region_writable) begin
                    memory[addr[15:2]] <= write_data;
                    operation_complete <= 1'b1;
                end
                else begin
                    // SICHER: Schreiben in nicht-beschreibbare Region verweigern
                    access_violation <= 1'b1;
                    operation_complete <= 1'b0;
                end
            end
            else if (fetch_enable) begin
                // SICHER: Ausführungsberechtigung prüfen
                if (region_executable) begin
                    read_data <= memory[addr[15:2]];
                    operation_complete <= 1'b1;
                end
                else begin
                    // SICHER: Ausführung aus nicht-ausführbarer Region verweigern
                    access_violation <= 1'b1;
                    operation_complete <= 1'b0;
                end
            end
            else if (read_enable) begin
                // Datenlesen aus jeder lesbaren Region erlaubt
                read_data <= memory[addr[15:2]];
                operation_complete <= 1'b1;
            end
            else begin
                operation_complete <= 1'b0;
            end
        end
    end

endmodule

// SICHER: MPU korrekt konfiguriert und aktiviert
module secure_mpu_enabled (
    input wire clk,
    input wire reset_n,
    input wire mpu_enable,
    input wire [31:0] access_addr,
    input wire access_write,
    input wire access_execute,
    output reg access_allowed,
    output reg access_fault
);

    // MPU-Regionskonfiguration
    reg [31:0] region_base [0:7];
    reg [31:0] region_limit [0:7];
    reg region_writable [0:7];
    reg region_executable [0:7];
    reg region_enabled [0:7];

    // Passende Region finden
    reg region_found;
    reg [2:0] matched_region;
    reg match_writable;
    reg match_executable;

    integer i;

    always @(*) begin
        region_found = 1'b0;
        matched_region = 3'h0;
        match_writable = 1'b0;
        match_executable = 1'b0;

        // Regionen durchsuchen (höhere Priorität zuerst)
        for (i = 7; i >= 0; i = i - 1) begin
            if (region_enabled[i] &&
                access_addr >= region_base[i] &&
                access_addr < region_limit[i]) begin
                region_found = 1'b1;
                matched_region = i;
                match_writable = region_writable[i];
                match_executable = region_executable[i];
            end
        end
    end

    always @(*) begin
        access_fault = 1'b0;

        if (!mpu_enable) begin
            // SICHER: Standardmäßig verweigern wenn MPU deaktiviert
            access_allowed = 1'b0;
            access_fault = 1'b1;
        end
        else if (!region_found) begin
            // Keine passende Region - verweigern
            access_allowed = 1'b0;
            access_fault = 1'b1;
        end
        else begin
            // SICHER: Berechtigungen prüfen
            if (access_write && !match_writable) begin
                access_allowed = 1'b0;
                access_fault = 1'b1;
            end
            else if (access_execute && !match_executable) begin
                access_allowed = 1'b0;
                access_fault = 1'b1;
            end
            // SICHER: W^X-Prüfung
            else if (access_write && access_execute) begin
                // Gleichzeitiges Schreiben und Ausführen niemals erlauben
                access_allowed = 1'b0;
                access_fault = 1'b1;
            end
            else begin
                access_allowed = 1'b1;
            end
        end
    end

endmodule

CVE-Beispiele

  • MCS51 Mikrocontroller: Fehlt sowohl dediziertes Exklusivitäts-Bit als auch MMU/MPU-Unterstützung, wodurch gesamter Speicher sowohl beschreibbar als auch ausführbar ist.
  • Cortex-M ohne MPU-Konfiguration: MPU existiert aber bleibt unkonfiguriert, was Code-Injection-Angriffe ermöglicht.
  • Verschiedene eingebettete Systeme und IoT-Geräte ohne W^X-Durchsetzung.

Verwandte CWEs

  • CWE-284: Unsachgemäße Zugriffskontrolle (übergeordnet)
  • CWE-119: Unsachgemäße Beschränkung von Operationen innerhalb der Grenzen eines Speicherpuffers (verwandt)
  • CWE-94: Unsachgemäße Kontrolle der Codegenerierung ('Code Injection') (verwandt)
  • CAPEC-679: Ausnutzung unsachgemäß konfigurierter Speicherschutzmaßnahmen (Angriffsmuster)

Referenzen

  1. MITRE Corporation. "CWE-1252: CPU Hardware Not Configured to Support Exclusivity of Write and Execute Operations." https://cwe.mitre.org/data/definitions/1252.html
  2. ARM. "Cortex-M MPU Configuration Guide"
  3. Intel. "Execute Disable Bit Functionality"