On-Chip-Debug- und Testschnittstelle mit unsachgemäßer Zugriffskontrolle
Beschreibung
On-Chip-Debug- und Testschnittstelle mit unsachgemäßer Zugriffskontrolle tritt auf, wenn Debug- und Testschnittstellen in Chips und SoCs ohne ordnungsgemäße Zugriffskontrolle implementiert werden. JTAG- und Debug-Schnittstellen sind für Entwicklung, Testen und Fertigung unerlässlich, müssen aber nach der Bereitstellung entweder gesperrt oder entfernt werden. Ohne ordnungsgemäße Kontrollen können Angreifer diese Schnittstellen ausnutzen, um Speicher zu lesen/schreiben, Secure Boot zu umgehen, kryptografische Schlüssel zu extrahieren oder bösartigen Code auszuführen.
Risiko
Ungeschützte Debug-Schnittstellen haben schwerwiegende Sicherheitsauswirkungen. Angreifer können gesamten Speicherinhalt lesen. Firmware kann modifiziert werden. Kryptografische Schlüssel können extrahiert werden. Secure Boot kann umgangen werden. Geistiges Eigentum kann offengelegt werden. Produktklonierung wird möglich. Alle Sicherheitskontrollen können umgangen werden. Persistenter Zugriff kann etabliert werden.
Lösung
Für Produktionsfreigabe: Sperren, Deaktivieren oder Entfernen von Debug-Ports. Implementierung von Authentifizierung für Debug-Zugriff. Verwendung von Sicherungen oder OTP, um Debug dauerhaft zu deaktivieren. Definition verschiedener Debug-Zugriffsebenen. Für Entwicklung: Beschränkung des Debug-Zugriffs auf autorisierte Entwickler. Schutz von Debug-Anmeldeinformationen. Protokollierung von Debug-Zugriffen. Sicherstellung, dass Debug-Schlüssel sich von Produktionsschlüsseln unterscheiden.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Speicher lesen - Debug-Schnittstellen können verwendet werden, um beliebigen Speicher einschließlich Geheimnisse zu lesen. |
| Integrität | Bereich: Integrität Speicher modifizieren - Debug kann Speicher und Code ändern. |
| Autorisierung | Bereich: Autorisierung Privilegien erlangen - Debug gewährt oft höchste Systemrechte. |
Beispielcode und Lösung
Verwundbarer Code
// Verwundbar: JTAG-Passwortauthentifizierung ohne Brute-Force-Schutz
module vulnerable_jtag_auth (
input wire tck,
input wire tms,
input wire tdi,
output reg tdo,
input wire [31:0] password_input,
output reg debug_enabled
);
// Hartcodiertes Passwort - kann extrahiert werden
parameter [31:0] DEBUG_PASSWORD = 32'hDEADBEEF;
reg [31:0] entered_password;
reg password_valid;
always @(posedge tck) begin
// VERWUNDBAR: Keine Sperre nach fehlgeschlagenen Versuchen
// Angreifer kann alle 2^32 Kombinationen durchprobieren
if (password_input == DEBUG_PASSWORD) begin
debug_enabled <= 1'b1;
password_valid <= 1'b1;
end else begin
password_valid <= 1'b0;
// Kein Zähler für fehlgeschlagene Versuche!
// Kein Sperrmechanismus!
end
end
endmodule
// Verwundbar: HMAC-Authentifizierung mit abgeschnittenem Geheimnis
module vulnerable_hmac_auth (
input wire clk,
input wire [511:0] secret_key,
input wire [255:0] challenge,
input wire [255:0] response,
output reg authenticated
);
wire [255:0] expected_response;
// VERWUNDBAR: Nur 32 Bits des 512-Bit-Geheimnisses verwendet!
// Reduziert Sicherheit drastisch von 2^512 auf 2^32
hmac_sha256 hmac (
.key(secret_key[31:0]), // Nur 32 Bits verwendet!
.message(challenge),
.mac(expected_response)
);
always @(posedge clk) begin
if (response == expected_response) begin
authenticated <= 1'b1;
end else begin
authenticated <= 1'b0;
end
end
endmodule
// Verwundbar: Debug-Schnittstelle ohne Authentifizierung
module vulnerable_debug_interface (
input wire clk,
input wire debug_request,
input wire [7:0] debug_command,
input wire [31:0] debug_address,
output reg [31:0] debug_data,
output reg debug_ack
);
// ÜBERHAUPT KEINE AUTHENTIFIZIERUNG
// Jeder mit physischem Zugang kann Debug-Schnittstelle verwenden
always @(posedge clk) begin
if (debug_request) begin
case (debug_command)
8'h01: debug_data <= read_memory(debug_address);
8'h02: write_memory(debug_address, debug_data);
8'h03: debug_data <= read_secure_fuses(); // Legt Geheimnisse offen!
8'h04: bypass_secure_boot(); // Deaktiviert Sicherheit!
default: debug_data <= 32'h0;
endcase
debug_ack <= 1'b1;
end
end
endmodule
// Verwundbar: Firmware mit schwacher Debug-Authentifizierung
#define DEBUG_PASSWORD "debug123" // Schwaches, hartcodiertes Passwort
bool authenticate_debug_access(const char* provided_password) {
// VERWUNDBAR: Einfacher String-Vergleich
// Timing-Angriff möglich
// Keine Sperre nach fehlgeschlagenen Versuchen
return strcmp(provided_password, DEBUG_PASSWORD) == 0;
}
void handle_debug_command(uint8_t command, uint32_t param) {
// Keine Authentifizierungsprüfung!
switch (command) {
case CMD_READ_MEMORY:
debug_output(read_memory(param));
break;
case CMD_WRITE_MEMORY:
write_memory(param, debug_input());
break;
case CMD_DUMP_KEYS:
debug_output(get_encryption_keys()); // Legt Geheimnisse offen!
break;
case CMD_DISABLE_SECURITY:
security_enabled = false; // Deaktiviert Sicherheit!
break;
}
}
Sichere Lösung
// Sicher: JTAG-Authentifizierung mit Brute-Force-Schutz
module secure_jtag_auth (
input wire tck,
input wire tms,
input wire tdi,
output reg tdo,
input wire [255:0] auth_response, // Kryptographische Antwort
input wire auth_attempt,
output reg debug_enabled,
output reg permanently_locked
);
// Sicherheitsparameter
parameter MAX_FAILED_ATTEMPTS = 3;
parameter LOCKOUT_CYCLES = 32'd1000000; // ~1 Sekunde bei 1MHz
reg [3:0] failed_attempts;
reg [31:0] lockout_counter;
reg in_lockout;
// Challenge-Response-Authentifizierung
reg [255:0] current_challenge;
wire [255:0] expected_response;
wire response_valid;
// Kryptographische Verifikation
crypto_verify verifier (
.challenge(current_challenge),
.response(auth_response),
.valid(response_valid)
);
always @(posedge tck) begin
if (permanently_locked) begin
// Gerät permanent gesperrt - kein Debug-Zugriff mehr
debug_enabled <= 1'b0;
end
else if (in_lockout) begin
// Temporäre Sperre aktiv
if (lockout_counter > 0) begin
lockout_counter <= lockout_counter - 1;
end else begin
in_lockout <= 1'b0;
end
debug_enabled <= 1'b0;
end
else if (auth_attempt) begin
if (response_valid) begin
// Erfolgreiche Authentifizierung
debug_enabled <= 1'b1;
failed_attempts <= 4'b0;
// Neue Challenge für nächstes Mal generieren
current_challenge <= generate_new_challenge();
end else begin
// Fehlgeschlagene Authentifizierung
failed_attempts <= failed_attempts + 1;
if (failed_attempts >= MAX_FAILED_ATTEMPTS - 1) begin
// Zu viele Fehlversuche - permanente Sperre
permanently_locked <= 1'b1;
end else begin
// Temporäre Sperre mit exponentiellem Backoff
in_lockout <= 1'b1;
lockout_counter <= LOCKOUT_CYCLES << failed_attempts;
end
debug_enabled <= 1'b0;
end
end
end
endmodule
// Sicher: HMAC-Authentifizierung mit vollständigem Geheimnis
module secure_hmac_auth (
input wire clk,
input wire [511:0] secret_key,
input wire [255:0] challenge,
input wire [255:0] response,
input wire verify_request,
output reg authenticated,
output reg auth_complete
);
wire [255:0] expected_response;
// Vollständigen 512-Bit-Geheimschlüssel verwenden
hmac_sha256 hmac (
.key(secret_key), // Volle 512 Bits
.message(challenge),
.mac(expected_response)
);
// Konstantzeitvergleich zur Verhinderung von Timing-Angriffen
wire [255:0] diff;
assign diff = response ^ expected_response;
always @(posedge clk) begin
if (verify_request) begin
// Konstantzeit: prüfen ob alle Bits null sind
authenticated <= (diff == 256'b0);
auth_complete <= 1'b1;
end else begin
auth_complete <= 1'b0;
end
end
endmodule
// Sicher: Debug-Schnittstelle mit korrekter Zugriffskontrolle
module secure_debug_interface (
input wire clk,
input wire reset_n,
input wire debug_request,
input wire [7:0] debug_command,
input wire [31:0] debug_address,
input wire debug_authenticated,
input wire [2:0] debug_access_level, // Gestufte Zugriffskontrolle
input wire production_mode,
output reg [31:0] debug_data,
output reg debug_ack,
output reg debug_error
);
// Zugriffsebenen-Definitionen
parameter ACCESS_NONE = 3'b000;
parameter ACCESS_BASIC = 3'b001;
parameter ACCESS_ADVANCED = 3'b010;
parameter ACCESS_FULL = 3'b111;
// Befehlszugriffsanforderungen
function [2:0] required_access_level;
input [7:0] cmd;
begin
case (cmd)
8'h01: required_access_level = ACCESS_BASIC; // Speicher lesen
8'h02: required_access_level = ACCESS_ADVANCED; // Speicher schreiben
8'h03: required_access_level = ACCESS_FULL; // Fuses lesen
8'h04: required_access_level = ACCESS_FULL; // Sicherheitsoperationen
default: required_access_level = ACCESS_NONE;
endcase
end
endfunction
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
debug_data <= 32'b0;
debug_ack <= 1'b0;
debug_error <= 1'b0;
end
else if (debug_request) begin
// Prüfen ob im Produktionsmodus - meiste Debug-Funktionen deaktiviert
if (production_mode && debug_command != 8'h01) begin
debug_error <= 1'b1;
debug_ack <= 1'b0;
end
// Authentifizierung prüfen
else if (!debug_authenticated) begin
debug_error <= 1'b1;
debug_ack <= 1'b0;
end
// Zugriffsebene prüfen
else if (debug_access_level < required_access_level(debug_command)) begin
debug_error <= 1'b1;
debug_ack <= 1'b0;
end
else begin
// Autorisiert - Befehl ausführen
case (debug_command)
8'h01: debug_data <= read_memory(debug_address);
8'h02: write_memory(debug_address, debug_data);
8'h03: debug_data <= read_secure_fuses();
default: debug_data <= 32'b0;
endcase
debug_ack <= 1'b1;
debug_error <= 1'b0;
end
end
end
endmodule
// Sicher: Firmware mit sicherer Debug-Authentifizierung
#include <stdint.h>
#include <stdbool.h>
#include "crypto.h"
#define MAX_AUTH_ATTEMPTS 3
#define LOCKOUT_DURATION_MS 30000
static uint8_t failed_attempts = 0;
static uint32_t lockout_until = 0;
static bool permanently_locked = false;
static uint8_t current_challenge[32];
// Kryptographische Challenge generieren
void generate_challenge(void) {
secure_random(current_challenge, sizeof(current_challenge));
}
// Kryptographische Antwort verifizieren
bool verify_response(const uint8_t* response, size_t response_len) {
uint8_t expected[32];
// Erwartete Antwort mit Gerätegeheimnis berechnen
hmac_sha256(get_device_secret(), 32, current_challenge, 32, expected);
// Konstantzeitvergleich
return secure_compare(response, expected, 32);
}
// Debug-Zugriff mit Sperrschutz authentifizieren
bool authenticate_debug_access(const uint8_t* response, size_t len) {
// Permanente Sperre prüfen
if (permanently_locked) {
return false;
}
// Temporäre Sperre prüfen
if (get_current_time_ms() < lockout_until) {
return false;
}
// Kryptographische Antwort verifizieren
if (verify_response(response, len)) {
failed_attempts = 0;
generate_challenge(); // Neue Challenge für nächstes Mal
return true;
}
// Authentifizierung fehlgeschlagen
failed_attempts++;
if (failed_attempts >= MAX_AUTH_ATTEMPTS) {
// Permanente Sperre
permanently_locked = true;
blow_lockout_fuse(); // Hardware-Durchsetzung
} else {
// Exponentieller Backoff bei Sperre
lockout_until = get_current_time_ms() +
(LOCKOUT_DURATION_MS << failed_attempts);
}
generate_challenge(); // Neue Challenge
return false;
}
// Debug-Befehlshandler mit Zugriffskontrolle
void handle_debug_command(uint8_t command, uint32_t param, uint8_t access_level) {
// Produktionsmodus-Prüfung
if (is_production_device() && command != CMD_READ_BASIC) {
debug_error(ERR_PRODUCTION_LOCKED);
return;
}
// Zugriffsebenen-Prüfung
if (access_level < get_required_access(command)) {
debug_error(ERR_INSUFFICIENT_ACCESS);
return;
}
// Autorisierten Befehl ausführen
switch (command) {
case CMD_READ_BASIC:
debug_output(read_memory_safe(param));
break;
case CMD_READ_ADVANCED:
debug_output(read_memory(param));
break;
case CMD_WRITE_MEMORY:
write_memory_checked(param, debug_input());
break;
default:
debug_error(ERR_UNKNOWN_COMMAND);
}
}
Ausgenutzt in der Praxis
Xbox 360 JTAG-Hack (2007)
Hacker nutzten ungeschützte JTAG-Schnittstellen auf der Xbox 360 aus, um das Sicherheitssystem der Konsole zu umgehen, was das Ausführen von nicht signiertem Code und raubkopierter Software ermöglichte.
Router-Debug-Port-Ausnutzungen (verschiedene)
Zahlreiche Router wurden mit aktivierten Debug-Ports ausgeliefert, die Angreifern volle Kontrolle über die Geräte ermöglichten.
Tools zum Testen und Ausnutzen
-
OpenOCD — Open-Source-On-Chip-Debugging-Tool.
-
JTAGulator — Hardware zur Identifizierung von JTAG-Pinouts.
-
JTAG Exploitation Tools — Black Magic Probe für ARM-Debugging.
CVE-Beispiele
-
CVE-2019-18827 — JTAG-Zugriff vor ROM-Code-Ausführung auf Barco ClickShare möglich.
-
CVE-2021-26312 — Debug-Schnittstelle ermöglicht unbefugten Speicherzugriff.
Referenzen
-
MITRE Corporation. "CWE-1191: On-Chip Debug and Test Interface With Improper Access Control." https://cwe.mitre.org/data/definitions/1191.html
-
NIST. "Guidelines for Securing JTAG Interfaces."
-
ARM. "Debug Authentication Architecture."