Unzureichende oder unvollständige Datenentfernung innerhalb von Hardware-Komponenten

Beschreibung

Unzureichende oder unvollständige Datenentfernung innerhalb von Hardware-Komponenten tritt auf, wenn der Datenentfernungsprozess eines Produkts nicht alle sensiblen Informationen vollständig eliminiert, die in Hardware-Komponenten gespeichert sind. Physikalische Eigenschaften von Hardware-Geräten - wie magnetische Medienremanenz, Rest-ROM/RAM-Ladung oder Bildschirm-Einbrennen - können sensible Daten nach Löschung und Stromabschaltung zurückbehalten. Diese Schwäche stammt von Datenremanenz, einem Phänomen, bei dem wiederholte Schreibvorgänge an Speicherstellen physisch Zellen verändern, sodass selbst nach dem Löschen der Originaldaten diese Daten durch physikalische Charakterisierung dieser Zellen wiederhergestellt werden können.

Risiko

Unvollständige Datenentfernung hat schwerwiegende Sicherheitsauswirkungen. Kryptographische Schlüssel nach Löschung wiederherstellbar. Benutzerdaten verbleiben auf "gelöschten" Geräten. Werksreset löscht Geheimnisse nicht. Daten des Vorbesitzers zugänglich. Speicherremanenz ermöglicht Cold-Boot-Angriffe. Bildschirm-Einbrennen enthüllt sensible Muster. Physikalische Analyse stellt "gelöschte" Daten wieder her. Besonders schwerwiegend für Geräte, die den Besitzer wechseln.

Lösung

Wenden Sie Blinding- oder Masking-Techniken an, um Speicherverschleißmuster während der Architektur- und Designphase zu reduzieren. Modifizieren Sie Löschmethoden während der Implementierung - verwenden Sie mehrere Überschreibdurchgänge, fügen Sie Medienschutz hinzu oder zerstoren Sie Speichermedien physisch, um Datenwiederherstellung zu verhindern. Implementieren Sie ordnungsgemäße Secure-Erase-Sequenzen für verschiedene Speichertypen. Verifizieren Sie die Löscheffektivität durch Tests. Erwägen Sie manipulationssichere Gehäuse.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitScope: Vertraulichkeit

Sensible Informationen einschließlich Schlüsseln und Benutzerdaten nach Löschung wiederherstellbar.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Unzureichende Datenlöschung

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

// VERWUNDBAR: Einfaches memset für sensible Daten
void vulnerable_erase_key(uint8_t* key, size_t len) {
    // VERWUNDBAR: Einzeldurchgang-Überschreibung
    memset(key, 0, len);

    // Probleme:
    // 1. Compiler kann "totes" memset wegoptimieren
    // 2. Einzeldurchgang eliminiert Remanenz nicht
    // 3. Speicherzellen behalten physische Spuren
}

// VERWUNDBAR: Werksreset löscht nicht alle Daten
typedef struct {
    uint8_t  wifi_password[64];
    uint8_t  user_pin[16];
    uint8_t  encryption_key[32];
    uint32_t user_settings;
    uint8_t  firmware_config[256];
} device_storage_t;

void vulnerable_factory_reset(device_storage_t* storage) {
    // VERWUNDBAR: Löscht nur einige Felder
    memset(storage->user_pin, 0, sizeof(storage->user_pin));
    memset(&storage->user_settings, 0, sizeof(storage->user_settings));

    // VERWUNDBAR: Lässt WLAN-Passwort und Verschlüsselungsschlüssel!
    // Anmeldedaten des Vorbesitzers bleiben zugänglich

    // Auf Standardwerte zurücksetzen
    storage->firmware_config[0] = 0x01;  // Reset-Flag
}

// VERWUNDBAR: Speicher wird vor Ausschalten nicht gelöscht
void vulnerable_shutdown(void) {
    // VERWUNDBAR: Schaltet nur aus ohne RAM zu löschen
    // Sensible Daten verbleiben minutenlang in DRAM nach Ausschalten
    // Cold-Boot-Angriff kann Verschlüsselungsschlüssel wiederherstellen

    power_off();
}

// VERWUNDBAR: Dateilöschung löscht keine Daten
void vulnerable_delete_file(const char* filename) {
    // VERWUNDBAR: Entfernt nur Verzeichniseintrag
    // Tatsächliche Datenblöcke verbleiben auf Speicher
    remove(filename);

    // Dateiinhalt noch wiederherstellbar über:
    // - Direkte Sektorzugriffe
    // - Data-Carving-Tools
    // - Physikalische Medienanalyse
}
// VERWUNDBAR: Hardware ohne ordnungsgemäßes Memory-Scrubbing

module vulnerable_memory_controller (
    input  wire        clk,
    input  wire        rst_n,
    input  wire        erase_command,
    input  wire [15:0] erase_start,
    input  wire [15:0] erase_end,
    output reg         erase_done
);

    reg [31:0] memory [0:65535];
    reg [15:0] erase_addr;
    reg erasing;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            erase_done <= 1'b0;
            erasing <= 1'b0;
        end else if (erase_command && !erasing) begin
            erasing <= 1'b1;
            erase_addr <= erase_start;
        end else if (erasing) begin
            // VERWUNDBAR: Einzeldurchgang mit Nullfüllung
            memory[erase_addr] <= 32'h0000_0000;

            if (erase_addr == erase_end) begin
                erase_done <= 1'b1;
                erasing <= 1'b0;
            end else begin
                erase_addr <= erase_addr + 1;
            end
        end
    end

    // VERWUNDBAR: Einzeldurchgang eliminiert Remanenz nicht
    // Physikalische Analyse kann noch Originaldaten wiederherstellen

endmodule

// VERWUNDBAR: Schlüsselspeicher ohne Secure Erase
module vulnerable_key_storage (
    input  wire        clk,
    input  wire        rst_n,
    input  wire        store_key,
    input  wire        clear_key,
    input  wire [255:0] key_in,
    output reg  [255:0] key_out,
    output reg         key_valid
);

    reg [255:0] stored_key;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            stored_key <= 256'b0;  // VERWUNDBAR: Nur initiale Löschung
            key_valid <= 1'b0;
        end else if (store_key) begin
            stored_key <= key_in;
            key_valid <= 1'b1;
        end else if (clear_key) begin
            // VERWUNDBAR: Einzelnes Schreiben von Nullen
            stored_key <= 256'b0;
            key_valid <= 1'b0;
            // Remanenz kann Schlüsselmuster behalten
        end
    end

    assign key_out = stored_key;

endmodule

Sichere Lösung

// SICHER: Ordnungsgemäße sichere Datenlöschung

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

// SICHER: Sicheres Speicherlöschen, das Compiler nicht wegoptimiert
void secure_memzero(volatile void* ptr, size_t len) {
    volatile uint8_t* p = (volatile uint8_t*)ptr;

    // SICHER: volatile verhindert Optimierung
    while (len--) {
        *p++ = 0;
    }

    // SICHER: Memory-Barrier um sicherzustellen, dass Schreibvorgänge abgeschlossen werden
    __asm__ __volatile__("" ::: "memory");
}

// SICHER: Mehrdurchgang-Überschreibung für sensible Daten
void secure_erase_key(uint8_t* key, size_t len) {
    // SICHER: Mehrere Überschreibmuster
    // Muster 1: Alle Nullen
    for (size_t i = 0; i < len; i++) {
        ((volatile uint8_t*)key)[i] = 0x00;
    }

    // Muster 2: Alle Einsen
    for (size_t i = 0; i < len; i++) {
        ((volatile uint8_t*)key)[i] = 0xFF;
    }

    // Muster 3: Zufallsdaten
    uint8_t random_bytes[256];
    get_random_bytes(random_bytes, len > 256 ? 256 : len);
    for (size_t i = 0; i < len; i++) {
        ((volatile uint8_t*)key)[i] = random_bytes[i % 256];
    }

    // Muster 4: Finale Nullen
    for (size_t i = 0; i < len; i++) {
        ((volatile uint8_t*)key)[i] = 0x00;
    }

    // SICHER: Zufallspuffer ebenfalls löschen
    secure_memzero(random_bytes, sizeof(random_bytes));
}

// SICHER: Vollständiger Werksreset
typedef struct {
    uint8_t  wifi_password[64];
    uint8_t  user_pin[16];
    uint8_t  encryption_key[32];
    uint32_t user_settings;
    uint8_t  firmware_config[256];
} device_storage_t;

void secure_factory_reset(device_storage_t* storage) {
    // SICHER: ALLE sensiblen Felder löschen

    // WLAN-Passwort - Secure Erase
    secure_erase_key(storage->wifi_password, sizeof(storage->wifi_password));

    // PIN - Secure Erase
    secure_erase_key(storage->user_pin, sizeof(storage->user_pin));

    // Verschlüsselungsschlüssel - am kritischsten
    secure_erase_key(storage->encryption_key, sizeof(storage->encryption_key));

    // Benutzereinstellungen
    storage->user_settings = 0;

    // SICHER: Gesamte Struktur mit mehreren Durchgängen löschen
    secure_memzero(storage, sizeof(device_storage_t));

    // SICHER: Bekannte gute Standardwerte schreiben
    init_default_config(storage);

    // SICHER: Löschung verifizieren
    if (!verify_erasure_complete(storage)) {
        // Löschung fehlgeschlagen - sicheren Fehlermodus betreten
        enter_secure_lockdown();
    }
}

// SICHER: RAM vor Herunterfahren löschen
void secure_shutdown(void) {
    // SICHER: Alle sensiblen Speicherbereiche löschen
    clear_key_storage();
    clear_session_data();
    clear_crypto_context();

    // SICHER: Gesamten RAM scrubben wenn möglich
    scrub_ram_region(RAM_START, RAM_SIZE);

    // SICHER: Sicherstellen, dass Scrubbing vor Ausschalten abgeschlossen ist
    sync_memory();

    power_off();
}

// SICHER: Sichere Dateilöschung
void secure_delete_file(const char* filename) {
    FILE* f = fopen(filename, "r+b");
    if (f == NULL) return;

    // SICHER: Dateigröße ermitteln
    fseek(f, 0, SEEK_END);
    long size = ftell(f);
    fseek(f, 0, SEEK_SET);

    // SICHER: Dateiinhalt mit mehreren Mustern überschreiben
    uint8_t buffer[4096];

    // Durchgang 1: Nullen
    memset(buffer, 0x00, sizeof(buffer));
    for (long i = 0; i < size; i += sizeof(buffer)) {
        size_t to_write = (size - i < sizeof(buffer)) ? (size - i) : sizeof(buffer);
        fwrite(buffer, 1, to_write, f);
    }
    fflush(f);
    fsync(fileno(f));

    // Durchgang 2: Einsen
    memset(buffer, 0xFF, sizeof(buffer));
    fseek(f, 0, SEEK_SET);
    for (long i = 0; i < size; i += sizeof(buffer)) {
        size_t to_write = (size - i < sizeof(buffer)) ? (size - i) : sizeof(buffer);
        fwrite(buffer, 1, to_write, f);
    }
    fflush(f);
    fsync(fileno(f));

    // Durchgang 3: Zufallsdaten
    fseek(f, 0, SEEK_SET);
    for (long i = 0; i < size; i += sizeof(buffer)) {
        get_random_bytes(buffer, sizeof(buffer));
        size_t to_write = (size - i < sizeof(buffer)) ? (size - i) : sizeof(buffer);
        fwrite(buffer, 1, to_write, f);
    }
    fflush(f);
    fsync(fileno(f));

    fclose(f);

    // SICHER: Jetzt Verzeichniseintrag entfernen
    remove(filename);
}
// SICHER: Hardware mit ordnungsgemäßem Memory-Scrubbing

module secure_memory_controller (
    input  wire        clk,
    input  wire        rst_n,
    input  wire        erase_command,
    input  wire [15:0] erase_start,
    input  wire [15:0] erase_end,
    input  wire [1:0]  erase_level,  // 0=schnell, 1=standard, 2=sicher
    output reg         erase_done,
    output reg         erase_verified
);

    reg [31:0] memory [0:65535];
    reg [15:0] erase_addr;
    reg [2:0]  pass_counter;
    reg [1:0]  current_level;
    reg erasing;
    reg verifying;

    // SICHER: LFSR für pseudo-zufälliges Muster
    reg [31:0] lfsr;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            erase_done <= 1'b0;
            erase_verified <= 1'b0;
            erasing <= 1'b0;
            verifying <= 1'b0;
            pass_counter <= 3'b0;
            lfsr <= 32'hDEADBEEF;
        end else if (erase_command && !erasing && !verifying) begin
            erasing <= 1'b1;
            erase_addr <= erase_start;
            pass_counter <= 3'b0;
            current_level <= erase_level;
            erase_done <= 1'b0;
            erase_verified <= 1'b0;
        end else if (erasing) begin
            // SICHER: Mehrdurchgang-Löschung basierend auf Sicherheitsstufe
            case (pass_counter)
                3'd0: begin
                    // Durchgang 1: Alle Nullen
                    memory[erase_addr] <= 32'h0000_0000;
                end

                3'd1: begin
                    // Durchgang 2: Alle Einsen
                    memory[erase_addr] <= 32'hFFFF_FFFF;
                end

                3'd2: begin
                    // Durchgang 3: Alternierendes Muster
                    memory[erase_addr] <= 32'hAAAA_AAAA;
                end

                3'd3: begin
                    // Durchgang 4: Inverses Alternieren
                    memory[erase_addr] <= 32'h5555_5555;
                end

                3'd4: begin
                    // Durchgang 5: Zufällig (nur sichere Stufe)
                    lfsr <= {lfsr[30:0], lfsr[31] ^ lfsr[21] ^ lfsr[1] ^ lfsr[0]};
                    memory[erase_addr] <= lfsr;
                end

                3'd5: begin
                    // Durchgang 6: Finale Nullen
                    memory[erase_addr] <= 32'h0000_0000;
                end
            endcase

            if (erase_addr == erase_end) begin
                // Ein Durchgang abgeschlossen
                if ((current_level == 2'b00 && pass_counter >= 3'd1) ||
                    (current_level == 2'b01 && pass_counter >= 3'd3) ||
                    (current_level == 2'b10 && pass_counter >= 3'd5)) begin
                    // SICHER: Verifizierungsphase betreten
                    erasing <= 1'b0;
                    verifying <= 1'b1;
                    erase_addr <= erase_start;
                end else begin
                    pass_counter <= pass_counter + 1;
                    erase_addr <= erase_start;
                end
            end else begin
                erase_addr <= erase_addr + 1;
            end
        end else if (verifying) begin
            // SICHER: Verifizieren, dass alle Stellen Null sind
            if (memory[erase_addr] != 32'h0000_0000) begin
                // Verifizierung fehlgeschlagen - Löschung neu starten
                verifying <= 1'b0;
                erasing <= 1'b1;
                erase_addr <= erase_start;
                pass_counter <= 3'b0;
            end else if (erase_addr == erase_end) begin
                // SICHER: Verifizierung abgeschlossen
                erase_done <= 1'b1;
                erase_verified <= 1'b1;
                verifying <= 1'b0;
            end else begin
                erase_addr <= erase_addr + 1;
            end
        end
    end

endmodule

// SICHER: Schlüsselspeicher mit Secure Erase
module secure_key_storage (
    input  wire        clk,
    input  wire        rst_n,
    input  wire        store_key,
    input  wire        clear_key,
    input  wire [255:0] key_in,
    output reg  [255:0] key_out,
    output reg         key_valid,
    output reg         erase_complete
);

    reg [255:0] stored_key;
    reg [2:0]   erase_pass;
    reg         erasing;

    // SICHER: Mehrdurchgang-Schlüssellöschung
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            stored_key <= 256'b0;
            key_valid <= 1'b0;
            erase_complete <= 1'b0;
            erasing <= 1'b0;
            erase_pass <= 3'b0;
        end else if (store_key && !erasing) begin
            stored_key <= key_in;
            key_valid <= 1'b1;
            erase_complete <= 1'b0;
        end else if (clear_key && !erasing) begin
            erasing <= 1'b1;
            erase_pass <= 3'b0;
            key_valid <= 1'b0;
            erase_complete <= 1'b0;
        end else if (erasing) begin
            case (erase_pass)
                3'd0: stored_key <= 256'hFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF;
                3'd1: stored_key <= 256'h0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
                3'd2: stored_key <= 256'hAAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA;
                3'd3: stored_key <= 256'h5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555;
                3'd4: stored_key <= 256'b0;  // Finale Nullen
                3'd5: begin
                    erase_complete <= 1'b1;
                    erasing <= 1'b0;
                end
            endcase

            if (erase_pass < 3'd5) begin
                erase_pass <= erase_pass + 1;
            end
        end
    end

    assign key_out = key_valid ? stored_key : 256'b0;

endmodule

CVE-Beispiele

  • CVE-2019-8575: Werksreset löschte nicht alle Benutzerdaten, was Wiederherstellung von WLAN-Anmeldedaten ermöglichte.
  • CVE-2020-8911: SSD-Firmware implementierte Secure Erase nicht ordnungsgemäß, wodurch verschlüsselte Daten wiederherstellbar blieben.

Verwandte CWEs

  • CWE-226: Sensitive Information in Resource Not Removed Before Reuse (übergeordnet)
  • CWE-1330: Remanent Data Readable after Memory Erase (untergeordnet)
  • CWE-1208: Cross-Cutting Problems (Kategorie)
  • CWE-212: Improper Removal of Sensitive Information Before Storage or Transfer (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1301: Insufficient or Incomplete Data Removal within Hardware Component." https://cwe.mitre.org/data/definitions/1301.html
  2. NIST SP 800-88: Guidelines for Media Sanitization
  3. IEEE. "Data Remanence in Semiconductor Devices"