Offenlegung sensibler Systeminformationen durch nicht gelöschte Debug-Informationen
Beschreibung
Offenlegung sensibler Systeminformationen durch nicht gelöschte Debug-Informationen tritt auf, wenn Hardware sicherheitsrelevante Werte -- wie kryptografische Schlüssel und Zwischenwerte -- beim Aktivieren des Debug-Modus nicht vollständig löscht. Sensible Daten, die in temporären Hardware-Registern gespeichert sind, werden für Debugger zugänglich, wenn sie beim Eintritt in den Debug-Modus nicht gelöscht werden, wodurch potenziell kryptografische Geheimnisse an nicht vertrauenswürdige Parteien offengelegt werden, die Debug-Zugriff erlangen.
Risiko
Nicht gelöschte Debug-Informationen haben schwerwiegende Sicherheitsauswirkungen. Kryptografische Schlüssel können für Debugger offengelegt werden. Zwischenwerte von Berechnungen können Geheimnisse offenbaren. Authentifizierungstoken können lesbar sein. Secure-Boot-Schlüssel können kompromittiert werden. Sitzungsschlüssel können extrahiert werden. Private Schlüssel können geleakt werden. Hardware-Sicherheitsmodule können umgangen werden. Vertrauensanker können durch Debug-Zugriff kompromittiert werden.
Lösung
Wann immer der Debug-Modus aktiviert wird, müssen alle Register, die sensible Daten enthalten, gelöscht werden. Implementieren Sie hardware-erzwungenes Löschen sensibler Register beim Debug-Eintritt. Löschen Sie alle Schlüsselspeicher, Zwischenwerte und Sicherheitszustande. Füllen Sie sensible Speicherbereiche mit Nullen, bevor Debug-Zugriff gewährt wird. Überprüfen Sie die Vollständigkeit des Löschens, bevor Debug-Operationen erlaubt werden. Erwägen Sie, Debug-Zugriff vollständig zu verhindern, wenn sensible Daten vorhanden sind.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Umfang: Vertraulichkeit Speicher lesen - Sensible Daten einschließlich kryptografischer Schlüssel können über Debug-Schnittstelle gelesen werden. |
| Zugriffskontrolle | Umfang: Zugriffskontrolle Schutzmechanismus umgehen - Debug-Zugriff auf Schlüssel ermöglicht die Umgehung von Verschlüsselung und Authentifizierung. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Unvollständige Schlüssellöschung bei Debug-Modus
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
);
// Interner Schlüsselspeicher
reg [127:0] core_key0;
reg [127:0] core_key1;
// Zwischenwerte
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
// VERWUNDBAR: Löscht nur einen Schlüssel, vergisst den anderen!
core_key0 <= 128'h0;
// core_key1 NICHT gelöscht - für Debugger offengelegt!
// round_key NICHT gelöscht - Zwischenwerte offengelegt!
// state NICHT gelöscht - Verschlüsselungszustand offengelegt!
end
else if (key_load) begin
core_key0 <= key0_in;
core_key1 <= key1_in;
end
end
// AES-Kern-Instanziierung
aes_core aes_inst (
.clk(clk),
.key0(core_key0),
.key1(core_key1), // VERWUNDBAR: Im Debug-Modus zugänglich
.plaintext(plaintext),
.ciphertext(ciphertext),
.done(done)
);
endmodule
// VERWUNDBAR: Debug-Register mit Schlüsseloffenlegung
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
);
// Sensible Register, die nicht lesbar sein sollten
reg [127:0] secret_key;
reg [127:0] session_key;
reg [31:0] auth_token;
// VERWUNDBAR: Debug kann alle Register lesen
always @(posedge clk) begin
if (debug_enable && debug_read) begin
case (debug_addr)
8'h00: debug_data <= secret_key[31:0]; // VERWUNDBAR!
8'h04: debug_data <= secret_key[63:32]; // VERWUNDBAR!
8'h08: debug_data <= secret_key[95:64]; // VERWUNDBAR!
8'h0C: debug_data <= secret_key[127:96]; // VERWUNDBAR!
8'h10: debug_data <= session_key[31:0]; // VERWUNDBAR!
8'h20: debug_data <= auth_token; // VERWUNDBAR!
default: debug_data <= 32'h0;
endcase
end
end
endmodule
// VERWUNDBAR: Software-Debug-Handler ohne Löschung
#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) {
// VERWUNDBAR: Tritt in Debug-Modus ein, ohne sensible Daten zu löschen
// Debug-Zugriff aktivieren
DEBUG_CTRL |= DEBUG_ENABLE;
// VERWUNDBAR: Schlüssel und Zustand verbleiben im Speicher
// Debugger kann jetzt crypto_ctx->aes_key lesen, etc.
// Debug-Bereitschaft signalisieren
DEBUG_STATUS = DEBUG_READY;
}
void vulnerable_debug_read(uint32_t addr, uint32_t* value) {
// VERWUNDBAR: Keine Filterung sensibler Adressen
*value = *(volatile uint32_t*)addr;
// Debugger kann jeden Speicher einschließlich Schlüssel lesen
}
Sichere Lösung
// SICHER: Vollständige Schlüssellöschung bei Debug-Modus
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 // Zeigt an, dass Debug sicher ist
);
// Interner Schlüsselspeicher
reg [127:0] core_key0;
reg [127:0] core_key1;
// Zwischenwerte
reg [127:0] round_key;
reg [127:0] state;
// Zusätzlicher sensibler Zustand
reg [127:0] key_schedule [0:10];
reg [127:0] temp_buffer;
// SICHER: Löschungsabschluss verfolgen
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
// SICHER: ALLE sensiblen Register löschen
core_key0 <= 128'h0;
core_key1 <= 128'h0;
round_key <= 128'h0;
state <= 128'h0;
temp_buffer <= 128'h0;
// SICHER: Schlüsselplan-Array löschen
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
// SICHER: Alles gelöscht, sicher für 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
// SICHER: Debug-Zugriff sperren bis Löschung abgeschlossen
wire debug_access_allowed = debug_mode && debug_ready && !clearing_in_progress;
// AES-Kern mit geschützten Schlüsseln
aes_core aes_inst (
.clk(clk),
.key0(debug_mode ? 128'h0 : core_key0), // SICHER: Null während Debug
.key1(debug_mode ? 128'h0 : core_key1), // SICHER: Null während Debug
.plaintext(plaintext),
.ciphertext(ciphertext),
.done(done)
);
endmodule
// SICHER: Debug-Schnittstelle mit geschützten Registern
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
);
// Sensible Register
reg [127:0] secret_key;
reg [127:0] session_key;
reg [31:0] auth_token;
// SICHER: Nicht-sensible Debug-Register
reg [31:0] debug_status;
reg [31:0] debug_version;
reg [31:0] debug_counter;
// SICHER: Sensibler Adressbereich
wire is_sensitive_addr = (debug_addr >= 8'h00) && (debug_addr < 8'h30);
// SICHER: Löschungszustand
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
// SICHER: Alle sensiblen Daten löschen wenn Debug aktiviert
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
// SICHER: Zugriff auf sensible Adressen blockieren
if (is_sensitive_addr) begin
debug_data <= 32'hDEADBEEF; // Dummy-Wert zurückgeben
access_denied <= 1'b1;
end
else begin
// Zugriff auf nicht-sensible Debug-Register erlauben
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; // Für nächste Debug-Sitzung zurücksetzen
access_denied <= 1'b0;
end
end
endmodule
// SICHER: Software-Debug-Handler mit ordnungsgemäßer Löschung
#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;
// SICHER: Sichere Speicherlöschung, die nicht wegoptimiert werden kann
static void secure_zero(volatile void* ptr, size_t size) {
volatile uint8_t* p = (volatile uint8_t*)ptr;
while (size--) {
*p++ = 0;
}
// Speicherbarriere, um sicherzustellen, dass Löschung abgeschlossen wird
__asm__ volatile("" ::: "memory");
}
void secure_enter_debug_mode(void) {
// SICHER: Alle sensiblen Daten vor Debug-Aktivierung löschen
// Kryptografische Schlüssel löschen
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));
// Zwischenzustand löschen
secure_zero(crypto_ctx->intermediate_state, sizeof(crypto_ctx->intermediate_state));
// SICHER: Alle anderen sensiblen Speicherbereiche löschen
clear_key_cache();
clear_pending_operations();
clear_temporary_buffers();
// Speicherbarriere vor Debug-Aktivierung
__sync_synchronize();
// Löschungsabschluss überprüfen
if (!verify_sensitive_memory_cleared()) {
// Löschung fehlgeschlagen - Debug nicht aktivieren
DEBUG_STATUS = DEBUG_ERROR;
return;
}
// SICHER: Jetzt sicher Debug-Zugriff zu aktivieren
DEBUG_CTRL |= DEBUG_ENABLE;
DEBUG_STATUS = DEBUG_READY;
}
// SICHER: Gefilterter Debug-Lesevorgang mit Adressvalidierung
int secure_debug_read(uint32_t addr, uint32_t* value) {
// Geschützte Adressbereiche definieren
static const struct {
uint32_t start;
uint32_t end;
} protected_ranges[] = {
{0x40000000, 0x40000100}, // Krypto-Kontext
{0x40001000, 0x40001100}, // Schlüsselspeicher
{0x40002000, 0x40002100}, // Sitzungsdaten
};
// SICHER: Prüfen, ob Adresse im geschützten Bereich liegt
for (int i = 0; i < sizeof(protected_ranges)/sizeof(protected_ranges[0]); i++) {
if (addr >= protected_ranges[i].start && addr < protected_ranges[i].end) {
// Null statt tatsächlichem Wert zurückgeben
*value = 0;
log_debug_blocked_access(addr);
return -EACCES;
}
}
// Adresse ist nicht geschützt, Lesen erlauben
*value = *(volatile uint32_t*)addr;
return 0;
}
// SICHER: Überprüfung, dass sensibler Speicher gelöscht ist
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-Beispiele
- CVE-2021-33080: Nicht gelöschte Debug-Informationen im SSD-Speicherbeschleuniger legten sensible Daten offen
- CVE-2022-31162: Rust-Bibliothek leakt OAuth-Details in Debug-Logs
Verwandte CWEs
- CWE-212: Unsachgemäße Entfernung sensibler Informationen vor Speicherung oder Übertragung (übergeordnet)
- CWE-1272: Sensible Informationen nicht gelöscht vor Debug-/Energiezustandsübergang (verwandt)
- CWE-1244: Internes Asset unsicherem Debug-Zugriffslevel oder -zustand ausgesetzt (verwandt)
Referenzen
- 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"