Insufficient or Incomplete Data Removal within Hardware Component

Description

Insufficient or Incomplete Data Removal within Hardware Component occurs when a product's data removal process fails to completely eliminate all sensitive information stored within hardware components. Physical properties of hardware devices—such as magnetic media remanence, residual ROM/RAM charge, or screen burn-in—may retain sensitive data after erasure and power removal. This weakness stems from data remanence, a phenomenon where repeated writes to memory locations physically alter cells such that even after the original data is erased, that data can still be recovered through physical characterization of those cells.

Risk

Incomplete data removal has severe security implications. Cryptographic keys recoverable after erasure. User data persists on "erased" devices. Factory reset fails to clear secrets. Previous owner data accessible. Memory remanence enables cold boot attacks. Screen burn-in reveals sensitive patterns. Physical analysis recovers "deleted" data. Particularly severe for devices that change ownership.

Solution

Apply blinding or masking techniques to reduce memory wear patterns during architecture and design phase. Modify erasure methods during implementation—use multiple overwrite passes, add media protection, or physically destroy storage media to prevent data recovery. Implement proper secure erase sequences for different memory types. Verify erasure effectiveness through testing. Consider tamper-evident and tamper-resistant enclosures.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Sensitive information including keys and user data recoverable after erasure.

Example Code

Vulnerable Code

// Vulnerable: Insufficient data erasure

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

// VULNERABLE: Simple memset for sensitive data
void vulnerable_erase_key(uint8_t* key, size_t len) {
    // VULNERABLE: Single pass overwrite
    memset(key, 0, len);

    // Problems:
    // 1. Compiler may optimize away "dead" memset
    // 2. Single pass doesn't eliminate remanence
    // 3. Memory cells retain physical traces
}

// VULNERABLE: Factory reset doesn't clear all data
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) {
    // VULNERABLE: Only clears some fields
    memset(storage->user_pin, 0, sizeof(storage->user_pin));
    memset(&storage->user_settings, 0, sizeof(storage->user_settings));

    // VULNERABLE: Leaves wifi password and encryption key!
    // Previous owner's credentials remain accessible

    // Reset to defaults
    storage->firmware_config[0] = 0x01;  // Reset flag
}

// VULNERABLE: Memory not cleared before power-off
void vulnerable_shutdown(void) {
    // VULNERABLE: Just powers off without clearing RAM
    // Sensitive data remains in DRAM for minutes after power-off
    // Cold boot attack can recover encryption keys

    power_off();
}

// VULNERABLE: File deletion doesn't clear data
void vulnerable_delete_file(const char* filename) {
    // VULNERABLE: Only removes directory entry
    // Actual data blocks remain on storage
    remove(filename);

    // File content still recoverable via:
    // - Direct sector reads
    // - Data carving tools
    // - Physical media analysis
}
// Vulnerable: Hardware without proper 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
            // VULNERABLE: Single-pass zero fill
            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

    // VULNERABLE: Single pass doesn't eliminate remanence
    // Physical analysis can still recover original data

endmodule

// Vulnerable: Key storage without 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;  // VULNERABLE: Initial clear only
            key_valid <= 1'b0;
        end else if (store_key) begin
            stored_key <= key_in;
            key_valid <= 1'b1;
        end else if (clear_key) begin
            // VULNERABLE: Single write of zeros
            stored_key <= 256'b0;
            key_valid <= 1'b0;
            // Remanence may retain key patterns
        end
    end

    assign key_out = stored_key;

endmodule

Fixed Code

// Fixed: Proper secure data erasure

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

// FIXED: Secure memory clear that compiler won't optimize away
void secure_memzero(volatile void* ptr, size_t len) {
    volatile uint8_t* p = (volatile uint8_t*)ptr;

    // FIXED: volatile prevents optimization
    while (len--) {
        *p++ = 0;
    }

    // FIXED: Memory barrier to ensure writes complete
    __asm__ __volatile__("" ::: "memory");
}

// FIXED: Multi-pass overwrite for sensitive data
void secure_erase_key(uint8_t* key, size_t len) {
    // FIXED: Multiple overwrite patterns
    // Pattern 1: All zeros
    for (size_t i = 0; i < len; i++) {
        ((volatile uint8_t*)key)[i] = 0x00;
    }

    // Pattern 2: All ones
    for (size_t i = 0; i < len; i++) {
        ((volatile uint8_t*)key)[i] = 0xFF;
    }

    // Pattern 3: Random data
    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];
    }

    // Pattern 4: Final zeros
    for (size_t i = 0; i < len; i++) {
        ((volatile uint8_t*)key)[i] = 0x00;
    }

    // FIXED: Clear the random buffer too
    secure_memzero(random_bytes, sizeof(random_bytes));
}

// FIXED: Complete factory reset
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) {
    // FIXED: Erase ALL sensitive fields

    // Wifi password - 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));

    // Encryption key - most critical
    secure_erase_key(storage->encryption_key, sizeof(storage->encryption_key));

    // User settings
    storage->user_settings = 0;

    // FIXED: Clear entire structure with multiple passes
    secure_memzero(storage, sizeof(device_storage_t));

    // FIXED: Write known-good defaults
    init_default_config(storage);

    // FIXED: Verify erasure
    if (!verify_erasure_complete(storage)) {
        // Erasure failed - enter secure failure mode
        enter_secure_lockdown();
    }
}

// FIXED: Clear RAM before shutdown
void secure_shutdown(void) {
    // FIXED: Clear all sensitive memory regions
    clear_key_storage();
    clear_session_data();
    clear_crypto_context();

    // FIXED: Scrub entire RAM if possible
    scrub_ram_region(RAM_START, RAM_SIZE);

    // FIXED: Ensure scrub completes before power-off
    sync_memory();

    power_off();
}

// FIXED: Secure file deletion
void secure_delete_file(const char* filename) {
    FILE* f = fopen(filename, "r+b");
    if (f == NULL) return;

    // FIXED: Get file size
    fseek(f, 0, SEEK_END);
    long size = ftell(f);
    fseek(f, 0, SEEK_SET);

    // FIXED: Overwrite file contents with multiple patterns
    uint8_t buffer[4096];

    // Pass 1: Zeros
    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));

    // Pass 2: Ones
    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));

    // Pass 3: Random
    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);

    // FIXED: Now remove directory entry
    remove(filename);
}
// Fixed: Hardware with proper 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=quick, 1=standard, 2=secure
    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;

    // FIXED: LFSR for pseudo-random pattern
    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
            // FIXED: Multi-pass erase based on security level
            case (pass_counter)
                3'd0: begin
                    // Pass 1: All zeros
                    memory[erase_addr] <= 32'h0000_0000;
                end

                3'd1: begin
                    // Pass 2: All ones
                    memory[erase_addr] <= 32'hFFFF_FFFF;
                end

                3'd2: begin
                    // Pass 3: Alternating pattern
                    memory[erase_addr] <= 32'hAAAA_AAAA;
                end

                3'd3: begin
                    // Pass 4: Inverse alternating
                    memory[erase_addr] <= 32'h5555_5555;
                end

                3'd4: begin
                    // Pass 5: Random (secure level only)
                    lfsr <= {lfsr[30:0], lfsr[31] ^ lfsr[21] ^ lfsr[1] ^ lfsr[0]};
                    memory[erase_addr] <= lfsr;
                end

                3'd5: begin
                    // Pass 6: Final zeros
                    memory[erase_addr] <= 32'h0000_0000;
                end
            endcase

            if (erase_addr == erase_end) begin
                // Completed one pass
                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
                    // FIXED: Enter verification phase
                    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
            // FIXED: Verify all locations are zero
            if (memory[erase_addr] != 32'h0000_0000) begin
                // Verification failed - restart erase
                verifying <= 1'b0;
                erasing <= 1'b1;
                erase_addr <= erase_start;
                pass_counter <= 3'b0;
            end else if (erase_addr == erase_end) begin
                // FIXED: Verification complete
                erase_done <= 1'b1;
                erase_verified <= 1'b1;
                verifying <= 1'b0;
            end else begin
                erase_addr <= erase_addr + 1;
            end
        end
    end

endmodule

// Fixed: Key storage with 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;

    // FIXED: Multi-pass key erasure
    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;  // Final zeros
                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 Examples

  • CVE-2019-8575: Factory reset failed to delete all user data, allowing recovery of wireless network credentials.
  • CVE-2020-8911: SSD firmware didn't properly implement secure erase, leaving encrypted data recoverable.

  • CWE-226: Sensitive Information in Resource Not Removed Before Reuse (parent)
  • CWE-1330: Remanent Data Readable after Memory Erase (child)
  • CWE-1208: Cross-Cutting Problems (category)
  • CWE-212: Improper Removal of Sensitive Information Before Storage or Transfer (related)

References

  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"