Fehlende Möglichkeit zum Patchen von ROM-Code
Beschreibung
Die fehlende Möglichkeit zum Patchen von ROM-Code tritt auf, wenn das Fehlen einer Patch-Fähigkeit für ROM-Code Systeme anfällig für nicht behebbare Sicherheitsprobleme macht, die nach der Bereitstellung entdeckt werden. Systeme, die Root-of-Trust (RoT)-Mechanismen verwenden, führen unveränderlichen ROM-Code während des Bootvorgangs aus. Wenn Schwachstellen nach der Auslieferung entdeckt werden, erzeugt die Unfähigkeit, den ROM zu patchen, ein permanentes Sicherheitsrisiko -- alle in dem ROM-Code entdeckten Sicherheitsschwachstellen können für bereits im Einsatz befindliche Systeme niemals behoben werden. Dies ist besonders kritisch für Secure-Boot-Implementierungen und kryptografische Operationen, die im ROM ausgeführt werden.
Risiko
Fehlendes ROM-Patching hat schwerwiegende Auswirkungen. Permanente Sicherheitsschwachstellen. Nicht behebbare Fehler in bereitgestellten Systemen. Root of Trust dauerhaft kompromittiert. Kryptografische Mängel nicht reparierbar. Secure-Boot-Umgehung möglich. Produktrückrufe können erforderlich sein. Reduzierte Systemwartbarkeit. Hohe Wahrscheinlichkeit dauerhafter Kompromittierung.
Lösung
Implementieren Sie sichere Patch-Unterstützung, die das Patchen von ROM-Code bei nachfolgenden Bootvorgängen während der Architektur-, Entwurfs- und Implementierungsphasen ermöglicht. Beachten Sie, dass Hardware-Initialisierung und Signaturverifizierung möglicherweise nicht patchbar bleiben. Unterstützen Sie feldprogrammierbare Patches unter Verwendung von Hardware-Fuses für begrenzte Nachbereitstellungs-Patches. Integrieren Sie das Patch-Laden in die frühe Boot-Sequenz. Verwenden Sie signierte Patches, um nicht autorisierte Änderungen zu verhindern.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Wartbarkeit | Bereich: Sonstiges Systeme, die nicht gepatcht werden können, verbleiben auf unbestimmte Zeit in verwundbaren Zuständen, was die Wartbarkeit reduziert. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: ROM ohne Patch-Fähigkeit
module vulnerable_boot_rom (
input wire clk,
input wire rst_n,
input wire [11:0] rom_addr,
input wire ariane_boot_sel_i, // Boot-Auswahlsignal
output wire [31:0] rom_rdata
);
// Fester ROM-Inhalt - kann nach der Fertigung nicht geändert werden
reg [31:0] rom_linux [0:4095];
reg [31:0] rom_rdata_linux;
// ROM-Initialisierung (synthetisiert als Mask-ROM oder OTP)
initial begin
$readmemh("linux_bootrom.hex", rom_linux);
end
always @(posedge clk) begin
rom_rdata_linux <= rom_linux[rom_addr];
end
// VERWUNDBAR: Fehler - gibt immer Linux-ROM aus, unabhängig von der Auswahl
// Dieser Fehler kann nach der Chipfertigung nicht behoben werden!
assign rom_rdata = (ariane_boot_sel_i) ? rom_rdata_linux : rom_rdata_linux;
// Wenn eine Sicherheitsschwachstelle in rom_linux gefunden wird:
// - ROM-Inhalt kann nicht aktualisiert werden
// - Auswahllogik-Fehler kann nicht behoben werden
// - Alle bereitgestellten Geräte bleiben verwundbar
endmodule
// VERWUNDBAR: Secure Boot mit nicht patchbarer Verifizierung
module vulnerable_secure_boot_rom (
input wire clk,
input wire rst_n,
input wire [31:0] firmware_addr,
input wire [31:0] firmware_data,
input wire [2047:0] firmware_signature,
output reg boot_authorized,
output reg boot_error
);
// VERWUNDBAR: Öffentlicher Schlüssel im ROM eingebettet
// Kann nicht geändert werden, wenn Schlüssel kompromittiert ist
reg [2047:0] rom_public_key;
initial begin
// Schlüssel ist permanent eingebettet
rom_public_key = 2048'h<fixed_public_key>;
end
// VERWUNDBAR: Signaturverifizierungslogik im ROM
// Alle Fehler hier sind permanent
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
boot_authorized <= 1'b0;
boot_error <= 1'b0;
end else begin
// VERWUNDBAR: Wenn RSA-Verifizierung einen Fehler hat (z.B. Padding-Oracle),
// kann er nicht behoben werden
if (rsa_verify(firmware_data, firmware_signature, rom_public_key)) begin
boot_authorized <= 1'b1;
end else begin
boot_error <= 1'b1;
end
end
end
// Wenn Schwachstelle entdeckt wird (z.B. Bleichenbacher-Angriff):
// - Verifizierungsalgorithmus kann nicht aktualisiert werden
// - Kompromittierter Schlüssel kann nicht widerrufen werden
// - Alle Geräte dauerhaft verwundbar
endmodule
// VERWUNDBAR: Boot-ROM-Code ohne Patch-Unterstützung
// Dieser Code ist im ROM eingebrannt - kann nach der Fertigung nicht geändert werden
#include <stdint.h>
// VERWUNDBAR: Feste kryptografische Implementierung im ROM
// Wenn Algorithmus Schwachstelle hat, nicht patchbar
void rom_sha256(const uint8_t* data, size_t len, uint8_t* hash) {
// SHA-256-Implementierung
// VERWUNDBAR: Wenn hier Fehler gefunden wird, nicht behebbar
}
// VERWUNDBAR: Secure-Boot-Verifizierung im ROM
int rom_verify_firmware(const uint8_t* firmware, size_t size,
const uint8_t* signature) {
// VERWUNDBAR: Fester öffentlicher Schlüssel im ROM
static const uint8_t rom_public_key[256] = {
// RSA-2048 öffentlicher Schlüssel
// Kann nicht geändert werden, wenn kompromittiert
};
uint8_t hash[32];
rom_sha256(firmware, size, hash);
// VERWUNDBAR: RSA-Verifizierungscode im ROM
// Padding-Schwachstellen können nicht behoben werden
return rom_rsa_verify(hash, signature, rom_public_key);
}
// VERWUNDBAR: Haupt-Boot-Sequenz im ROM
void rom_boot(void) {
// 1. Hardware-Initialisierung (muss typischerweise im ROM sein)
init_hardware();
// 2. Firmware aus Speicher laden
uint8_t* firmware = load_firmware();
size_t size = get_firmware_size();
uint8_t* signature = get_firmware_signature();
// 3. VERWUNDBAR: Verifizierung verwendet potenziell fehlerhaften ROM-Code
if (!rom_verify_firmware(firmware, size, signature)) {
// Boot-Fehler
halt();
}
// 4. Firmware ausführen
// VERWUNDBAR: Keine Möglichkeit, Patches vor diesem Punkt anzuwenden
jump_to_firmware(firmware);
}
Sichere Lösung
// SICHER: ROM mit Patch-Ladefähigkeit
module secure_boot_rom_with_patch (
input wire clk,
input wire rst_n,
input wire [11:0] rom_addr,
input wire ariane_boot_sel_i,
// Patch-Schnittstelle
input wire [11:0] patch_addr,
input wire [31:0] patch_data,
input wire patch_valid,
input wire patch_enable, // Von Fuse oder sicherem Register
input wire [2047:0] patch_signature,
output wire [31:0] rom_rdata,
output reg patch_error
);
// Fester ROM-Inhalt
reg [31:0] rom_linux [0:4095];
reg [31:0] rom_patch [0:4095]; // SICHER: Patchbares RAM-Overlay
reg [31:0] rom_rdata_linux;
reg [31:0] rom_rdata_patch;
reg patch_loaded;
// SICHER: Fuse-Bits, die anzeigen, welche Adressen gepatcht sind
reg [4095:0] patch_mask;
// ROM-Initialisierung
initial begin
$readmemh("linux_bootrom.hex", rom_linux);
patch_loaded = 1'b0;
patch_mask = 4096'b0;
end
// SICHER: Patch-Laden mit Signaturverifizierung
wire patch_sig_valid;
patch_signature_verifier u_patch_verify (
.clk(clk),
.rst_n(rst_n),
.patch_data(patch_data),
.patch_signature(patch_signature),
.signature_valid(patch_sig_valid)
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
patch_loaded <= 1'b0;
patch_error <= 1'b0;
patch_mask <= 4096'b0;
end else if (patch_enable && patch_valid) begin
// SICHER: Nur signierte Patches laden
if (patch_sig_valid) begin
rom_patch[patch_addr] <= patch_data;
patch_mask[patch_addr] <= 1'b1;
patch_loaded <= 1'b1;
end else begin
patch_error <= 1'b1;
end
end
end
// ROM-Lesezugriff
always @(posedge clk) begin
rom_rdata_linux <= rom_linux[rom_addr];
rom_rdata_patch <= rom_patch[rom_addr];
end
// SICHER: Patch-Daten ausgeben wenn Adresse gepatcht ist, sonst ROM-Daten
wire use_patch = patch_loaded && patch_mask[rom_addr];
// SICHER: Korrekte Boot-Auswahl (Fehler behoben)
wire [31:0] selected_rom_data;
assign selected_rom_data = ariane_boot_sel_i ? rom_rdata_patch : rom_rdata_linux;
// SICHER: Patch-Overlay
assign rom_rdata = use_patch ? rom_rdata_patch : selected_rom_data;
endmodule
// SICHER: Fuse-basiertes Patching für begrenzte Aktualisierungen
module secure_fuse_patchable_rom (
input wire clk,
input wire rst_n,
input wire [11:0] rom_addr,
output wire [31:0] rom_rdata,
// Fuse-Schnittstelle
input wire [31:0] fuse_patch_0, // Hardware-Fuses - einmal programmierbar
input wire [31:0] fuse_patch_1,
input wire [31:0] fuse_patch_2,
input wire [31:0] fuse_patch_3,
input wire [11:0] fuse_patch_addr_0,
input wire [11:0] fuse_patch_addr_1,
input wire [11:0] fuse_patch_addr_2,
input wire [11:0] fuse_patch_addr_3,
input wire [3:0] fuse_patch_valid
);
reg [31:0] rom_content [0:4095];
reg [31:0] rom_rdata_reg;
initial begin
$readmemh("bootrom.hex", rom_content);
end
always @(posedge clk) begin
// SICHER: Prüfen ob Adresse einem Fuse-Patch entspricht
if (fuse_patch_valid[0] && rom_addr == fuse_patch_addr_0) begin
rom_rdata_reg <= fuse_patch_0;
end else if (fuse_patch_valid[1] && rom_addr == fuse_patch_addr_1) begin
rom_rdata_reg <= fuse_patch_1;
end else if (fuse_patch_valid[2] && rom_addr == fuse_patch_addr_2) begin
rom_rdata_reg <= fuse_patch_2;
end else if (fuse_patch_valid[3] && rom_addr == fuse_patch_addr_3) begin
rom_rdata_reg <= fuse_patch_3;
end else begin
rom_rdata_reg <= rom_content[rom_addr];
end
end
assign rom_rdata = rom_rdata_reg;
// SICHER: Fuses ermöglichen begrenztes Feld-Patching
// Bis zu 4 Befehl-Patches über OTP-Fuses
endmodule
// SICHER: Boot-ROM mit Patch-Ladefähigkeit
#include <stdint.h>
#include <stdbool.h>
// Minimaler ROM-Code - gerade genug zum Laden und Verifizieren von Patches
// Größerer Boot-Code ist im Flash und kann gepatcht werden
// Fester ROM-öffentlicher Schlüssel für Patch-Verifizierung
static const uint8_t ROM_PATCH_KEY[256] = {
// RSA-2048 Schlüssel nur für Patch-Verifizierung
};
// Patch-Speicherstruktur
typedef struct {
uint32_t patch_address;
uint32_t patch_size;
uint8_t patch_data[4096];
uint8_t signature[256];
} rom_patch_t;
// SICHER: Patch-Signatur vor dem Anwenden verifizieren
bool rom_verify_patch(const rom_patch_t* patch) {
uint8_t hash[32];
// Patch-Metadaten und Daten hashen
sha256_context ctx;
sha256_init(&ctx);
sha256_update(&ctx, &patch->patch_address, sizeof(patch->patch_address));
sha256_update(&ctx, &patch->patch_size, sizeof(patch->patch_size));
sha256_update(&ctx, patch->patch_data, patch->patch_size);
sha256_final(&ctx, hash);
// Signatur mit ROM-Schlüssel verifizieren
return rom_rsa_verify(hash, patch->signature, ROM_PATCH_KEY);
}
// SICHER: Patch-RAM-Overlay
static uint8_t patch_overlay[4096];
static uint32_t patch_base_address;
static uint32_t patch_size;
static bool patch_active = false;
// SICHER: Verifizierten Patch auf RAM-Overlay anwenden
bool rom_apply_patch(const rom_patch_t* patch) {
// Patch zuerst verifizieren
if (!rom_verify_patch(patch)) {
return false;
}
// Patch in RAM-Overlay kopieren
memcpy(patch_overlay, patch->patch_data, patch->patch_size);
patch_base_address = patch->patch_address;
patch_size = patch->patch_size;
patch_active = true;
return true;
}
// SICHER: ROM-Lesefunktion, die Patch-Overlay prüft
uint32_t rom_read(uint32_t address) {
// Prüfen ob Adresse im gepatchten Bereich liegt
if (patch_active &&
address >= patch_base_address &&
address < patch_base_address + patch_size) {
// Gepatchte Daten zurückgeben
return *(uint32_t*)&patch_overlay[address - patch_base_address];
}
// Originale ROM-Daten zurückgeben
return *(uint32_t*)(ROM_BASE + address);
}
// SICHER: Boot-Sequenz mit Patch-Unterstützung
void rom_boot(void) {
// 1. Minimale Hardware-Initialisierung (muss im ROM sein)
init_critical_hardware();
// 2. SICHER: Patches aus sicherem Speicher laden und verifizieren
rom_patch_t* patch = load_patch_from_storage();
if (patch != NULL) {
if (!rom_apply_patch(patch)) {
// Patch-Verifizierung fehlgeschlagen
// Mit ungepatchtem ROM fortfahren
}
}
// 3. Sekundären Bootloader laden (kann gepatcht sein)
uint8_t* bootloader = load_bootloader();
size_t size = get_bootloader_size();
uint8_t* signature = get_bootloader_signature();
// 4. Bootloader verifizieren (mit potenziell gepatchter Verifizierung)
if (!verify_bootloader(bootloader, size, signature)) {
halt();
}
// 5. Bootloader ausführen
jump_to_bootloader(bootloader);
}
// SICHER: Fuse-basierter Notfall-Patch
void apply_fuse_patches(void) {
// Patch-Daten aus OTP-Fuses lesen
uint32_t fuse_patch_addr = read_fuse(FUSE_PATCH_ADDR);
uint32_t fuse_patch_data = read_fuse(FUSE_PATCH_DATA);
if (fuse_patch_addr != 0xFFFFFFFF) {
// Einzelbefehl-Fuse-Patch anwenden
// Begrenzt, aber verfügbar für kritische Korrekturen
apply_instruction_patch(fuse_patch_addr, fuse_patch_data);
}
}
CVE-Beispiele
- CVE-2020-0069: MediaTek-BootROM-Schwachstelle, die nicht gepatcht werden könnte und Millionen von Geräten betraf.
- CVE-2019-14192: U-Boot-ROM-Schwachstellen, die einen Hardwareaustausch erforderten.
- CVE-2021-30900: Apple-BootROM (SecureROM)-Schwachstelle im checkm8-Exploit.
Verwandte CWEs
- CWE-1329: Reliance on Component That is Not Updateable (Eltern)
- CWE-1196: Security Flow Issues (Kategorie)
- CWE-693: Protection Mechanism Failure (verwandt)
Referenzen
- MITRE Corporation. "CWE-1310: Missing Ability to Patch ROM Code." https://cwe.mitre.org/data/definitions/1310.html
- NIST. "Platform Firmware Resiliency Guidelines (SP 800-193)"
- OpenTitan. "ROM and ROM Extension Design"