Exposure of Sensitive System Information Due to Uncleared Debug Information
Description
Exposure of Sensitive System Information Due to Uncleared Debug Information occurs when hardware fails to fully clear security-sensitive values—such as cryptographic keys and intermediate values—when debug mode is activated. Sensitive data stored in temporary hardware registers becomes accessible to debuggers if not cleared upon entering debug mode, potentially exposing cryptographic secrets to untrusted parties who gain debug access.
Risk
Uncleared debug information has severe security implications. Cryptographic keys may be exposed to debuggers. Intermediate computation values may reveal secrets. Authentication tokens may be readable. Secure boot keys may be compromised. Session keys may be extracted. Private keys may be leaked. Hardware security modules may be bypassed. Trust anchors may be compromised through debug access.
Solution
Whenever debug mode is enabled, all registers containing sensitive assets must be cleared. Implement hardware-enforced clearing of sensitive registers on debug entry. Clear all key storage, intermediate values, and security state. Zero-fill sensitive memory regions before debug access is granted. Verify clearing completeness before allowing debug operations. Consider preventing debug access entirely when sensitive data is present.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Memory - Sensitive data including cryptographic keys can be read through debug interface. |
| Access Control | Scope: Access Control Bypass Protection Mechanism - Debug access to keys enables bypassing of encryption and authentication. |
Example Code
Vulnerable Code
// Vulnerable: Incomplete key clearing on debug mode
module vulnerable_aes_wrapper (
input wire clk,
input wire reset_n,
input wire debug_mode,
input wire [127:0] key0_in,
input wire [127:0] key1_in,
input wire key_load,
input wire [127:0] plaintext,
input wire encrypt_start,
output wire [127:0] ciphertext,
output wire done
);
// Internal key storage
reg [127:0] core_key0;
reg [127:0] core_key1;
// Intermediate values
reg [127:0] round_key;
reg [127:0] state;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
core_key0 <= 128'h0;
core_key1 <= 128'h0;
round_key <= 128'h0;
state <= 128'h0;
end
else if (debug_mode) begin
// VULNERABLE: Only clears one key, forgets the other!
core_key0 <= 128'h0;
// core_key1 NOT cleared - exposed to debugger!
// round_key NOT cleared - intermediate values exposed!
// state NOT cleared - encryption state exposed!
end
else if (key_load) begin
core_key0 <= key0_in;
core_key1 <= key1_in;
end
end
// AES core instantiation
aes_core aes_inst (
.clk(clk),
.key0(core_key0),
.key1(core_key1), // VULNERABLE: Accessible in debug mode
.plaintext(plaintext),
.ciphertext(ciphertext),
.done(done)
);
endmodule
// Vulnerable: Debug register with key exposure
module vulnerable_debug_interface (
input wire clk,
input wire reset_n,
input wire debug_enable,
input wire [7:0] debug_addr,
input wire debug_read,
output reg [31:0] debug_data
);
// Sensitive registers that should not be readable
reg [127:0] secret_key;
reg [127:0] session_key;
reg [31:0] auth_token;
// VULNERABLE: Debug can read all registers
always @(posedge clk) begin
if (debug_enable && debug_read) begin
case (debug_addr)
8'h00: debug_data <= secret_key[31:0]; // VULNERABLE!
8'h04: debug_data <= secret_key[63:32]; // VULNERABLE!
8'h08: debug_data <= secret_key[95:64]; // VULNERABLE!
8'h0C: debug_data <= secret_key[127:96]; // VULNERABLE!
8'h10: debug_data <= session_key[31:0]; // VULNERABLE!
8'h20: debug_data <= auth_token; // VULNERABLE!
default: debug_data <= 32'h0;
endcase
end
end
endmodule
// Vulnerable: Software debug handler without clearing
#include <stdint.h>
typedef struct {
uint8_t aes_key[32];
uint8_t hmac_key[32];
uint8_t session_key[32];
uint8_t intermediate_state[64];
} crypto_context_t;
volatile crypto_context_t* crypto_ctx = (volatile crypto_context_t*)0x40000000;
void vulnerable_enter_debug_mode(void) {
// VULNERABLE: Enters debug mode without clearing sensitive data
// Enable debug access
DEBUG_CTRL |= DEBUG_ENABLE;
// VULNERABLE: Keys and state remain in memory
// Debugger can now read crypto_ctx->aes_key, etc.
// Signal debug ready
DEBUG_STATUS = DEBUG_READY;
}
void vulnerable_debug_read(uint32_t addr, uint32_t* value) {
// VULNERABLE: No filtering of sensitive addresses
*value = *(volatile uint32_t*)addr;
// Debugger can read any memory including keys
}
Fixed Code
// Fixed: Complete key clearing on debug mode
module secure_aes_wrapper (
input wire clk,
input wire reset_n,
input wire debug_mode,
input wire [127:0] key0_in,
input wire [127:0] key1_in,
input wire key_load,
input wire [127:0] plaintext,
input wire encrypt_start,
output wire [127:0] ciphertext,
output wire done,
output reg debug_ready // Indicates safe to debug
);
// Internal key storage
reg [127:0] core_key0;
reg [127:0] core_key1;
// Intermediate values
reg [127:0] round_key;
reg [127:0] state;
// Additional sensitive state
reg [127:0] key_schedule [0:10];
reg [127:0] temp_buffer;
// FIXED: Track clearing completion
reg clearing_in_progress;
reg [3:0] clear_counter;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
core_key0 <= 128'h0;
core_key1 <= 128'h0;
round_key <= 128'h0;
state <= 128'h0;
temp_buffer <= 128'h0;
clearing_in_progress <= 1'b0;
clear_counter <= 4'h0;
debug_ready <= 1'b0;
end
else if (debug_mode) begin
// FIXED: Clear ALL sensitive registers
core_key0 <= 128'h0;
core_key1 <= 128'h0;
round_key <= 128'h0;
state <= 128'h0;
temp_buffer <= 128'h0;
// FIXED: Clear key schedule array
if (clear_counter < 4'd11) begin
key_schedule[clear_counter] <= 128'h0;
clear_counter <= clear_counter + 1;
clearing_in_progress <= 1'b1;
debug_ready <= 1'b0;
end
else begin
// FIXED: All cleared, safe for debug
clearing_in_progress <= 1'b0;
debug_ready <= 1'b1;
end
end
else begin
clear_counter <= 4'h0;
debug_ready <= 1'b0;
if (key_load) begin
core_key0 <= key0_in;
core_key1 <= key1_in;
end
end
end
// FIXED: Gate debug access until clearing complete
wire debug_access_allowed = debug_mode && debug_ready && !clearing_in_progress;
// AES core with protected keys
aes_core aes_inst (
.clk(clk),
.key0(debug_mode ? 128'h0 : core_key0), // FIXED: Zero during debug
.key1(debug_mode ? 128'h0 : core_key1), // FIXED: Zero during debug
.plaintext(plaintext),
.ciphertext(ciphertext),
.done(done)
);
endmodule
// Fixed: Debug interface with protected registers
module secure_debug_interface (
input wire clk,
input wire reset_n,
input wire debug_enable,
input wire [7:0] debug_addr,
input wire debug_read,
output reg [31:0] debug_data,
output reg access_denied
);
// Sensitive registers
reg [127:0] secret_key;
reg [127:0] session_key;
reg [31:0] auth_token;
// FIXED: Non-sensitive debug registers
reg [31:0] debug_status;
reg [31:0] debug_version;
reg [31:0] debug_counter;
// FIXED: Sensitive address range
wire is_sensitive_addr = (debug_addr >= 8'h00) && (debug_addr < 8'h30);
// FIXED: Clearing state
reg sensitive_data_present;
reg clearing_complete;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
secret_key <= 128'h0;
session_key <= 128'h0;
auth_token <= 32'h0;
sensitive_data_present <= 1'b0;
clearing_complete <= 1'b1;
debug_data <= 32'h0;
access_denied <= 1'b0;
end
else if (debug_enable) begin
// FIXED: Clear all sensitive data when debug enabled
if (!clearing_complete) begin
secret_key <= 128'h0;
session_key <= 128'h0;
auth_token <= 32'h0;
clearing_complete <= 1'b1;
sensitive_data_present <= 1'b0;
end
if (debug_read) begin
// FIXED: Block access to sensitive addresses
if (is_sensitive_addr) begin
debug_data <= 32'hDEADBEEF; // Return dummy value
access_denied <= 1'b1;
end
else begin
// Allow access to non-sensitive debug registers
case (debug_addr)
8'h80: debug_data <= debug_status;
8'h84: debug_data <= debug_version;
8'h88: debug_data <= debug_counter;
default: debug_data <= 32'h0;
endcase
access_denied <= 1'b0;
end
end
end
else begin
clearing_complete <= 1'b0; // Reset for next debug session
access_denied <= 1'b0;
end
end
endmodule
// Fixed: Software debug handler with proper clearing
#include <stdint.h>
#include <string.h>
typedef struct {
uint8_t aes_key[32];
uint8_t hmac_key[32];
uint8_t session_key[32];
uint8_t intermediate_state[64];
} crypto_context_t;
volatile crypto_context_t* crypto_ctx = (volatile crypto_context_t*)0x40000000;
// FIXED: Secure memory clearing that cannot be optimized away
static void secure_zero(volatile void* ptr, size_t size) {
volatile uint8_t* p = (volatile uint8_t*)ptr;
while (size--) {
*p++ = 0;
}
// Memory barrier to ensure clearing completes
__asm__ volatile("" ::: "memory");
}
void secure_enter_debug_mode(void) {
// FIXED: Clear all sensitive data before enabling debug
// Clear cryptographic keys
secure_zero(crypto_ctx->aes_key, sizeof(crypto_ctx->aes_key));
secure_zero(crypto_ctx->hmac_key, sizeof(crypto_ctx->hmac_key));
secure_zero(crypto_ctx->session_key, sizeof(crypto_ctx->session_key));
// Clear intermediate state
secure_zero(crypto_ctx->intermediate_state, sizeof(crypto_ctx->intermediate_state));
// FIXED: Clear any other sensitive memory regions
clear_key_cache();
clear_pending_operations();
clear_temporary_buffers();
// Memory barrier before enabling debug
__sync_synchronize();
// Verify clearing completed
if (!verify_sensitive_memory_cleared()) {
// Clearing failed - do not enable debug
DEBUG_STATUS = DEBUG_ERROR;
return;
}
// FIXED: Now safe to enable debug access
DEBUG_CTRL |= DEBUG_ENABLE;
DEBUG_STATUS = DEBUG_READY;
}
// FIXED: Filtered debug read with address validation
int secure_debug_read(uint32_t addr, uint32_t* value) {
// Define protected address ranges
static const struct {
uint32_t start;
uint32_t end;
} protected_ranges[] = {
{0x40000000, 0x40000100}, // Crypto context
{0x40001000, 0x40001100}, // Key storage
{0x40002000, 0x40002100}, // Session data
};
// FIXED: Check if address is in protected range
for (int i = 0; i < sizeof(protected_ranges)/sizeof(protected_ranges[0]); i++) {
if (addr >= protected_ranges[i].start && addr < protected_ranges[i].end) {
// Return zero instead of actual value
*value = 0;
log_debug_blocked_access(addr);
return -EACCES;
}
}
// Address is not protected, allow read
*value = *(volatile uint32_t*)addr;
return 0;
}
// FIXED: Verification that sensitive memory is cleared
static bool verify_sensitive_memory_cleared(void) {
volatile uint8_t* p = (volatile uint8_t*)crypto_ctx;
size_t size = sizeof(crypto_context_t);
for (size_t i = 0; i < size; i++) {
if (p[i] != 0) {
return false;
}
}
return true;
}
CVE Examples
- CVE-2021-33080: Uncleared debug information in SSD memory accelerator exposed sensitive data
- CVE-2022-31162: Rust library leaks OAuth details in debug logs
Related CWEs
- CWE-212: Improper Removal of Sensitive Information Before Storage or Transfer (parent)
- CWE-1272: Sensitive Information Uncleared Before Debug/Power State Transition (related)
- CWE-1244: Internal Asset Exposed to Unsafe Debug Access Level or State (related)
References
- MITRE Corporation. "CWE-1258: Exposure of Sensitive System Information Due to Uncleared Debug Information." https://cwe.mitre.org/data/definitions/1258.html
- NIST. "Guidelines for Hardware Security"
- Common Criteria. "Protection Profile for Hardware Security Modules"