Internal Asset Exposed to Unsafe Debug Access Level or State
Description
Internal Asset Exposed to Unsafe Debug Access Level or State occurs when a product uses physical debug or test interfaces with multiple access levels but assigns incorrect debug access to internal assets, providing unintended access from untrusted debug agents. Debug authorization supports multiple access levels controlling which system assets are accessible based on authenticated privilege. Authorization can depend on system state or boot stage. For instance, full system debug access might only be allowed early in boot after a system reset to ensure that previous session data is not accessible.
Risk
Incorrect debug access levels have severe security implications. Sensitive assets may be accessible at wrong privilege levels. Debug access during wrong boot stages may expose secrets. Attackers may modify boot flow through debug. Encryption keys may be accessible before lockdown. Memory protection may be bypassable through debug. Previous session data may be accessible. Privilege escalation may be possible through debug.
Solution
Ensure debug access levels correctly protect all internal assets. Restrict full debug access to appropriate boot stages. Default debug protection registers to secured state. Use AND logic rather than OR for combining access checks. Verify debug protection across all system states. Test debug access at each boot stage. Document debug access requirements for each asset. Implement proper debug authentication.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Memory - Attackers could obtain sensitive information from internal assets using a debugger if protection mechanisms fail to enforce correct debug access levels. |
| Integrity | Scope: Integrity Modify Memory - Unauthorized memory alteration through improperly protected debug access. |
| Access Control | Scope: Access Control Bypass Protection Mechanism, Gain Privileges - Authorization and access control violations enabling unauthorized privilege assumption. |
Example Code
Vulnerable Code
// Vulnerable: JTAG access allowed before security initialization
module vulnerable_jtag_protection (
input wire clk,
input wire reset_n,
input wire jtag_request,
input wire [31:0] jtag_addr,
input wire jtag_write,
input wire [31:0] jtag_wdata,
input wire boot_complete,
input wire security_init_done,
output reg [31:0] jtag_rdata,
output reg jtag_ack
);
// JTAG shield register
reg jtag_shield_enabled;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
// VULNERABLE: JTAG protection disabled by default!
jtag_shield_enabled <= 1'b0; // Should be 1'b1
end
else if (boot_complete) begin
// Shield only enabled after boot completes
// Too late - attacker can access during boot
jtag_shield_enabled <= 1'b1;
end
end
// VULNERABLE: Full JTAG access before boot_complete
always @(posedge clk) begin
if (jtag_request) begin
if (!jtag_shield_enabled) begin
// JTAG has full access during early boot!
if (jtag_write) begin
memory[jtag_addr] <= jtag_wdata; // Can modify boot code!
end else begin
jtag_rdata <= memory[jtag_addr]; // Can read keys!
end
jtag_ack <= 1'b1;
end else begin
jtag_ack <= 1'b0; // Blocked after boot
end
end
end
// Attack window: From reset to boot_complete, full debug access
endmodule
// Vulnerable: Debug privilege check uses OR instead of AND
module vulnerable_debug_auth (
input wire clk,
input wire reset_n,
input wire debug_request,
input wire debug_mode_active,
input wire user_mode,
input wire password_correct,
output reg debug_allowed
);
// VULNERABLE: OR logic - either condition allows access
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
debug_allowed <= 1'b0;
end
else begin
// Bug: debug_mode_active OR user_mode grants access
// Should require BOTH conditions
debug_allowed <= debug_request && (debug_mode_active || password_correct);
end
end
// If debug_mode_active is set (e.g., by pulling a pin), no password needed
endmodule
// Vulnerable: Debug access level not matched to boot stage
module vulnerable_boot_debug (
input wire clk,
input wire reset_n,
input wire [2:0] boot_stage,
input wire debug_request,
input wire [2:0] debug_access_level,
input wire [31:0] debug_addr,
output reg [31:0] debug_data,
output reg debug_granted
);
// Boot stages
parameter STAGE_ROM = 3'h0;
parameter STAGE_BOOTLOADER = 3'h1;
parameter STAGE_KERNEL = 3'h2;
parameter STAGE_USER = 3'h3;
// Debug access levels
parameter ACCESS_NONE = 3'h0;
parameter ACCESS_USER = 3'h1;
parameter ACCESS_PRIVILEGED = 3'h2;
parameter ACCESS_FULL = 3'h3;
always @(posedge clk) begin
if (debug_request) begin
// VULNERABLE: Access level not properly restricted by boot stage
// Full access allowed even in later boot stages
if (debug_access_level == ACCESS_FULL) begin
debug_data <= memory[debug_addr];
debug_granted <= 1'b1;
end
else begin
debug_granted <= 1'b0;
end
end
end
// Should restrict full access to early boot only
endmodule
// Vulnerable: Debug access during sensitive boot operations
void vulnerable_secure_boot(void) {
// Load encryption keys
load_boot_keys(); // Keys now in memory
// VULNERABLE: Debug still enabled, keys accessible!
// Verify firmware signature
if (!verify_signature()) {
// Boot failed, but debug can still access keys
halt();
}
// Eventually disable debug...
disable_debug(); // Too late - keys may have been extracted
}
// Vulnerable: Debug privilege check
bool vulnerable_debug_check(uint32_t addr) {
// VULNERABLE: Always allow debug during boot
if (!boot_complete) {
return true; // Full access during boot
}
// After boot, check privilege
return is_privileged_debug();
}
Fixed Code
// Fixed: JTAG protection enabled by default
module secure_jtag_protection (
input wire clk,
input wire reset_n,
input wire jtag_request,
input wire [31:0] jtag_addr,
input wire jtag_write,
input wire [31:0] jtag_wdata,
input wire jtag_authenticated, // Requires authentication
input wire boot_complete,
input wire security_init_done,
output reg [31:0] jtag_rdata,
output reg jtag_ack,
output reg jtag_denied
);
// JTAG shield register
reg jtag_shield_enabled;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
// FIXED: JTAG protection ENABLED by default
jtag_shield_enabled <= 1'b1;
end
// Shield can only be disabled with authentication after security init
else if (security_init_done && jtag_authenticated) begin
// Allow authenticated debug after security is configured
jtag_shield_enabled <= 1'b0;
end
end
// FIXED: JTAG blocked until authenticated
always @(posedge clk) begin
jtag_denied <= 1'b0;
if (jtag_request) begin
if (jtag_shield_enabled) begin
// Debug blocked
jtag_rdata <= 32'h0;
jtag_ack <= 1'b0;
jtag_denied <= 1'b1;
end
else if (jtag_authenticated) begin
// Authenticated debug allowed
if (jtag_write) begin
memory[jtag_addr] <= jtag_wdata;
end else begin
jtag_rdata <= memory[jtag_addr];
end
jtag_ack <= 1'b1;
end
else begin
jtag_ack <= 1'b0;
jtag_denied <= 1'b1;
end
end
end
endmodule
// Fixed: Debug privilege check uses AND logic
module secure_debug_auth (
input wire clk,
input wire reset_n,
input wire debug_request,
input wire debug_mode_active,
input wire user_authorized,
input wire password_correct,
output reg debug_allowed
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
debug_allowed <= 1'b0;
end
else begin
// FIXED: AND logic - ALL conditions required
debug_allowed <= debug_request &&
debug_mode_active &&
user_authorized &&
password_correct;
end
end
endmodule
// Fixed: Debug access level matched to boot stage
module secure_boot_debug (
input wire clk,
input wire reset_n,
input wire [2:0] boot_stage,
input wire debug_request,
input wire [2:0] debug_access_level,
input wire debug_authenticated,
input wire [31:0] debug_addr,
output reg [31:0] debug_data,
output reg debug_granted,
output reg access_violation
);
// Boot stages
parameter STAGE_ROM = 3'h0;
parameter STAGE_BOOTLOADER = 3'h1;
parameter STAGE_KERNEL = 3'h2;
parameter STAGE_USER = 3'h3;
// Debug access levels
parameter ACCESS_NONE = 3'h0;
parameter ACCESS_USER = 3'h1;
parameter ACCESS_PRIVILEGED = 3'h2;
parameter ACCESS_FULL = 3'h3;
// Maximum allowed access level per boot stage
function [2:0] max_access_for_stage;
input [2:0] stage;
begin
case (stage)
STAGE_ROM: max_access_for_stage = ACCESS_NONE; // No debug during ROM
STAGE_BOOTLOADER: max_access_for_stage = ACCESS_PRIVILEGED; // Limited
STAGE_KERNEL: max_access_for_stage = ACCESS_PRIVILEGED;
STAGE_USER: max_access_for_stage = ACCESS_USER; // User-level only
default: max_access_for_stage = ACCESS_NONE;
endcase
end
endfunction
always @(posedge clk) begin
access_violation <= 1'b0;
if (debug_request && debug_authenticated) begin
// FIXED: Check access level is appropriate for boot stage
if (debug_access_level <= max_access_for_stage(boot_stage)) begin
debug_data <= memory[debug_addr];
debug_granted <= 1'b1;
end
else begin
// Access level too high for current boot stage
debug_data <= 32'h0;
debug_granted <= 1'b0;
access_violation <= 1'b1;
end
end
else begin
debug_granted <= 1'b0;
end
end
endmodule
// Fixed: Debug properly restricted during secure boot
void secure_boot_sequence(void) {
// FIXED: Disable debug BEFORE loading keys
disable_debug_access();
// Verify debug is disabled
if (is_debug_enabled()) {
panic("Debug not disabled during secure boot!");
}
// Now safe to load keys
load_boot_keys();
// Verify firmware signature
if (!verify_signature()) {
// Clear keys before halting
clear_boot_keys();
halt();
}
// Clear keys from memory after use
clear_boot_keys();
// Debug can be re-enabled after boot if needed
// (with proper authentication)
}
// Fixed: Debug privilege properly checked
bool secure_debug_check(uint32_t addr, uint32_t access_level) {
uint32_t current_stage = get_boot_stage();
uint32_t max_level = get_max_debug_level(current_stage);
// Check authentication
if (!is_debug_authenticated()) {
return false;
}
// Check access level appropriate for boot stage
if (access_level > max_level) {
log_security_event("Debug access level violation: %d > %d",
access_level, max_level);
return false;
}
// Check address is accessible at this level
if (!is_address_accessible(addr, access_level)) {
return false;
}
return true;
}
uint32_t get_max_debug_level(uint32_t boot_stage) {
switch (boot_stage) {
case STAGE_ROM:
return ACCESS_NONE; // No debug during ROM
case STAGE_BOOTLOADER:
return ACCESS_PRIVILEGED; // Limited access
case STAGE_KERNEL:
return ACCESS_PRIVILEGED;
case STAGE_USER:
return ACCESS_USER; // User-level only
default:
return ACCESS_NONE;
}
}
CVE Examples
- CVE-2019-18827: JTAG access possible before ROM code execution on Barco ClickShare
- CVA6 processor debug mode vulnerabilities
Related CWEs
- CWE-863: Incorrect Authorization (parent)
- CWE-1191: On-Chip Debug and Test Interface With Improper Access Control (related)
- CWE-1243: Sensitive Non-Volatile Information Not Protected During Debug (related)
References
- MITRE Corporation. "CWE-1244: Internal Asset Exposed to Unsafe Debug Access Level or State." https://cwe.mitre.org/data/definitions/1244.html
- REF-1377, REF-1378: CVA6 Processor Vulnerability Examples
- REF-1056: Barco ClickShare JTAG Vulnerabilities