Unzureichende Zugriffskontrolle für fluchtigen Speicher mit Boot-Code
Beschreibung
Unzureichende Zugriffskontrolle für fluchtigen Speicher mit Boot-Code tritt auf, wenn ein Secure-Boot-Prozess Bootloader-Code von nichtfluchtigem Speicher (NVM) in fluchtigen Speicher (VM) ubertragt, ohne ausreichende Zugriffskontrollen zu implementieren. Während des Secure-Boot holt ROM-Code in einem System-on-Chip Bootloader-Code von externem NVM und speichert ihn in internem VM (DRAM/SRAM). Während der Code während der Übertragung authentifiziert wird, fehlen dem resultierenden fluchtigen Speicher angemessene Schutzmaßnahmen gegen Modifikation. Angreifer könnten den Secure-Boot-Prozess umgehen und ihren eigenen nicht vertrauenswurdigen, bösartigen Boot-Code ausführen.
Risiko
Unzureichender Schutz fluchtigen Speichers hat schwerwiegende Sicherheitsauswirkungen. Boot-Code kann nach der Authentifizierung modifiziert werden. Secure Boot kann vollständig umgangen werden. Bosartiger Code kann ausgeführt werden. Berechtigungserweiterung wird möglich. Systemintegritat ist kompromittiert. Die Vertrauenskette ist gebrochen. Firmware-Rootkits können installiert werden. Das Gerat kann dauerhaft kompromittiert werden.
Lösung
Entwerfen Sie Schutzmaßnahmen für fluchtigen Speicher, die Modifikationen durch Angreifer verhindern, nachdem Boot-Code geladen wurde. Testen Sie Schutzmaßnahmen grundlich gegen unbefugte Zugriffsversuche. Verifizieren Sie, dass fluchtiger Speicher sperrbar ist und während des Boots ordnungsgemäß gesperrt wird. Testen Sie Schreibversuche von nicht vertrauenswurdigen Agenten um Ablehnung sicherzustellen. Erfassen Sie Fabric-Master-Agenten, die während des Code-Ladens aktiv sind, und verifizieren Sie Zugriffsbeschrankungen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Umfang: Zugriffskontrolle Schutzmechanismus umgehen - Secure Boot kann durch Speichermodifikation umgangen werden. |
| Integritat | Umfang: Integritat Speicher modifizieren - Boot-Code kann nach der Authentifizierung verändert werden. |
| Autorisierung | Umfang: Autorisierung Unbefugten Code ausführen - Bosartiger Boot-Code kann ausgeführt werden. |
| Authentifizierung | Umfang: Authentifizierung Berechtigungen erlangen - Angreifer können Systemberechtigungen erlangen. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Boot-Code-Speicher ohne Zugriffskontrolle
module vulnerable_boot_memory (
input wire clk,
input wire reset_n,
input wire [31:0] address,
input wire [31:0] write_data,
input wire write_enable,
input wire read_enable,
input wire [3:0] master_id,
output reg [31:0] read_data,
// Boot-Steuerung
input wire boot_complete,
input wire signature_verified
);
// Boot-Code-Speicherbereich
reg [31:0] boot_memory [0:4095];
// Speicherbereich für Boot-Code
parameter BOOT_START = 32'h0000_0000;
parameter BOOT_END = 32'h0000_FFFF;
wire is_boot_region = (address >= BOOT_START) && (address <= BOOT_END);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
read_data <= 32'h0;
end
else begin
// VERWUNDBAR: Keine Zugriffskontrolle nach Laden des Boot-Codes
// Jeder Master kann in Boot-Speicher schreiben
if (write_enable && is_boot_region) begin
// VERWUNDBAR: Schreiben auch nach Signaturverifizierung erlaubt
boot_memory[address[13:2]] <= write_data;
// Problem: DMA-Controller (master_id = 4) kann Boot-Code
// überschreiben nachdem er authentifiziert wurde
end
if (read_enable && is_boot_region) begin
read_data <= boot_memory[address[13:2]];
end
end
end
endmodule
// VERWUNDBAR: Bootloader ohne Speichersperre
module vulnerable_boot_loader (
input wire clk,
input wire reset_n,
// NVM-Schnittstelle
output reg [31:0] nvm_addr,
input wire [31:0] nvm_data,
output reg nvm_read,
// VM-Schnittstelle
output reg [31:0] vm_addr,
output reg [31:0] vm_data,
output reg vm_write,
// Steuerung
input wire start_boot,
output reg boot_done,
output reg signature_valid
);
reg [15:0] byte_count;
reg [2:0] state;
parameter IDLE = 3'd0;
parameter LOAD = 3'd1;
parameter VERIFY = 3'd2;
parameter EXECUTE = 3'd3;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
state <= IDLE;
boot_done <= 1'b0;
signature_valid <= 1'b0;
end
else begin
case (state)
IDLE: begin
if (start_boot) begin
state <= LOAD;
byte_count <= 16'h0;
end
end
LOAD: begin
// Boot-Code von NVM in VM laden
nvm_addr <= byte_count;
nvm_read <= 1'b1;
vm_addr <= byte_count;
vm_data <= nvm_data;
vm_write <= 1'b1;
byte_count <= byte_count + 4;
if (byte_count >= 16'hFFF0) begin
state <= VERIFY;
end
end
VERIFY: begin
// Signatur verifizieren
if (verify_signature()) begin
signature_valid <= 1'b1;
state <= EXECUTE;
end
else begin
state <= IDLE;
end
end
EXECUTE: begin
// VERWUNDBAR: Keine Speichersperre vor Ausführung
// Boot-Code kann noch modifiziert werden!
boot_done <= 1'b1;
// Boot-Code ausführen...
end
endcase
end
end
endmodule
// VERWUNDBAR: Software-Boot ohne Speicherschutz
#include <stdint.h>
#define BOOT_MEMORY_BASE 0x20000000
#define BOOT_MEMORY_SIZE 0x10000
#define NVM_BASE 0x10000000
// VERWUNDBAR: Kein Speicherschutz nach Laden des Boot-Codes
void vulnerable_load_boot_code(void) {
uint32_t* boot_mem = (uint32_t*)BOOT_MEMORY_BASE;
uint32_t* nvm = (uint32_t*)NVM_BASE;
// Boot-Code von NVM in fluchtigen Speicher kopieren
for (int i = 0; i < BOOT_MEMORY_SIZE/4; i++) {
boot_mem[i] = nvm[i];
}
// Signatur verifizieren
if (!verify_boot_signature(boot_mem, BOOT_MEMORY_SIZE)) {
halt();
return;
}
// VERWUNDBAR: Kein Speicherschutz aktiviert
// Boot-Code kann zwischen Verifizierung und Ausführung modifiziert werden
// Boot-Code ausführen
void (*boot_entry)(void) = (void(*)(void))BOOT_MEMORY_BASE;
boot_entry();
// Angriff: DMA oder ein anderer Master modifiziert boot_mem nach
// Signaturverifizierung aber vor Ausführung
}
// VERWUNDBAR: MPU nicht für Boot-Speicher konfiguriert
void vulnerable_mpu_setup(void) {
// MPU für andere Regionen konfigurieren
configure_mpu_region(0, PERIPHERAL_BASE, PERIPHERAL_SIZE, MPU_RW);
configure_mpu_region(1, RAM_BASE, RAM_SIZE, MPU_RW);
// VERWUNDBAR: Boot-Speicherregion nicht geschützt
// Keine MPU-Region für BOOT_MEMORY_BASE konfiguriert
// Jeder Code kann Boot-Speicher modifizieren!
}
Sichere Lösung
// SICHER: Boot-Code-Speicher mit ordnungsgemäßer Zugriffskontrolle
module secure_boot_memory (
input wire clk,
input wire reset_n,
input wire [31:0] address,
input wire [31:0] write_data,
input wire write_enable,
input wire read_enable,
input wire [3:0] master_id,
output reg [31:0] read_data,
output reg access_denied,
// Boot-Steuerung
input wire boot_loading, // Boot-Code wird geladen
input wire signature_verified, // Signaturprüfung bestanden
input wire boot_complete, // Boot-Prozess abgeschlossen
// Sperrsteuerung
input wire lock_boot_memory, // Sperrbefehl
output reg boot_memory_locked
);
// Boot-Code-Speicherbereich
reg [31:0] boot_memory [0:4095];
// Speicherbereich für Boot-Code
parameter BOOT_START = 32'h0000_0000;
parameter BOOT_END = 32'h0000_FFFF;
// Vertrauenswurdige Master
parameter BOOT_ROM = 4'd0;
parameter SECURE_DMA = 4'd1;
wire is_boot_region = (address >= BOOT_START) && (address <= BOOT_END);
wire is_trusted_master = (master_id == BOOT_ROM) || (master_id == SECURE_DMA);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
read_data <= 32'h0;
access_denied <= 1'b0;
boot_memory_locked <= 1'b0;
end
else begin
access_denied <= 1'b0;
// SICHER: Boot-Speicher nach Signaturverifizierung sperren
if (lock_boot_memory || signature_verified) begin
boot_memory_locked <= 1'b1;
end
if (write_enable && is_boot_region) begin
// SICHER: Zugriffsberechtigungen prüfen
if (boot_memory_locked) begin
// Speicher gesperrt - alle Schreibvorgange verweigern
access_denied <= 1'b1;
end
else if (boot_loading && is_trusted_master) begin
// Nur Schreibvorgange während des Ladens von vertrauenswurdigen Mastern erlauben
boot_memory[address[13:2]] <= write_data;
end
else begin
access_denied <= 1'b1;
end
end
if (read_enable && is_boot_region) begin
// SICHER: Lesezugriff kontrolliert
if (is_trusted_master || boot_complete) begin
read_data <= boot_memory[address[13:2]];
end
else begin
read_data <= 32'h0;
access_denied <= 1'b1;
end
end
end
end
endmodule
// SICHER: Bootloader mit Speichersperre
module secure_boot_loader (
input wire clk,
input wire reset_n,
// NVM-Schnittstelle
output reg [31:0] nvm_addr,
input wire [31:0] nvm_data,
output reg nvm_read,
// VM-Schnittstelle
output reg [31:0] vm_addr,
output reg [31:0] vm_data,
output reg vm_write,
// Speicherschutz
output reg lock_boot_memory,
input wire memory_locked,
// Steuerung
input wire start_boot,
output reg boot_done,
output reg signature_valid,
output reg boot_error
);
reg [15:0] byte_count;
reg [3:0] state;
parameter IDLE = 4'd0;
parameter LOAD = 4'd1;
parameter VERIFY = 4'd2;
parameter LOCK = 4'd3;
parameter VERIFY_LOCK = 4'd4;
parameter EXECUTE = 4'd5;
parameter ERROR = 4'd6;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
state <= IDLE;
boot_done <= 1'b0;
signature_valid <= 1'b0;
lock_boot_memory <= 1'b0;
boot_error <= 1'b0;
end
else begin
case (state)
IDLE: begin
boot_error <= 1'b0;
if (start_boot) begin
state <= LOAD;
byte_count <= 16'h0;
end
end
LOAD: begin
// Boot-Code von NVM in VM laden
nvm_addr <= byte_count;
nvm_read <= 1'b1;
vm_addr <= byte_count;
vm_data <= nvm_data;
vm_write <= 1'b1;
byte_count <= byte_count + 4;
if (byte_count >= 16'hFFF0) begin
state <= VERIFY;
end
end
VERIFY: begin
vm_write <= 1'b0;
// Signatur verifizieren
if (verify_signature()) begin
signature_valid <= 1'b1;
state <= LOCK;
end
else begin
state <= ERROR;
end
end
LOCK: begin
// SICHER: Boot-Speicher vor Ausführung sperren
lock_boot_memory <= 1'b1;
state <= VERIFY_LOCK;
end
VERIFY_LOCK: begin
// SICHER: Verifizieren dass Speicher tatsachlich gesperrt ist
if (memory_locked) begin
state <= EXECUTE;
end
else begin
// Sperre fehlgeschlagen - abbrechen
state <= ERROR;
end
end
EXECUTE: begin
// SICHER: Nur ausführen nachdem Speicher gesperrt ist
boot_done <= 1'b1;
// Boot-Code sicher ausführen
end
ERROR: begin
boot_error <= 1'b1;
// Anhalten oder erneut versuchen
end
endcase
end
end
endmodule
// SICHER: Speicherschutzeinheit für Boot-Code
module boot_memory_protection (
input wire clk,
input wire reset_n,
input wire [31:0] address,
input wire [3:0] master_id,
input wire access_type, // 0=Lesen, 1=Schreiben
input wire boot_phase_complete,
input wire lock_enable,
output reg access_allowed,
output reg protection_violation
);
parameter BOOT_START = 32'h0000_0000;
parameter BOOT_END = 32'h0000_FFFF;
parameter BOOT_ROM = 4'd0;
reg memory_locked;
wire is_boot_region = (address >= BOOT_START) && (address <= BOOT_END);
wire is_boot_rom = (master_id == BOOT_ROM);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
memory_locked <= 1'b0;
access_allowed <= 1'b0;
protection_violation <= 1'b0;
end
else begin
protection_violation <= 1'b0;
// SICHER: Speicher nach Boot permanent sperren
if (lock_enable || boot_phase_complete) begin
memory_locked <= 1'b1;
end
// SICHER: Zugriffskontrolllogik
if (is_boot_region) begin
if (access_type == 1'b1) begin // Schreibzugriff
if (memory_locked) begin
// SICHER: Keine Schreibvorgange nach Sperre
access_allowed <= 1'b0;
protection_violation <= 1'b1;
end
else if (is_boot_rom) begin
// Nur Boot-ROM kann vor Sperre schreiben
access_allowed <= 1'b1;
end
else begin
access_allowed <= 1'b0;
protection_violation <= 1'b1;
end
end
else begin // Lesezugriff
// Lesen nach Boot-Abschluss erlaubt
access_allowed <= boot_phase_complete || is_boot_rom;
end
end
else begin
access_allowed <= 1'b1; // Nicht-Boot-Regionen
end
end
end
endmodule
// SICHER: Software-Boot mit ordnungsgemäßem Speicherschutz
#include <stdint.h>
#include <stdbool.h>
#define BOOT_MEMORY_BASE 0x20000000
#define BOOT_MEMORY_SIZE 0x10000
#define NVM_BASE 0x10000000
// MPU-Region für Boot-Speicher
#define MPU_BOOT_REGION 2
// SICHER: Boot-Speicher mit MPU sperren
static void lock_boot_memory(void) {
// MPU-Region für Boot-Speicher als schreibgeschützt konfigurieren
configure_mpu_region(
MPU_BOOT_REGION,
BOOT_MEMORY_BASE,
BOOT_MEMORY_SIZE,
MPU_RO | MPU_XN_CLEAR // Schreibgeschützt, Ausführung erlaubt
);
// MPU aktivieren
enable_mpu();
// Memory Barrier um sicherzustellen, dass Schutz aktiv ist
__asm__ volatile("dmb sy" ::: "memory");
__asm__ volatile("isb" ::: "memory");
}
// SICHER: Verifizieren dass Boot-Speicher geschützt ist
static bool verify_boot_memory_locked(void) {
// Versuchen in Boot-Speicher zu schreiben (sollte fehlschlagen)
volatile uint32_t* test_addr = (volatile uint32_t*)BOOT_MEMORY_BASE;
uint32_t original = *test_addr;
// Temporar Fault-Handler deaktivieren um Schreibversuch abzufangen
uint32_t fault_occurred = 0;
set_mem_fault_handler(&fault_occurred);
// Schreibversuch
*test_addr = 0xDEADBEEF;
// Fault-Handler wiederherstellen
restore_mem_fault_handler();
// Verifizieren dass Schreiben blockiert wurde
if (!fault_occurred || *test_addr != original) {
return false; // Schutz funktioniert nicht
}
return true;
}
// SICHER: Sicherer Boot mit Speicherschutz
void secure_load_boot_code(void) {
uint32_t* boot_mem = (uint32_t*)BOOT_MEMORY_BASE;
uint32_t* nvm = (uint32_t*)NVM_BASE;
// Boot-Code von NVM in fluchtigen Speicher kopieren
for (int i = 0; i < BOOT_MEMORY_SIZE/4; i++) {
boot_mem[i] = nvm[i];
}
// Memory Barrier vor Verifizierung
__asm__ volatile("dmb sy" ::: "memory");
// Signatur verifizieren
if (!verify_boot_signature(boot_mem, BOOT_MEMORY_SIZE)) {
log_error("Boot-Signaturverifizierung fehlgeschlagen");
secure_halt();
return;
}
// SICHER: Boot-Speicher vor Ausführung sperren
lock_boot_memory();
// SICHER: Verifizieren dass Schutz aktiv ist
if (!verify_boot_memory_locked()) {
log_error("Sperren des Boot-Speichers fehlgeschlagen");
secure_halt();
return;
}
// SICHER: Memory Barrier vor Ausführung
__asm__ volatile("dmb sy" ::: "memory");
__asm__ volatile("isb" ::: "memory");
// Jetzt sicher auszuführen - Speicher ist geschützt
void (*boot_entry)(void) = (void(*)(void))BOOT_MEMORY_BASE;
boot_entry();
}
// SICHER: Umfassende MPU-Einrichtung
void secure_mpu_setup(void) {
// MPU während Konfiguration deaktivieren
disable_mpu();
// Regionen konfigurieren
configure_mpu_region(0, PERIPHERAL_BASE, PERIPHERAL_SIZE, MPU_RW | MPU_XN);
configure_mpu_region(1, RAM_BASE, RAM_SIZE, MPU_RW | MPU_XN);
// SICHER: Boot-Speicherregion - anfangs RW für Laden
configure_mpu_region(
MPU_BOOT_REGION,
BOOT_MEMORY_BASE,
BOOT_MEMORY_SIZE,
MPU_RW | MPU_XN_CLEAR // RW während Laden, wird auf RO geändert
);
// MPU mit Standard-Verweigerung aktivieren
enable_mpu_with_default_deny();
}
CVE-Beispiele
- CVE-2019-2267: Gesperrte Speicherregionen könnten über andere Schnittstellen in einem Secure-Boot-Loader-Image aufgrund unzureichender Zugriffskontrolle modifiziert werden, was die Umgehung von Secure Boot ermöglichte.
Verwandte CWEs
- CWE-284: Unzureichende Zugriffskontrolle (ubergeordnet)
- CWE-1196: Sicherheitsfluss-Probleme (Kategorie)
- CWE-693: Versagen des Schutzmechanismus (verwandt)
- CAPEC-456: Infizierter Speicher (Angriffsmuster)
- CAPEC-679: Ausnutzung unsachgemäß konfigurierter Speicherschutzmaßnahmen (Angriffsmuster)
Referenzen
- MITRE Corporation. "CWE-1274: Improper Access Control for Volatile Memory Containing Boot Code." https://cwe.mitre.org/data/definitions/1274.html
- ARM. "TrustZone Security for Boot"
- NIST. "Secure Boot Guidelines"