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
| Impact | Details |
|---|---|
| Confidentiality | Scope: 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.
Related CWEs
- 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
- MITRE Corporation. "CWE-1301: Insufficient or Incomplete Data Removal within Hardware Component." https://cwe.mitre.org/data/definitions/1301.html
- NIST SP 800-88: Guidelines for Media Sanitization
- IEEE. "Data Remanence in Semiconductor Devices"