Improper Scrubbing of Sensitive Data from Decommissioned Device
Description
Improper Scrubbing of Sensitive Data from Decommissioned Device occurs when a product lacks adequate capability for administrators to remove sensitive data when equipment is taken out of service. This absence, inadequacy, or incorrectness of scrubbing functionality creates vulnerability exposure. Decommissioning best practices and regulatory compliance often mandate data removal before equipment retirement. When sensitive information isn't properly erased, malicious actors can retrieve it from disposed or recycled equipment.
Risk
Inadequate data scrubbing has severe security implications. Authentication credentials may be recovered. Network configurations may be exposed. Cryptographic keys may be extracted. Proprietary information may leak. Customer data may be compromised. Intellectual property may be stolen. Compliance violations may occur. Reputational damage may result from data breaches.
Solution
Incorporate comprehensive data scrubbing functionality during initial design rather than retrofitting later. Document storage locations, removal policies, and procedures in administrative guides or volatility statements. Provide utility tools for erasing sensitive data from non-accessible storage areas like EEPROMs. Implement cryptographic erasure where appropriate. Verify scrubbing effectiveness through testing.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Memory - Adversaries gain unauthorized access to stored sensitive information from decommissioned devices. |
Example Code
Vulnerable Code
// Vulnerable: Device without data scrubbing capability
#include <stdint.h>
#define EEPROM_BASE 0x50000000
#define EEPROM_SIZE 4096
// Sensitive data storage
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;
// VULNERABLE: No scrubbing function provided
// Device stores credentials permanently
void vulnerable_store_credentials(device_credentials_t* creds) {
// Write credentials to EEPROM
volatile uint8_t* eeprom = (volatile uint8_t*)EEPROM_BASE;
memcpy((void*)eeprom, creds, sizeof(device_credentials_t));
}
void vulnerable_factory_reset(void) {
// VULNERABLE: Only resets configuration flags
// Does NOT erase credentials from EEPROM
config_flags = DEFAULT_FLAGS;
reboot_device();
// Credentials remain in EEPROM!
// When device is decommissioned, attacker can:
// 1. Remove EEPROM chip
// 2. Read it with programmer
// 3. Extract all credentials
}
// VULNERABLE: No data destruction on decommission
void vulnerable_shutdown(void) {
// Normal shutdown - data persists
save_state_to_flash();
power_down();
}
// Vulnerable: Hardware without secure erase capability
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
);
// Non-volatile storage (NVM)
reg [31:0] nvm_storage [0:255];
// VULNERABLE: No secure erase command
// Data persists indefinitely
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
// VULNERABLE: Reset does not clear NVM
// Only clears volatile state
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
// No command to securely erase all storage
// No verification that data is actually erased
// No zeroization on tamper or decommission
endmodule
// Vulnerable: Device with incomplete scrubbing
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]; // Gets scrubbed
reg [31:0] key_storage [0:31]; // VULNERABLE: NOT scrubbed
reg [31:0] log_storage [0:127]; // VULNERABLE: NOT scrubbed
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
scrub_complete <= 1'b0;
end
else if (scrub_command) begin
// VULNERABLE: Only scrubs config, misses keys and logs
config_storage[scrub_addr] <= 32'h0;
// key_storage and log_storage are NOT cleared!
// Attacker can still recover:
// - Encryption keys
// - Access logs with user information
// - Network credentials stored in logs
scrub_complete <= 1'b1;
end
end
endmodule
Fixed Code
// Fixed: Device with comprehensive data scrubbing
#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;
// Documented storage locations for decommissioning
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[] = {
{"Credentials", 0x0000, sizeof(device_credentials_t), true},
{"Encryption Keys", 0x1000, 256, true},
{"Logs", 0x2000, 1024, true},
{"Configuration", 0x3000, 512, false},
{NULL, 0, 0, false}
};
// FIXED: Secure erase with verification
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;
}
// Memory barrier
__asm__ volatile("" ::: "memory");
}
// FIXED: Multi-pass overwrite for secure erasure
static bool secure_erase_region(uint32_t address, uint32_t size) {
volatile uint8_t* ptr = (volatile uint8_t*)(EEPROM_BASE + address);
// Pattern 1: All zeros
secure_memset(ptr, 0x00, size);
if (!verify_pattern(ptr, 0x00, size)) return false;
// Pattern 2: All ones
secure_memset(ptr, 0xFF, size);
if (!verify_pattern(ptr, 0xFF, size)) return false;
// Pattern 3: Random data
uint8_t* random_data = get_random_bytes(size);
memcpy((void*)ptr, random_data, size);
free(random_data);
// Pattern 4: Final zeros
secure_memset(ptr, 0x00, size);
if (!verify_pattern(ptr, 0x00, size)) return false;
return true;
}
// FIXED: Verify erasure
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;
}
// FIXED: Comprehensive scrubbing function
int secure_decommission(void) {
int errors = 0;
log_event("Starting secure decommission");
// FIXED: Scrub all documented storage locations
for (int i = 0; storage_map[i].name != NULL; i++) {
log_event("Scrubbing: %s", storage_map[i].name);
if (!secure_erase_region(storage_map[i].address, storage_map[i].size)) {
log_error("Failed to scrub: %s", storage_map[i].name);
errors++;
}
}
// FIXED: Clear RAM containing sensitive data
clear_sensitive_ram();
// FIXED: Invalidate encryption keys in hardware
invalidate_hw_keys();
// FIXED: Clear any cached credentials
clear_credential_cache();
// FIXED: Generate report
generate_decommission_report();
if (errors == 0) {
log_event("Secure decommission complete");
return 0;
} else {
log_error("Decommission incomplete: %d errors", errors);
return -1;
}
}
// FIXED: Factory reset that includes credential erasure
void secure_factory_reset(void) {
// First, scrub all sensitive data
secure_decommission();
// Then reset configuration
config_flags = DEFAULT_FLAGS;
// Verify scrubbing before reboot
if (!verify_all_scrubbed()) {
// Fail secure - don't allow device to continue
enter_lockout_mode();
}
reboot_device();
}
// Fixed: Hardware with comprehensive secure erase
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,
// FIXED: Secure erase interface
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
);
// Storage arrays
reg [31:0] config_storage [0:63];
reg [31:0] key_storage [0:31];
reg [31:0] log_storage [0:127];
// FIXED: Scrubbing state machine
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
// FIXED: Scrub ALL config storage
if (scrub_index < 64) begin
// Multi-pass overwrite
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
// FIXED: Scrub ALL key storage
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
// FIXED: Scrub ALL log storage
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
// FIXED: Verify all storage is zeroed
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 Examples
Data recovery from decommissioned devices has been demonstrated in numerous cases including:
- Medical devices with patient data
- Network equipment with credentials
- Enterprise storage with confidential data
- Point-of-sale terminals with payment information
Related CWEs
- CWE-404: Improper Resource Shutdown or Release (parent)
- CWE-226: Sensitive Information in Resource Not Removed Before Reuse (related)
- CWE-459: Incomplete Cleanup (related)
- CAPEC-150: Collect Data from Common Resource Locations (attack pattern)
- CAPEC-675: Retrieve Data from Decommissioned Devices (attack pattern)
References
- MITRE Corporation. "CWE-1266: Improper Scrubbing of Sensitive Data from Decommissioned Device." https://cwe.mitre.org/data/definitions/1266.html
- NIST SP 800-88. "Guidelines for Media Sanitization"
- FIPS 140-3. "Zeroization Requirements"