Unsachgemäße Loschung sensibler Daten von ausgemusterten Geraten

Beschreibung

Unsachgemäße Loschung sensibler Daten von ausgemusterten Geraten tritt auf, wenn ein Produkt keine ausreichende Fahigkeit bietet, damit Administratoren sensible Daten entfernen können, wenn Gerate außer Betrieb genommen werden. Dieses Fehlen, die Unzulanglichkeit oder die Fehlerhaftigkeit der Löschfunktionalität erzeugt Schwachstellen. Best Practices für die Außerbetriebnahme und regulatorische Anforderungen schreiben oft die Datenentfernung vor der Gerateausmusterung vor. Wenn sensible Informationen nicht ordnungsgemäß geloscht werden, können böswillige Akteure diese von entsorgten oder recycelten Geraten wiederherstellen.

Risiko

Unzureichende Datenlöschung hat schwerwiegende Sicherheitsauswirkungen. Authentifizierungsdaten können wiederhergestellt werden. Netzwerkkonfigurationen können offengelegt werden. Kryptografische Schlüssel können extrahiert werden. Proprietare Informationen können durchsickern. Kundendaten können kompromittiert werden. Geistiges Eigentum kann gestohlen werden. Compliance-Verstöße können auftreten. Reputationsschäden kann durch Datenverletzungen entstehen.

Lösung

Integrieren Sie umfassende Datenloschfunktionalitat während des initialen Designs, anstatt sie später nachzurusten. Dokumentieren Sie Speicherorte, Entfernungsrichtlinien und -verfahren in Administrationshandbuchern oder Volatilitätsaussagen. Stellen Sie Hilfsprogramme zum Loschen sensibler Daten aus nicht zuganglichen Speicherbereichen wie EEPROMs bereit. Implementieren Sie kryptografische Loschung, wo angemessen. Uberprüfen Sie die Wirksamkeit der Loschung durch Tests.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitUmfang: Vertraulichkeit

Speicher lesen - Angreifer erhalten unbefugten Zugriff auf gespeicherte sensible Informationen von ausgemusterten Geraten.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Gerat ohne Datenlöschfähigkeit

#include <stdint.h>

#define EEPROM_BASE 0x50000000
#define EEPROM_SIZE 4096

// Speicher für sensible Daten
typedef struct {
    uint8_t wifi_ssid[32];
    uint8_t wifi_password[64];
    uint8_t admin_password[32];
    uint8_t api_key[64];
    uint8_t encryption_key[32];
    uint8_t certificate[1024];
} device_credentials_t;

// VERWUNDBAR: Keine Loschfunktion vorgesehen
// Gerat speichert Anmeldedaten dauerhaft

void vulnerable_store_credentials(device_credentials_t* creds) {
    // Anmeldedaten ins EEPROM schreiben
    volatile uint8_t* eeprom = (volatile uint8_t*)EEPROM_BASE;
    memcpy((void*)eeprom, creds, sizeof(device_credentials_t));
}

void vulnerable_factory_reset(void) {
    // VERWUNDBAR: Setzt nur Konfigurations-Flags zurück
    // Loscht KEINE Anmeldedaten aus dem EEPROM

    config_flags = DEFAULT_FLAGS;
    reboot_device();

    // Anmeldedaten bleiben im EEPROM!
    // Wenn Gerat ausgemustert wird, kann Angreifer:
    // 1. EEPROM-Chip entfernen
    // 2. Mit Programmer auslesen
    // 3. Alle Anmeldedaten extrahieren
}

// VERWUNDBAR: Keine Datenzerstörung bei Außerbetriebnahme
void vulnerable_shutdown(void) {
    // Normales Herunterfahren - Daten bleiben erhalten
    save_state_to_flash();
    power_down();
}
// VERWUNDBAR: Hardware ohne sichere Loschfunktion

module vulnerable_secure_storage (
    input wire clk,
    input wire reset_n,
    input wire [7:0] addr,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    output reg [31:0] read_data
);

    // Nichtfluchtiger Speicher (NVM)
    reg [31:0] nvm_storage [0:255];

    // VERWUNDBAR: Kein Befehl zum sicheren Loschen
    // Daten bleiben auf unbestimmte Zeit erhalten

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            // VERWUNDBAR: Reset loscht NVM nicht
            // Loscht nur fluchtigen Zustand
            read_data <= 32'h0;
        end
        else if (write_enable) begin
            nvm_storage[addr] <= write_data;
        end
        else if (read_enable) begin
            read_data <= nvm_storage[addr];
        end
    end

    // Kein Befehl zum sicheren Loschen des gesamten Speichers
    // Keine Verifizierung, dass Daten tatsachlich geloscht wurden
    // Keine Nullung bei Manipulation oder Außerbetriebnahme

endmodule

// VERWUNDBAR: Gerat mit unvollständiger Loschung
module vulnerable_partial_scrub (
    input wire clk,
    input wire reset_n,
    input wire scrub_command,
    input wire [7:0] scrub_addr,
    output reg scrub_complete
);

    reg [31:0] config_storage [0:63];    // Wird geloscht
    reg [31:0] key_storage [0:31];       // VERWUNDBAR: NICHT geloscht
    reg [31:0] log_storage [0:127];      // VERWUNDBAR: NICHT geloscht

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            scrub_complete <= 1'b0;
        end
        else if (scrub_command) begin
            // VERWUNDBAR: Loscht nur Konfiguration, ubersieht Schlüssel und Logs
            config_storage[scrub_addr] <= 32'h0;

            // key_storage und log_storage werden NICHT geloscht!
            // Angreifer kann noch wiederherstellen:
            // - Verschlüsselungsschlüssel
            // - Zugriffsprotokolle mit Benutzerinformationen
            // - Netzwerkanmeldedaten in Protokollen gespeichert

            scrub_complete <= 1'b1;
        end
    end

endmodule

Sichere Lösung

// SICHER: Gerat mit umfassender Datenlöschung

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

#define EEPROM_BASE 0x50000000
#define EEPROM_SIZE 4096
#define SCRUB_PATTERN_COUNT 3

typedef struct {
    uint8_t wifi_ssid[32];
    uint8_t wifi_password[64];
    uint8_t admin_password[32];
    uint8_t api_key[64];
    uint8_t encryption_key[32];
    uint8_t certificate[1024];
} device_credentials_t;

// Dokumentierte Speicherorte für Außerbetriebnahme
typedef struct {
    const char* name;
    uint32_t address;
    uint32_t size;
    bool contains_sensitive_data;
} storage_location_t;

static const storage_location_t storage_map[] = {
    {"Anmeldedaten", 0x0000, sizeof(device_credentials_t), true},
    {"Verschlüsselungsschlüssel", 0x1000, 256, true},
    {"Protokolle", 0x2000, 1024, true},
    {"Konfiguration", 0x3000, 512, false},
    {NULL, 0, 0, false}
};

// SICHER: Sicheres Loschen mit Verifizierung
static void secure_memset(volatile void* ptr, uint8_t value, size_t size) {
    volatile uint8_t* p = (volatile uint8_t*)ptr;
    while (size--) {
        *p++ = value;
    }
    // Speicherbarriere
    __asm__ volatile("" ::: "memory");
}

// SICHER: Mehrfach-Überschreibung für sichere Loschung
static bool secure_erase_region(uint32_t address, uint32_t size) {
    volatile uint8_t* ptr = (volatile uint8_t*)(EEPROM_BASE + address);

    // Muster 1: Alles Nullen
    secure_memset(ptr, 0x00, size);
    if (!verify_pattern(ptr, 0x00, size)) return false;

    // Muster 2: Alles Einsen
    secure_memset(ptr, 0xFF, size);
    if (!verify_pattern(ptr, 0xFF, size)) return false;

    // Muster 3: Zufallsdaten
    uint8_t* random_data = get_random_bytes(size);
    memcpy((void*)ptr, random_data, size);
    free(random_data);

    // Muster 4: Abschließende Nullen
    secure_memset(ptr, 0x00, size);
    if (!verify_pattern(ptr, 0x00, size)) return false;

    return true;
}

// SICHER: Loschung verifizieren
static bool verify_pattern(volatile uint8_t* ptr, uint8_t expected, size_t size) {
    for (size_t i = 0; i < size; i++) {
        if (ptr[i] != expected) {
            return false;
        }
    }
    return true;
}

// SICHER: Umfassende Loschfunktion
int secure_decommission(void) {
    int errors = 0;

    log_event("Starte sichere Außerbetriebnahme");

    // SICHER: Alle dokumentierten Speicherorte loschen
    for (int i = 0; storage_map[i].name != NULL; i++) {
        log_event("Losche: %s", storage_map[i].name);

        if (!secure_erase_region(storage_map[i].address, storage_map[i].size)) {
            log_error("Loschung fehlgeschlagen: %s", storage_map[i].name);
            errors++;
        }
    }

    // SICHER: RAM mit sensiblen Daten loschen
    clear_sensitive_ram();

    // SICHER: Verschlüsselungsschlüssel in Hardware ungültig machen
    invalidate_hw_keys();

    // SICHER: Alle zwischengespeicherten Anmeldedaten loschen
    clear_credential_cache();

    // SICHER: Bericht erstellen
    generate_decommission_report();

    if (errors == 0) {
        log_event("Sichere Außerbetriebnahme abgeschlossen");
        return 0;
    } else {
        log_error("Außerbetriebnahme unvollständig: %d Fehler", errors);
        return -1;
    }
}

// SICHER: Werksreset mit Anmeldedaten-Loschung
void secure_factory_reset(void) {
    // Zuerst alle sensiblen Daten loschen
    secure_decommission();

    // Dann Konfiguration zurücksetzen
    config_flags = DEFAULT_FLAGS;

    // Loschung vor Neustart verifizieren
    if (!verify_all_scrubbed()) {
        // Sicher fehlschlagen - Gerat nicht weiterlaufen lassen
        enter_lockout_mode();
    }

    reboot_device();
}
// SICHER: Hardware mit umfassender sicherer Loschung

module secure_storage_with_scrub (
    input wire clk,
    input wire reset_n,
    input wire [7:0] addr,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    // SICHER: Sichere Losch-Schnittstelle
    input wire scrub_all_command,
    input wire scrub_verify_command,
    output reg [31:0] read_data,
    output reg scrub_complete,
    output reg scrub_verified,
    output reg scrub_error
);

    // Speicher-Arrays
    reg [31:0] config_storage [0:63];
    reg [31:0] key_storage [0:31];
    reg [31:0] log_storage [0:127];

    // SICHER: Losch-Zustandsmaschine
    reg [2:0] scrub_state;
    reg [7:0] scrub_index;
    reg [1:0] scrub_pass;

    parameter IDLE = 3'd0;
    parameter SCRUB_CONFIG = 3'd1;
    parameter SCRUB_KEYS = 3'd2;
    parameter SCRUB_LOGS = 3'd3;
    parameter VERIFY = 3'd4;
    parameter DONE = 3'd5;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            scrub_state <= IDLE;
            scrub_complete <= 1'b0;
            scrub_verified <= 1'b0;
            scrub_error <= 1'b0;
            scrub_index <= 8'h0;
            scrub_pass <= 2'd0;
        end
        else begin
            case (scrub_state)
                IDLE: begin
                    scrub_complete <= 1'b0;
                    scrub_verified <= 1'b0;
                    scrub_error <= 1'b0;

                    if (scrub_all_command) begin
                        scrub_state <= SCRUB_CONFIG;
                        scrub_index <= 8'h0;
                        scrub_pass <= 2'd0;
                    end
                end

                SCRUB_CONFIG: begin
                    // SICHER: GESAMTEN Konfigurationsspeicher loschen
                    if (scrub_index < 64) begin
                        // Mehrfach-Überschreibung
                        case (scrub_pass)
                            2'd0: config_storage[scrub_index] <= 32'h00000000;
                            2'd1: config_storage[scrub_index] <= 32'hFFFFFFFF;
                            2'd2: config_storage[scrub_index] <= 32'h00000000;
                        endcase

                        if (scrub_pass == 2'd2) begin
                            scrub_index <= scrub_index + 1;
                            scrub_pass <= 2'd0;
                        end
                        else begin
                            scrub_pass <= scrub_pass + 1;
                        end
                    end
                    else begin
                        scrub_index <= 8'h0;
                        scrub_pass <= 2'd0;
                        scrub_state <= SCRUB_KEYS;
                    end
                end

                SCRUB_KEYS: begin
                    // SICHER: GESAMTEN Schlüsselspeicher loschen
                    if (scrub_index < 32) begin
                        case (scrub_pass)
                            2'd0: key_storage[scrub_index] <= 32'h00000000;
                            2'd1: key_storage[scrub_index] <= 32'hFFFFFFFF;
                            2'd2: key_storage[scrub_index] <= 32'h00000000;
                        endcase

                        if (scrub_pass == 2'd2) begin
                            scrub_index <= scrub_index + 1;
                            scrub_pass <= 2'd0;
                        end
                        else begin
                            scrub_pass <= scrub_pass + 1;
                        end
                    end
                    else begin
                        scrub_index <= 8'h0;
                        scrub_pass <= 2'd0;
                        scrub_state <= SCRUB_LOGS;
                    end
                end

                SCRUB_LOGS: begin
                    // SICHER: GESAMTEN Protokollspeicher loschen
                    if (scrub_index < 128) begin
                        case (scrub_pass)
                            2'd0: log_storage[scrub_index] <= 32'h00000000;
                            2'd1: log_storage[scrub_index] <= 32'hFFFFFFFF;
                            2'd2: log_storage[scrub_index] <= 32'h00000000;
                        endcase

                        if (scrub_pass == 2'd2) begin
                            scrub_index <= scrub_index + 1;
                            scrub_pass <= 2'd0;
                        end
                        else begin
                            scrub_pass <= scrub_pass + 1;
                        end
                    end
                    else begin
                        scrub_index <= 8'h0;
                        scrub_state <= VERIFY;
                    end
                end

                VERIFY: begin
                    // SICHER: Verifizieren dass gesamter Speicher genullt ist
                    if (scrub_index < 64 && config_storage[scrub_index] != 32'h0) begin
                        scrub_error <= 1'b1;
                    end
                    else if (scrub_index < 32 && key_storage[scrub_index] != 32'h0) begin
                        scrub_error <= 1'b1;
                    end
                    else if (scrub_index < 128 && log_storage[scrub_index] != 32'h0) begin
                        scrub_error <= 1'b1;
                    end

                    scrub_index <= scrub_index + 1;
                    if (scrub_index >= 128) begin
                        scrub_state <= DONE;
                    end
                end

                DONE: begin
                    scrub_complete <= 1'b1;
                    scrub_verified <= !scrub_error;
                    scrub_state <= IDLE;
                end
            endcase
        end
    end

endmodule

CVE-Beispiele

Datenwiederherstellung von ausgemusterten Geraten wurde in zahlreichen Fallen nachgewiesen, darunter:

  • Medizinische Gerate mit Patientendaten
  • Netzwerkausrustung mit Anmeldedaten
  • Unternehmensspeicher mit vertraulichen Daten
  • Kassenterminals mit Zahlungsinformationen

Verwandte CWEs

  • CWE-404: Unsachgemäßes Herunterfahren oder Freigeben von Ressourcen (ubergeordnet)
  • CWE-226: Sensible Informationen in Ressource nicht vor Wiederverwendung entfernt (verwandt)
  • CWE-459: Unvollständige Bereinigung (verwandt)
  • CAPEC-150: Daten von allgemeinen Ressourcenstandorten sammeln (Angriffsmuster)
  • CAPEC-675: Daten von ausgemusterten Geraten abrufen (Angriffsmuster)

Referenzen

  1. MITRE Corporation. "CWE-1266: Improper Scrubbing of Sensitive Data from Decommissioned Device." https://cwe.mitre.org/data/definitions/1266.html
  2. NIST SP 800-88. "Guidelines for Media Sanitization"
  3. FIPS 140-3. "Zeroization Requirements"