Sensible nichtflüchtige Informationen nicht während Debug geschützt

Beschreibung

Sensible nichtflüchtige Informationen nicht während Debug geschützt tritt auf, wenn der Zugriff auf sicherheitsrelevante Informationen, die in Fuses gespeichert sind, während Debug-Operationen nicht eingeschränkt wird. Sicherheitsrelevante Werte wie Root-Schlüssel, Verschlüsselungsschlüssel, Fertigungsdaten und OEM-Informationen werden in Fuses programmiert. Beim Einschalten des Chips werden diese Werte aus den Fuses gelesen und in temporären Speicherorten wie Registern und lokalem Speicher abgelegt. Während sie normalerweise durch Zugriffskontrollen geschützt sind, können Debug-Operationen diese Schutzmechanismen umgehen, wenn sie nicht ordnungsgemäß eingeschränkt werden.

Risiko

Ungeschützter Debug-Zugriff auf Fuse-Daten hat schwerwiegende Sicherheitsauswirkungen. Root-Schlüssel können während des Debuggens extrahiert werden. Verschlüsselungsschlüssel können über Debug-Schnittstellen offengelegt werden. Fertigungsgeheimnisse können preisgegeben werden. OEM-spezifische Daten können kompromittiert werden. Secure-Boot-Schlüssel können ausgelesen werden. Geräteidentitäts-Anmeldeinformationen können gestohlen werden. Kryptographisches Material kann das Klonen von Geräten ermöglichen. Vertrauensanker können kompromittiert werden.

Lösung

Deaktivieren Sie den Zugriff auf sicherheitsrelevante Informationen, die in Fuses gespeichert sind, sowohl direkt als auch über temporäre Speicherorte, wenn der Debug-Modus aktiv ist. Implementieren Sie eine Debug-Authentifizierung, bevor Zugriff gewährt wird. Verwenden Sie Hardware-Gates, um das Lesen sensibler Register im Debug zu blockieren. Löschen Sie sensible Register beim Eintritt in den Debug-Modus. Überprüfen Sie den Debug-Schutz durch Sicherheitstests. Dokumentieren Sie, welche Assets während des Debuggens geschützt sind.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitUmfang: Vertraulichkeit

Anwendungsdaten lesen - Debug-Operationen können sicherheitsrelevante Informationen aus Fuses und deren temporären Speicherorten offenlegen.
ZugriffskontrolleUmfang: Zugriffskontrolle

Schutzmechanismus umgehen - Der Debug-Modus kann normale Zugriffskontrollen umgehen, die sensible Fuse-Daten schützen.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Debug-Modus legt Verschlüsselungsschlüssel offen

module vulnerable_aes_key_register (
    input wire clk,
    input wire reset_n,
    input wire [127:0] fuse_key,        // Schlüssel aus Fuses
    input wire fuse_valid,
    input wire debug_mode,
    input wire [31:0] debug_addr,
    input wire debug_read,
    output reg [127:0] key_register,
    output reg [31:0] debug_data
);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            key_register <= 128'h0;
        end
        else if (fuse_valid) begin
            // Schlüssel aus Fuses beim Einschalten laden
            key_register <= fuse_key;
        end
    end

    // VERWUNDBAR: Debug kann Schlüsselregister lesen!
    always @(posedge clk) begin
        if (debug_read) begin
            case (debug_addr)
                32'h0000: debug_data <= key_register[31:0];    // Schlüssel offengelegt!
                32'h0004: debug_data <= key_register[63:32];   // Schlüssel offengelegt!
                32'h0008: debug_data <= key_register[95:64];   // Schlüssel offengelegt!
                32'h000C: debug_data <= key_register[127:96];  // Schlüssel offengelegt!
                default: debug_data <= 32'h0;
            endcase
        end
    end

    // Angreifer im Debug-Modus kann den vollständigen Verschlüsselungsschlüssel lesen

endmodule

// VERWUNDBAR: Fuse-Werte über Debug-Schnittstelle lesbar
module vulnerable_fuse_controller (
    input wire clk,
    input wire reset_n,
    input wire debug_mode,
    input wire [7:0] debug_fuse_addr,
    input wire debug_fuse_read,
    output reg [31:0] debug_fuse_data,
    // Fuse-Array
    input wire [31:0] fuse_array [0:255]
);

    // VERWUNDBAR: Alle Fuses im Debug-Modus lesbar
    always @(posedge clk) begin
        if (debug_mode && debug_fuse_read) begin
            // Jede Fuse kann gelesen werden - einschließlich Geheimnisse!
            debug_fuse_data <= fuse_array[debug_fuse_addr];
        end
    end

    // Angreifer kann lesen:
    // - Root-Schlüssel
    // - Geräte-ID
    // - Fertigungsdaten
    // - Sicherheitskonfiguration
    // - OEM-Geheimnisse

endmodule

// VERWUNDBAR: Temporärer Schlüsselspeicher offengelegt
module vulnerable_boot_key_loader (
    input wire clk,
    input wire reset_n,
    input wire [255:0] boot_key_fuses,
    input wire boot_start,
    input wire debug_active,
    input wire [31:0] mem_addr,
    input wire mem_read,
    output reg [31:0] mem_data
);

    // Temporärer Speicher für Boot-Schlüssel
    reg [255:0] boot_key_temp;
    reg key_loaded;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            boot_key_temp <= 256'h0;
            key_loaded <= 1'b0;
        end
        else if (boot_start && !key_loaded) begin
            // Boot-Schlüssel aus Fuses laden
            boot_key_temp <= boot_key_fuses;
            key_loaded <= 1'b1;
        end
    end

    // VERWUNDBAR: Debug kann temporären Schlüsselspeicher lesen
    always @(*) begin
        if (debug_active && mem_read) begin
            case (mem_addr[7:0])
                8'h00: mem_data = boot_key_temp[31:0];
                8'h04: mem_data = boot_key_temp[63:32];
                8'h08: mem_data = boot_key_temp[95:64];
                8'h0C: mem_data = boot_key_temp[127:96];
                // ... alle 256 Bits lesbar
                default: mem_data = 32'h0;
            endcase
        end else begin
            mem_data = 32'h0;
        end
    end

endmodule
// VERWUNDBAR: Firmware legt Fuse-Daten während Debug offen

#define FUSE_BASE_ADDR 0x40010000
#define DEBUG_PORT_ADDR 0x40020000

// VERWUNDBAR: Debug-Befehl liest beliebige Fuse
void debug_read_fuse_command(uint8_t fuse_index) {
    uint32_t fuse_value = *(volatile uint32_t*)(FUSE_BASE_ADDR + fuse_index * 4);

    // An Debug-Port senden - legt Geheimnisse offen!
    *(volatile uint32_t*)DEBUG_PORT_ADDR = fuse_value;
}

// VERWUNDBAR: Schlüssel in Speicher geladen, im Debug lesbar
uint8_t encryption_key[32];

void load_encryption_key(void) {
    // Schlüssel aus Fuses in RAM lesen
    for (int i = 0; i < 32; i++) {
        encryption_key[i] = read_fuse(KEY_FUSE_START + i);
    }

    // Schlüssel jetzt im RAM - Debug kann Speicher auslesen!
}

Sichere Lösung

// SICHER: Debug-Modus blockiert Zugriff auf Verschlüsselungsschlüssel

module secure_aes_key_register (
    input wire clk,
    input wire reset_n,
    input wire [127:0] fuse_key,
    input wire fuse_valid,
    input wire debug_mode,
    input wire debug_authenticated,  // Debug erfordert Authentifizierung
    input wire [31:0] debug_addr,
    input wire debug_read,
    output reg [127:0] key_register,
    output reg [31:0] debug_data,
    output reg debug_access_violation
);

    // Schattenkopie für Normalbetrieb
    reg [127:0] key_shadow;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            key_register <= 128'h0;
            key_shadow <= 128'h0;
        end
        else if (fuse_valid && !debug_mode) begin
            // Schlüssel nur laden, wenn NICHT im Debug-Modus
            key_register <= fuse_key;
            key_shadow <= fuse_key;
        end
        else if (debug_mode && !debug_authenticated) begin
            // SICHER: Schlüssel bei unauthentifiziertem Debug löschen
            key_shadow <= 128'h0;
        end
    end

    // SICHER: Schlüssel NICHT im Debug-Modus lesbar
    always @(posedge clk) begin
        debug_access_violation <= 1'b0;

        if (debug_read) begin
            if (debug_addr >= 32'h0000 && debug_addr <= 32'h000F) begin
                // SICHER: Schlüsseladressen geben im Debug Nullen zurück
                if (debug_mode) begin
                    debug_data <= 32'h0;
                    debug_access_violation <= 1'b1;  // Versuch protokollieren
                end else begin
                    // Normalmodus ebenfalls vor externem Debug-Lesen geschützt
                    debug_data <= 32'h0;
                end
            end else begin
                // Andere Register können lesbar sein
                debug_data <= get_non_sensitive_register(debug_addr);
            end
        end
    end

endmodule

// SICHER: Fuse-Controller mit Debug-Schutz
module secure_fuse_controller (
    input wire clk,
    input wire reset_n,
    input wire debug_mode,
    input wire debug_authenticated,
    input wire [7:0] debug_fuse_addr,
    input wire debug_fuse_read,
    output reg [31:0] debug_fuse_data,
    output reg access_denied,
    input wire [31:0] fuse_array [0:255]
);

    // Sensible Fuse-Bereiche definieren
    parameter KEY_FUSE_START = 8'h00;
    parameter KEY_FUSE_END = 8'h1F;
    parameter OEM_FUSE_START = 8'h20;
    parameter OEM_FUSE_END = 8'h3F;

    function is_sensitive_fuse;
        input [7:0] addr;
        begin
            is_sensitive_fuse = (addr >= KEY_FUSE_START && addr <= KEY_FUSE_END) ||
                               (addr >= OEM_FUSE_START && addr <= OEM_FUSE_END);
        end
    endfunction

    always @(posedge clk) begin
        access_denied <= 1'b0;

        if (debug_mode && debug_fuse_read) begin
            if (is_sensitive_fuse(debug_fuse_addr)) begin
                // SICHER: Sensible Fuses im Debug nicht lesbar
                debug_fuse_data <= 32'h0;
                access_denied <= 1'b1;
            end
            else if (debug_authenticated) begin
                // Nicht-sensible Fuses mit Authentifizierung lesbar
                debug_fuse_data <= fuse_array[debug_fuse_addr];
            end
            else begin
                debug_fuse_data <= 32'h0;
                access_denied <= 1'b1;
            end
        end
        else begin
            debug_fuse_data <= 32'h0;
        end
    end

endmodule

// SICHER: Boot-Schlüssel wird beim Eintritt in Debug gelöscht
module secure_boot_key_loader (
    input wire clk,
    input wire reset_n,
    input wire [255:0] boot_key_fuses,
    input wire boot_start,
    input wire debug_active,
    input wire debug_entry,  // Steigende Flanke beim Eintritt in Debug
    input wire [31:0] mem_addr,
    input wire mem_read,
    output reg [31:0] mem_data,
    output reg key_valid
);

    reg [255:0] boot_key_temp;
    reg key_loaded;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            boot_key_temp <= 256'h0;
            key_loaded <= 1'b0;
            key_valid <= 1'b0;
        end
        else if (debug_entry) begin
            // SICHER: Schlüssel beim Eintritt in Debug-Modus löschen
            boot_key_temp <= 256'h0;
            key_valid <= 1'b0;
        end
        else if (boot_start && !key_loaded && !debug_active) begin
            // Schlüssel nur laden, wenn nicht im Debug
            boot_key_temp <= boot_key_fuses;
            key_loaded <= 1'b1;
            key_valid <= 1'b1;
        end
    end

    // SICHER: Debug kann Schlüsseladressen nicht lesen
    always @(*) begin
        if (mem_read) begin
            if (mem_addr[31:8] == 24'h000000) begin
                // Schlüsseladressbereich - immer 0 zurückgeben
                mem_data = 32'h0;
            end else begin
                // Andere Adressen können lesbar sein
                mem_data = normal_memory_read(mem_addr);
            end
        end else begin
            mem_data = 32'h0;
        end
    end

endmodule
// SICHER: Firmware schützt Fuse-Daten während Debug

#define FUSE_BASE_ADDR 0x40010000
#define DEBUG_PORT_ADDR 0x40020000

// Sensible Fuse-Bereiche
#define KEY_FUSE_START 0
#define KEY_FUSE_END 31
#define OEM_FUSE_START 32
#define OEM_FUSE_END 63

bool is_sensitive_fuse(uint8_t index) {
    return (index >= KEY_FUSE_START && index <= KEY_FUSE_END) ||
           (index >= OEM_FUSE_START && index <= OEM_FUSE_END);
}

// SICHER: Debug-Befehl blockiert sensible Fuses
void debug_read_fuse_command(uint8_t fuse_index) {
    if (is_sensitive_fuse(fuse_index)) {
        // Nullen für sensible Fuses zurückgeben
        *(volatile uint32_t*)DEBUG_PORT_ADDR = 0;
        log_security_event("Debug-Versuch, sensible Fuse %d zu lesen", fuse_index);
        return;
    }

    // Nicht-sensible Fuses können lesbar sein
    if (is_debug_authenticated()) {
        uint32_t fuse_value = *(volatile uint32_t*)(FUSE_BASE_ADDR + fuse_index * 4);
        *(volatile uint32_t*)DEBUG_PORT_ADDR = fuse_value;
    } else {
        *(volatile uint32_t*)DEBUG_PORT_ADDR = 0;
    }
}

// SICHER: Schlüssel im sicheren Speicher, beim Debug-Eintritt gelöscht
static uint8_t encryption_key[32] __attribute__((section(".secure_ram")));

void load_encryption_key(void) {
    if (is_debug_mode()) {
        // Schlüssel nicht im Debug-Modus laden
        return;
    }

    // Schlüssel aus Fuses in sicheren RAM lesen
    for (int i = 0; i < 32; i++) {
        encryption_key[i] = read_fuse(KEY_FUSE_START + i);
    }
}

void debug_entry_handler(void) {
    // SICHER: Alle sensiblen Daten beim Eintritt in Debug löschen
    secure_memzero(encryption_key, sizeof(encryption_key));

    // Alle anderen sensiblen temporären Speicher löschen
    clear_all_key_material();
}

CVE-Beispiele

Schwachstellen beim Debug-Zugriff auf Fuse-Daten wurden in verschiedenen SoC-Designs gefunden, bei denen Verschlüsselungsschlüssel und andere Geheimnisse über Debug-Schnittstellen extrahiert werden könnten.


Verwandte CWEs

  • CWE-1263: Unsachgemäße physische Zugriffskontrolle (übergeordnet)
  • CWE-1207: Debug- und Testprobleme (Kategoriemitglied)
  • CWE-1244: Internes Asset unsicherem Debug-Zugriffslevel ausgesetzt (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1243: Sensitive Non-Volatile Information Not Protected During Debug." https://cwe.mitre.org/data/definitions/1243.html
  2. REF-1366: HACK@DAC'21 OpenPiton SoC AES Wrapper Code
  3. REF-1367: Fixed Version of AES Wrapper Implementation