Wiederverwendung öffentlicher Schlüssel zum Signieren von Debug- und Produktionscode
Beschreibung
Die Wiederverwendung öffentlicher Schlüssel zum Signieren von Debug- und Produktionscode tritt auf, wenn ein einzelner öffentlicher Schlüssel zur Authentifizierung sowohl von Debug- als auch von Produktions-Firmware verwendet wird, was eine kritische Sicherheitsschwachstelle darstellt. Public-Key-Kryptographie verifiziert die Firmware-Integrität, indem ein Hash mit dem öffentlichen Schlüssel entschlüsselt und mit dem berechneten Firmware-Hash verglichen wird. Während der Entwicklung enthält Debug-Firmware umfangreiche Debug-Hooks, -Modi und -Nachrichten zum Testen. Wenn Debug-Firmware-Images durchsickern -- und das geschieht häufig -- können Angreifer sie auf Produktionsgeräten verwenden, wenn derselbe Schlüssel verwendet wird, und erhalten so erhebliche Systemkontrollfähigkeiten durch Debug-Funktionalität.
Risiko
Die Wiederverwendung von Schlüsseln für Debug und Produktion hat schwerwiegende Auswirkungen. Durchgesickerte Debug-Firmware ermöglicht Produktionsangriffe. Debug-Hooks sind auf Produktionssystemen ausnutzbar. Privilegieneskalation möglich. Systemkompromittierung erreichbar. Geistiges Eigentum offengelegt. Unbefugte Codeausführung ermöglicht. Speicher-Lese-/Schreibzugriff möglich. Identitätsübernahme-Angriffe ermöglicht. Die Vertrauensbasis ist grundlegend kompromittiert.
Lösung
Verwenden Sie verschiedene Schlüssel für Produktions- und Debug-Firmware. Reservieren Sie ausreichend Speicher für mehrere kryptographische Schlüssel. Wenn der Speicher begrenzt ist, erwägen Sie hardware-unterstütztes Schlüsselmanagement. Implementieren Sie Schlüsselwiderrufsmechanismen. Verwenden Sie separate Signierinfrastruktur für Debug und Produktion. Stellen Sie sicher, dass Debug-Firmware nicht auf Produktionsgeräten geladen werden kann. Erwägen Sie die Verwendung verschiedener Schlüsselgrößen oder Algorithmen zur zusätzlichen Trennung. Implementieren Sie Hardware-Fuses, um Debug-Fähigkeiten in der Produktion dauerhaft zu deaktivieren.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Debug-Modi legen sensible Daten und Speicherinhalte offen. |
| Integrität | Bereich: Integrität Debug-Firmware kann Speicher und Systemzustand modifizieren. |
| Verfügbarkeit | Bereich: Verfügbarkeit Debug-Fähigkeiten können den normalen Systembetrieb storen. |
| Zugriffskontrolle | Bereich: Zugriffskontrolle, Authentifizierung Privilegieneskalation und Identitätsübernahme durch Debug-Schnittstellen. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Einzelner Schlüssel für Debug und Produktion
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
// VERWUNDBAR: Einzelner öffentlicher Schlüssel für alle Firmware
static const uint8_t FIRMWARE_PUBLIC_KEY[256] = {
0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01,
// ... 2048-Bit-RSA-Öffentlicher-Schlüssel (gleich für Debug und Produktion)
// Dieser Schlüssel ist in Silizium/OTP eingebettet
};
typedef struct {
uint32_t magic;
uint32_t version;
uint32_t flags; // Bit 0: Debug-Modus
uint32_t size;
uint8_t signature[256];
uint8_t payload[];
} firmware_header_t;
// VERWUNDBAR: Keine Unterscheidung zwischen Debug und Produktion
bool vulnerable_verify_firmware(const firmware_header_t* header) {
uint8_t computed_hash[32];
uint8_t decrypted_hash[32];
// Hash der Firmware-Payload berechnen
sha256_hash(header->payload, header->size, computed_hash);
// VERWUNDBAR: Verwendet denselben Schlüssel für Debug und Produktion
rsa_decrypt(FIRMWARE_PUBLIC_KEY, header->signature, decrypted_hash);
// VERWUNDBAR: Prüft nur Signatur, nicht Firmware-Typ
return memcmp(computed_hash, decrypted_hash, 32) == 0;
}
bool vulnerable_boot_firmware(const firmware_header_t* header) {
// VERWUNDBAR: Debug-Firmware auf Produktionsgerät akzeptiert
if (!vulnerable_verify_firmware(header)) {
return false;
}
// VERWUNDBAR: Debug-Modus-Flag kann in durchgesickerter Debug-Firmware gesetzt sein
if (header->flags & 0x01) {
enable_debug_mode(); // Angreifer erhält Debug-Zugriff!
}
execute_firmware(header->payload, header->size);
return true;
}
// Angriffsszenario:
// 1. Debug-Firmware-Image sickert aus der Entwicklung durch
// 2. Angreifer extrahiert Debug-Firmware mit gesetztem Debug-Flag
// 3. Debug-Firmware ist mit demselben Schlüssel wie Produktion signiert
// 4. Angreifer lädt Debug-Firmware auf Produktionsgerät
// 5. Debug-Firmware bootet erfolgreich (gleicher Schlüssel!)
// 6. Angreifer hat vollen Debug-Zugriff auf Produktionshardware
# VERWUNDBAR: Firmware-Signierinfrastruktur mit gemeinsamen Schlüsseln
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
class VulnerableSigningInfrastructure:
def __init__(self):
# VERWUNDBAR: Einzelnes Schlüsselpaar für alles
self.private_key = self._load_master_key()
self.public_key = self.private_key.public_key()
def _load_master_key(self):
# VERWUNDBAR: Gleicher Schlüssel für alle Zwecke verwendet
return load_key_from_hsm("master_signing_key")
def sign_debug_firmware(self, firmware_data):
"""Debug-Firmware signieren - VERWUNDBAR: Verwendet Produktionsschlüssel."""
# VERWUNDBAR: Gleicher Schlüssel wie Produktion
return self._sign_data(firmware_data)
def sign_production_firmware(self, firmware_data):
"""Produktions-Firmware signieren - VERWUNDBAR: Gleicher Schlüssel wie Debug."""
# VERWUNDBAR: Gleicher Schlüssel wie Debug
return self._sign_data(firmware_data)
def _sign_data(self, data):
return self.private_key.sign(
data,
padding.PKCS1v15(),
hashes.SHA256()
)
# Build-System mit verwundbarer Infrastruktur
class VulnerableBuildSystem:
def __init__(self):
self.signing = VulnerableSigningInfrastructure()
def build_debug_firmware(self, source_code):
"""Debug-Firmware mit umfangreichen Hooks erstellen."""
firmware = compile_with_debug_flags(source_code)
# Debug-Fähigkeiten hinzufügen
firmware = add_debug_shell(firmware)
firmware = add_memory_dump_function(firmware)
firmware = add_jtag_enable(firmware)
# VERWUNDBAR: Mit demselben Schlüssel wie Produktion signiert
signature = self.signing.sign_debug_firmware(firmware)
return package_firmware(firmware, signature, debug=True)
def build_production_firmware(self, source_code):
"""Produktions-Firmware erstellen."""
firmware = compile_with_release_flags(source_code)
# VERWUNDBAR: Gleicher Schlüssel wie Debug, Debug-Firmware funktioniert hier auch
signature = self.signing.sign_production_firmware(firmware)
return package_firmware(firmware, signature, debug=False)
Sichere Lösung
// SICHER: Separate Schlüssel für Debug und Produktion
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
// SICHER: Separate öffentliche Schlüssel für verschiedene Firmware-Typen
static const uint8_t PRODUCTION_PUBLIC_KEY[384] = {
0x30, 0x82, 0x01, 0x8A, 0x02, 0x82, 0x01, 0x81,
// ... 3072-Bit-RSA-Öffentlicher-Schlüssel für Produktion
};
static const uint8_t DEBUG_PUBLIC_KEY[384] = {
0x30, 0x82, 0x01, 0x8A, 0x02, 0x82, 0x01, 0x81,
// ... Anderer 3072-Bit-RSA-Öffentlicher-Schlüssel für Debug
// Nur auf Debug-/Entwicklungseinheiten installiert
};
// SICHER: Gerätemodus in Hardware-Fuse gespeichert
typedef enum {
DEVICE_MODE_PRODUCTION = 0,
DEVICE_MODE_DEVELOPMENT = 1
} device_mode_t;
// SICHER: Gerätemodus aus OTP-Fuses lesen (kann nicht geändert werden)
device_mode_t get_device_mode(void) {
return read_otp_fuse(DEVICE_MODE_FUSE_ADDR);
}
typedef struct {
uint32_t magic;
uint32_t version;
uint32_t firmware_type; // 0 = Produktion, 1 = Debug
uint32_t size;
uint8_t signature[384];
uint8_t payload[];
} firmware_header_t;
// SICHER: Mit passendem Schlüssel basierend auf Firmware-Typ verifizieren
bool secure_verify_firmware(const firmware_header_t* header) {
const uint8_t* public_key;
device_mode_t device_mode = get_device_mode();
// SICHER: Schlüssel basierend auf Firmware-Typ auswählen
if (header->firmware_type == 0) {
// Produktions-Firmware - immer erlaubt
public_key = PRODUCTION_PUBLIC_KEY;
} else if (header->firmware_type == 1) {
// SICHER: Debug-Firmware nur auf Entwicklungsgeräten
if (device_mode != DEVICE_MODE_DEVELOPMENT) {
log_security_event("Debug-Firmware auf Produktionsgerät abgelehnt");
return false;
}
public_key = DEBUG_PUBLIC_KEY;
} else {
log_security_event("Unbekannter Firmware-Typ");
return false;
}
uint8_t computed_hash[32];
uint8_t decrypted_hash[32];
sha256_hash(header->payload, header->size, computed_hash);
rsa_decrypt(public_key, header->signature, decrypted_hash);
return secure_compare(computed_hash, decrypted_hash, 32);
}
bool secure_boot_firmware(const firmware_header_t* header) {
if (!secure_verify_firmware(header)) {
return false;
}
// SICHER: Debug-Funktionen nur auf Entwicklungsgeräten verfügbar
if (header->firmware_type == 1) {
// Bereits verifiziert, dass Gerät im Entwicklungsmodus ist
enable_debug_mode();
}
execute_firmware(header->payload, header->size);
return true;
}
// SICHER: Produktionsgeräte haben Debug-Schlüssel-Fuse gebrannt
void secure_production_provisioning(void) {
// Fuse brennen, um Produktionsmodus zu setzen (irreversibel)
blow_otp_fuse(DEVICE_MODE_FUSE_ADDR, DEVICE_MODE_PRODUCTION);
// SICHER: Optional Debug-Schlüsselspeicher löschen
secure_erase_key_slot(DEBUG_KEY_SLOT);
}
# SICHER: Firmware-Signierinfrastruktur mit separaten Schlüsseln
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend
from enum import Enum
class FirmwareType(Enum):
PRODUCTION = "production"
DEBUG = "debug"
FACTORY_TEST = "factory_test"
class SecureSigningInfrastructure:
def __init__(self):
# SICHER: Separate Schlüssel für jeden Firmware-Typ
self._keys = {
FirmwareType.PRODUCTION: self._load_production_key(),
FirmwareType.DEBUG: self._load_debug_key(),
FirmwareType.FACTORY_TEST: self._load_factory_key(),
}
def _load_production_key(self):
"""Produktions-Signierschlüssel aus sicherem HSM laden."""
# SICHER: Separater Schlüssel mit strikten Zugriffskontrollen
return load_key_from_hsm(
key_id="production_signing_key",
require_quorum=True, # Mehrere Bediener erforderlich
audit_log=True
)
def _load_debug_key(self):
"""Debug-Signierschlüssel aus Entwicklungs-HSM laden."""
# SICHER: Anderer Schlüssel, anderes HSM, anderer Zugriff
return load_key_from_hsm(
key_id="debug_signing_key",
hsm="development_hsm",
require_quorum=False # Weniger strikt für Entwicklung
)
def _load_factory_key(self):
"""Werktest-Signierschlüssel laden."""
return load_key_from_hsm(
key_id="factory_test_key",
hsm="factory_hsm"
)
def sign_firmware(self, firmware_data: bytes, firmware_type: FirmwareType) -> bytes:
"""Firmware mit dem passenden Schlüssel signieren."""
if firmware_type not in self._keys:
raise ValueError(f"Unbekannter Firmware-Typ: {firmware_type}")
private_key = self._keys[firmware_type]
# SICHER: Firmware-Typ in signierte Daten einbetten
type_prefix = firmware_type.value.encode() + b'\x00'
data_to_sign = type_prefix + firmware_data
signature = private_key.sign(
data_to_sign,
padding.PSS(
mgf=padding.MGF1(hashes.SHA384()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA384()
)
# SICHER: Signiervorgang protokollieren
audit_log(
action="firmware_signed",
firmware_type=firmware_type.value,
firmware_hash=sha384(firmware_data).hexdigest()
)
return signature
class SecureBuildSystem:
def __init__(self):
self.signing = SecureSigningInfrastructure()
def build_debug_firmware(self, source_code):
"""Debug-Firmware erstellen - funktioniert nur auf Debug-Geräten."""
firmware = compile_with_debug_flags(source_code)
firmware = add_debug_capabilities(firmware)
# SICHER: Verwendet Debug-spezifischen Schlüssel
signature = self.signing.sign_firmware(
firmware,
FirmwareType.DEBUG
)
return package_firmware(
firmware,
signature,
firmware_type=FirmwareType.DEBUG
)
def build_production_firmware(self, source_code):
"""Produktions-Firmware erstellen - funktioniert nur auf Produktionsgeräten."""
firmware = compile_with_release_flags(source_code)
# SICHER: Debug-Firmware mit anderem Schlüssel signiert funktioniert nicht
signature = self.signing.sign_firmware(
firmware,
FirmwareType.PRODUCTION
)
return package_firmware(
firmware,
signature,
firmware_type=FirmwareType.PRODUCTION
)
// SICHER: Hardware-Boot-ROM mit separater Schlüsselverifizierung
module secure_boot_rom (
input wire clk,
input wire rst_n,
input wire device_mode_fuse, // 0=Produktion, 1=Entwicklung
input wire [31:0] firmware_type,
input wire [3071:0] signature,
input wire [255:0] firmware_hash,
output reg boot_authorized,
output reg [7:0] boot_status
);
// SICHER: Separater Schlüsselspeicher
reg [3071:0] production_public_key; // Aus OTP
reg [3071:0] debug_public_key; // Aus OTP (in Produktion genullt)
// Boot-Statuscodes
localparam STATUS_OK = 8'h00;
localparam STATUS_INVALID_TYPE = 8'h01;
localparam STATUS_DEBUG_ON_PROD = 8'h02;
localparam STATUS_SIGNATURE_FAIL = 8'h03;
reg [3071:0] selected_key;
reg key_selected;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
boot_authorized <= 1'b0;
boot_status <= STATUS_OK;
key_selected <= 1'b0;
end else begin
// SICHER: Schlüsselauswahl basierend auf Firmware-Typ und Gerätemodus
case (firmware_type)
32'h0000_0000: begin // Produktions-Firmware
selected_key <= production_public_key;
key_selected <= 1'b1;
end
32'h0000_0001: begin // Debug-Firmware
// SICHER: Debug nur auf Entwicklungsgeräten erlauben
if (device_mode_fuse == 1'b1) begin
selected_key <= debug_public_key;
key_selected <= 1'b1;
end else begin
// SICHER: Debug-Firmware auf Produktion ablehnen
boot_authorized <= 1'b0;
boot_status <= STATUS_DEBUG_ON_PROD;
key_selected <= 1'b0;
end
end
default: begin
boot_authorized <= 1'b0;
boot_status <= STATUS_INVALID_TYPE;
key_selected <= 1'b0;
end
endcase
end
end
endmodule
CVE-Beispiele
- CVE-2020-10713: BootHole-Schwachstelle, bei der signierte Bootloader ersetzt werden könnten, was die Risiken des Schlüsselmanagements in Boot-Ketten verdeutlicht.
- CVE-2018-3665: Debug-Funktionen wurden durch Firmware-Schlüsselkompromittierung in bestimmten Prozessoren aktiviert.
Verwandte CWEs
- CWE-693: Versagen des Schutzmechanismus (übergeordnet)
- CWE-321: Verwendung eines fest codierten kryptographischen Schlüssels (gleichrangig)
- CWE-1207: Debug- und Testprobleme (Kategorie)
- CWE-522: Unzureichend geschützte Anmeldeinformationen (verwandt)
Referenzen
- MITRE Corporation. "CWE-1291: Public Key Re-Use for Signing both Debug and Production Code." https://cwe.mitre.org/data/definitions/1291.html
- NIST. "Guidelines for the Selection, Configuration, and Use of Transport Layer Security (TLS) Implementations"
- ARM. "Platform Security Architecture"