Unzureichende Zugriffskontrolle für fluchtigen Speicher mit Boot-Code

Beschreibung

Unzureichende Zugriffskontrolle für fluchtigen Speicher mit Boot-Code tritt auf, wenn ein Secure-Boot-Prozess Bootloader-Code von nichtfluchtigem Speicher (NVM) in fluchtigen Speicher (VM) ubertragt, ohne ausreichende Zugriffskontrollen zu implementieren. Während des Secure-Boot holt ROM-Code in einem System-on-Chip Bootloader-Code von externem NVM und speichert ihn in internem VM (DRAM/SRAM). Während der Code während der Übertragung authentifiziert wird, fehlen dem resultierenden fluchtigen Speicher angemessene Schutzmaßnahmen gegen Modifikation. Angreifer könnten den Secure-Boot-Prozess umgehen und ihren eigenen nicht vertrauenswurdigen, bösartigen Boot-Code ausführen.

Risiko

Unzureichender Schutz fluchtigen Speichers hat schwerwiegende Sicherheitsauswirkungen. Boot-Code kann nach der Authentifizierung modifiziert werden. Secure Boot kann vollständig umgangen werden. Bosartiger Code kann ausgeführt werden. Berechtigungserweiterung wird möglich. Systemintegritat ist kompromittiert. Die Vertrauenskette ist gebrochen. Firmware-Rootkits können installiert werden. Das Gerat kann dauerhaft kompromittiert werden.

Lösung

Entwerfen Sie Schutzmaßnahmen für fluchtigen Speicher, die Modifikationen durch Angreifer verhindern, nachdem Boot-Code geladen wurde. Testen Sie Schutzmaßnahmen grundlich gegen unbefugte Zugriffsversuche. Verifizieren Sie, dass fluchtiger Speicher sperrbar ist und während des Boots ordnungsgemäß gesperrt wird. Testen Sie Schreibversuche von nicht vertrauenswurdigen Agenten um Ablehnung sicherzustellen. Erfassen Sie Fabric-Master-Agenten, die während des Code-Ladens aktiv sind, und verifizieren Sie Zugriffsbeschrankungen.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleUmfang: Zugriffskontrolle

Schutzmechanismus umgehen - Secure Boot kann durch Speichermodifikation umgangen werden.
IntegritatUmfang: Integritat

Speicher modifizieren - Boot-Code kann nach der Authentifizierung verändert werden.
AutorisierungUmfang: Autorisierung

Unbefugten Code ausführen - Bosartiger Boot-Code kann ausgeführt werden.
AuthentifizierungUmfang: Authentifizierung

Berechtigungen erlangen - Angreifer können Systemberechtigungen erlangen.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Boot-Code-Speicher ohne Zugriffskontrolle

module vulnerable_boot_memory (
    input wire clk,
    input wire reset_n,
    input wire [31:0] address,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    input wire [3:0] master_id,
    output reg [31:0] read_data,
    // Boot-Steuerung
    input wire boot_complete,
    input wire signature_verified
);

    // Boot-Code-Speicherbereich
    reg [31:0] boot_memory [0:4095];

    // Speicherbereich für Boot-Code
    parameter BOOT_START = 32'h0000_0000;
    parameter BOOT_END = 32'h0000_FFFF;

    wire is_boot_region = (address >= BOOT_START) && (address <= BOOT_END);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            read_data <= 32'h0;
        end
        else begin
            // VERWUNDBAR: Keine Zugriffskontrolle nach Laden des Boot-Codes
            // Jeder Master kann in Boot-Speicher schreiben

            if (write_enable && is_boot_region) begin
                // VERWUNDBAR: Schreiben auch nach Signaturverifizierung erlaubt
                boot_memory[address[13:2]] <= write_data;

                // Problem: DMA-Controller (master_id = 4) kann Boot-Code
                // überschreiben nachdem er authentifiziert wurde
            end

            if (read_enable && is_boot_region) begin
                read_data <= boot_memory[address[13:2]];
            end
        end
    end

endmodule

// VERWUNDBAR: Bootloader ohne Speichersperre
module vulnerable_boot_loader (
    input wire clk,
    input wire reset_n,
    // NVM-Schnittstelle
    output reg [31:0] nvm_addr,
    input wire [31:0] nvm_data,
    output reg nvm_read,
    // VM-Schnittstelle
    output reg [31:0] vm_addr,
    output reg [31:0] vm_data,
    output reg vm_write,
    // Steuerung
    input wire start_boot,
    output reg boot_done,
    output reg signature_valid
);

    reg [15:0] byte_count;
    reg [2:0] state;

    parameter IDLE = 3'd0;
    parameter LOAD = 3'd1;
    parameter VERIFY = 3'd2;
    parameter EXECUTE = 3'd3;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            state <= IDLE;
            boot_done <= 1'b0;
            signature_valid <= 1'b0;
        end
        else begin
            case (state)
                IDLE: begin
                    if (start_boot) begin
                        state <= LOAD;
                        byte_count <= 16'h0;
                    end
                end

                LOAD: begin
                    // Boot-Code von NVM in VM laden
                    nvm_addr <= byte_count;
                    nvm_read <= 1'b1;

                    vm_addr <= byte_count;
                    vm_data <= nvm_data;
                    vm_write <= 1'b1;

                    byte_count <= byte_count + 4;

                    if (byte_count >= 16'hFFF0) begin
                        state <= VERIFY;
                    end
                end

                VERIFY: begin
                    // Signatur verifizieren
                    if (verify_signature()) begin
                        signature_valid <= 1'b1;
                        state <= EXECUTE;
                    end
                    else begin
                        state <= IDLE;
                    end
                end

                EXECUTE: begin
                    // VERWUNDBAR: Keine Speichersperre vor Ausführung
                    // Boot-Code kann noch modifiziert werden!
                    boot_done <= 1'b1;
                    // Boot-Code ausführen...
                end
            endcase
        end
    end

endmodule
// VERWUNDBAR: Software-Boot ohne Speicherschutz

#include <stdint.h>

#define BOOT_MEMORY_BASE 0x20000000
#define BOOT_MEMORY_SIZE 0x10000
#define NVM_BASE 0x10000000

// VERWUNDBAR: Kein Speicherschutz nach Laden des Boot-Codes
void vulnerable_load_boot_code(void) {
    uint32_t* boot_mem = (uint32_t*)BOOT_MEMORY_BASE;
    uint32_t* nvm = (uint32_t*)NVM_BASE;

    // Boot-Code von NVM in fluchtigen Speicher kopieren
    for (int i = 0; i < BOOT_MEMORY_SIZE/4; i++) {
        boot_mem[i] = nvm[i];
    }

    // Signatur verifizieren
    if (!verify_boot_signature(boot_mem, BOOT_MEMORY_SIZE)) {
        halt();
        return;
    }

    // VERWUNDBAR: Kein Speicherschutz aktiviert
    // Boot-Code kann zwischen Verifizierung und Ausführung modifiziert werden

    // Boot-Code ausführen
    void (*boot_entry)(void) = (void(*)(void))BOOT_MEMORY_BASE;
    boot_entry();

    // Angriff: DMA oder ein anderer Master modifiziert boot_mem nach
    // Signaturverifizierung aber vor Ausführung
}

// VERWUNDBAR: MPU nicht für Boot-Speicher konfiguriert
void vulnerable_mpu_setup(void) {
    // MPU für andere Regionen konfigurieren
    configure_mpu_region(0, PERIPHERAL_BASE, PERIPHERAL_SIZE, MPU_RW);
    configure_mpu_region(1, RAM_BASE, RAM_SIZE, MPU_RW);

    // VERWUNDBAR: Boot-Speicherregion nicht geschützt
    // Keine MPU-Region für BOOT_MEMORY_BASE konfiguriert

    // Jeder Code kann Boot-Speicher modifizieren!
}

Sichere Lösung

// SICHER: Boot-Code-Speicher mit ordnungsgemäßer Zugriffskontrolle

module secure_boot_memory (
    input wire clk,
    input wire reset_n,
    input wire [31:0] address,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    input wire [3:0] master_id,
    output reg [31:0] read_data,
    output reg access_denied,
    // Boot-Steuerung
    input wire boot_loading,        // Boot-Code wird geladen
    input wire signature_verified,  // Signaturprüfung bestanden
    input wire boot_complete,       // Boot-Prozess abgeschlossen
    // Sperrsteuerung
    input wire lock_boot_memory,    // Sperrbefehl
    output reg boot_memory_locked
);

    // Boot-Code-Speicherbereich
    reg [31:0] boot_memory [0:4095];

    // Speicherbereich für Boot-Code
    parameter BOOT_START = 32'h0000_0000;
    parameter BOOT_END = 32'h0000_FFFF;

    // Vertrauenswurdige Master
    parameter BOOT_ROM = 4'd0;
    parameter SECURE_DMA = 4'd1;

    wire is_boot_region = (address >= BOOT_START) && (address <= BOOT_END);
    wire is_trusted_master = (master_id == BOOT_ROM) || (master_id == SECURE_DMA);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            read_data <= 32'h0;
            access_denied <= 1'b0;
            boot_memory_locked <= 1'b0;
        end
        else begin
            access_denied <= 1'b0;

            // SICHER: Boot-Speicher nach Signaturverifizierung sperren
            if (lock_boot_memory || signature_verified) begin
                boot_memory_locked <= 1'b1;
            end

            if (write_enable && is_boot_region) begin
                // SICHER: Zugriffsberechtigungen prüfen
                if (boot_memory_locked) begin
                    // Speicher gesperrt - alle Schreibvorgange verweigern
                    access_denied <= 1'b1;
                end
                else if (boot_loading && is_trusted_master) begin
                    // Nur Schreibvorgange während des Ladens von vertrauenswurdigen Mastern erlauben
                    boot_memory[address[13:2]] <= write_data;
                end
                else begin
                    access_denied <= 1'b1;
                end
            end

            if (read_enable && is_boot_region) begin
                // SICHER: Lesezugriff kontrolliert
                if (is_trusted_master || boot_complete) begin
                    read_data <= boot_memory[address[13:2]];
                end
                else begin
                    read_data <= 32'h0;
                    access_denied <= 1'b1;
                end
            end
        end
    end

endmodule

// SICHER: Bootloader mit Speichersperre
module secure_boot_loader (
    input wire clk,
    input wire reset_n,
    // NVM-Schnittstelle
    output reg [31:0] nvm_addr,
    input wire [31:0] nvm_data,
    output reg nvm_read,
    // VM-Schnittstelle
    output reg [31:0] vm_addr,
    output reg [31:0] vm_data,
    output reg vm_write,
    // Speicherschutz
    output reg lock_boot_memory,
    input wire memory_locked,
    // Steuerung
    input wire start_boot,
    output reg boot_done,
    output reg signature_valid,
    output reg boot_error
);

    reg [15:0] byte_count;
    reg [3:0] state;

    parameter IDLE = 4'd0;
    parameter LOAD = 4'd1;
    parameter VERIFY = 4'd2;
    parameter LOCK = 4'd3;
    parameter VERIFY_LOCK = 4'd4;
    parameter EXECUTE = 4'd5;
    parameter ERROR = 4'd6;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            state <= IDLE;
            boot_done <= 1'b0;
            signature_valid <= 1'b0;
            lock_boot_memory <= 1'b0;
            boot_error <= 1'b0;
        end
        else begin
            case (state)
                IDLE: begin
                    boot_error <= 1'b0;
                    if (start_boot) begin
                        state <= LOAD;
                        byte_count <= 16'h0;
                    end
                end

                LOAD: begin
                    // Boot-Code von NVM in VM laden
                    nvm_addr <= byte_count;
                    nvm_read <= 1'b1;

                    vm_addr <= byte_count;
                    vm_data <= nvm_data;
                    vm_write <= 1'b1;

                    byte_count <= byte_count + 4;

                    if (byte_count >= 16'hFFF0) begin
                        state <= VERIFY;
                    end
                end

                VERIFY: begin
                    vm_write <= 1'b0;

                    // Signatur verifizieren
                    if (verify_signature()) begin
                        signature_valid <= 1'b1;
                        state <= LOCK;
                    end
                    else begin
                        state <= ERROR;
                    end
                end

                LOCK: begin
                    // SICHER: Boot-Speicher vor Ausführung sperren
                    lock_boot_memory <= 1'b1;
                    state <= VERIFY_LOCK;
                end

                VERIFY_LOCK: begin
                    // SICHER: Verifizieren dass Speicher tatsachlich gesperrt ist
                    if (memory_locked) begin
                        state <= EXECUTE;
                    end
                    else begin
                        // Sperre fehlgeschlagen - abbrechen
                        state <= ERROR;
                    end
                end

                EXECUTE: begin
                    // SICHER: Nur ausführen nachdem Speicher gesperrt ist
                    boot_done <= 1'b1;
                    // Boot-Code sicher ausführen
                end

                ERROR: begin
                    boot_error <= 1'b1;
                    // Anhalten oder erneut versuchen
                end
            endcase
        end
    end

endmodule

// SICHER: Speicherschutzeinheit für Boot-Code
module boot_memory_protection (
    input wire clk,
    input wire reset_n,
    input wire [31:0] address,
    input wire [3:0] master_id,
    input wire access_type,  // 0=Lesen, 1=Schreiben
    input wire boot_phase_complete,
    input wire lock_enable,
    output reg access_allowed,
    output reg protection_violation
);

    parameter BOOT_START = 32'h0000_0000;
    parameter BOOT_END = 32'h0000_FFFF;
    parameter BOOT_ROM = 4'd0;

    reg memory_locked;

    wire is_boot_region = (address >= BOOT_START) && (address <= BOOT_END);
    wire is_boot_rom = (master_id == BOOT_ROM);

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

            // SICHER: Speicher nach Boot permanent sperren
            if (lock_enable || boot_phase_complete) begin
                memory_locked <= 1'b1;
            end

            // SICHER: Zugriffskontrolllogik
            if (is_boot_region) begin
                if (access_type == 1'b1) begin  // Schreibzugriff
                    if (memory_locked) begin
                        // SICHER: Keine Schreibvorgange nach Sperre
                        access_allowed <= 1'b0;
                        protection_violation <= 1'b1;
                    end
                    else if (is_boot_rom) begin
                        // Nur Boot-ROM kann vor Sperre schreiben
                        access_allowed <= 1'b1;
                    end
                    else begin
                        access_allowed <= 1'b0;
                        protection_violation <= 1'b1;
                    end
                end
                else begin  // Lesezugriff
                    // Lesen nach Boot-Abschluss erlaubt
                    access_allowed <= boot_phase_complete || is_boot_rom;
                end
            end
            else begin
                access_allowed <= 1'b1;  // Nicht-Boot-Regionen
            end
        end
    end

endmodule
// SICHER: Software-Boot mit ordnungsgemäßem Speicherschutz

#include <stdint.h>
#include <stdbool.h>

#define BOOT_MEMORY_BASE 0x20000000
#define BOOT_MEMORY_SIZE 0x10000
#define NVM_BASE 0x10000000

// MPU-Region für Boot-Speicher
#define MPU_BOOT_REGION 2

// SICHER: Boot-Speicher mit MPU sperren
static void lock_boot_memory(void) {
    // MPU-Region für Boot-Speicher als schreibgeschützt konfigurieren
    configure_mpu_region(
        MPU_BOOT_REGION,
        BOOT_MEMORY_BASE,
        BOOT_MEMORY_SIZE,
        MPU_RO | MPU_XN_CLEAR  // Schreibgeschützt, Ausführung erlaubt
    );

    // MPU aktivieren
    enable_mpu();

    // Memory Barrier um sicherzustellen, dass Schutz aktiv ist
    __asm__ volatile("dmb sy" ::: "memory");
    __asm__ volatile("isb" ::: "memory");
}

// SICHER: Verifizieren dass Boot-Speicher geschützt ist
static bool verify_boot_memory_locked(void) {
    // Versuchen in Boot-Speicher zu schreiben (sollte fehlschlagen)
    volatile uint32_t* test_addr = (volatile uint32_t*)BOOT_MEMORY_BASE;
    uint32_t original = *test_addr;

    // Temporar Fault-Handler deaktivieren um Schreibversuch abzufangen
    uint32_t fault_occurred = 0;
    set_mem_fault_handler(&fault_occurred);

    // Schreibversuch
    *test_addr = 0xDEADBEEF;

    // Fault-Handler wiederherstellen
    restore_mem_fault_handler();

    // Verifizieren dass Schreiben blockiert wurde
    if (!fault_occurred || *test_addr != original) {
        return false;  // Schutz funktioniert nicht
    }

    return true;
}

// SICHER: Sicherer Boot mit Speicherschutz
void secure_load_boot_code(void) {
    uint32_t* boot_mem = (uint32_t*)BOOT_MEMORY_BASE;
    uint32_t* nvm = (uint32_t*)NVM_BASE;

    // Boot-Code von NVM in fluchtigen Speicher kopieren
    for (int i = 0; i < BOOT_MEMORY_SIZE/4; i++) {
        boot_mem[i] = nvm[i];
    }

    // Memory Barrier vor Verifizierung
    __asm__ volatile("dmb sy" ::: "memory");

    // Signatur verifizieren
    if (!verify_boot_signature(boot_mem, BOOT_MEMORY_SIZE)) {
        log_error("Boot-Signaturverifizierung fehlgeschlagen");
        secure_halt();
        return;
    }

    // SICHER: Boot-Speicher vor Ausführung sperren
    lock_boot_memory();

    // SICHER: Verifizieren dass Schutz aktiv ist
    if (!verify_boot_memory_locked()) {
        log_error("Sperren des Boot-Speichers fehlgeschlagen");
        secure_halt();
        return;
    }

    // SICHER: Memory Barrier vor Ausführung
    __asm__ volatile("dmb sy" ::: "memory");
    __asm__ volatile("isb" ::: "memory");

    // Jetzt sicher auszuführen - Speicher ist geschützt
    void (*boot_entry)(void) = (void(*)(void))BOOT_MEMORY_BASE;
    boot_entry();
}

// SICHER: Umfassende MPU-Einrichtung
void secure_mpu_setup(void) {
    // MPU während Konfiguration deaktivieren
    disable_mpu();

    // Regionen konfigurieren
    configure_mpu_region(0, PERIPHERAL_BASE, PERIPHERAL_SIZE, MPU_RW | MPU_XN);
    configure_mpu_region(1, RAM_BASE, RAM_SIZE, MPU_RW | MPU_XN);

    // SICHER: Boot-Speicherregion - anfangs RW für Laden
    configure_mpu_region(
        MPU_BOOT_REGION,
        BOOT_MEMORY_BASE,
        BOOT_MEMORY_SIZE,
        MPU_RW | MPU_XN_CLEAR  // RW während Laden, wird auf RO geändert
    );

    // MPU mit Standard-Verweigerung aktivieren
    enable_mpu_with_default_deny();
}

CVE-Beispiele

  • CVE-2019-2267: Gesperrte Speicherregionen könnten über andere Schnittstellen in einem Secure-Boot-Loader-Image aufgrund unzureichender Zugriffskontrolle modifiziert werden, was die Umgehung von Secure Boot ermöglichte.

Verwandte CWEs

  • CWE-284: Unzureichende Zugriffskontrolle (ubergeordnet)
  • CWE-1196: Sicherheitsfluss-Probleme (Kategorie)
  • CWE-693: Versagen des Schutzmechanismus (verwandt)
  • CAPEC-456: Infizierter Speicher (Angriffsmuster)
  • CAPEC-679: Ausnutzung unsachgemäß konfigurierter Speicherschutzmaßnahmen (Angriffsmuster)

Referenzen

  1. MITRE Corporation. "CWE-1274: Improper Access Control for Volatile Memory Containing Boot Code." https://cwe.mitre.org/data/definitions/1274.html
  2. ARM. "TrustZone Security for Boot"
  3. NIST. "Secure Boot Guidelines"