Versäumnis, reservierte Bits zu deaktivieren
Beschreibung
Versäumnis, reservierte Bits zu deaktivieren tritt auf, wenn ein Produkt nicht sicherstellt, dass als "reserviert" gekennzeichnete Hardware-Register-Bitfelder auf erwartete Standardwerte gesetzt werden. Reservierte Bits in Hardware-Registern sind Platzhalter für zukünftige Funktionalität oder für internen Gebrauch bestimmt. Wenn diese Bits beschreibbar und zugänglich bleiben, könnten Angreifer sie manipulieren, um unbeabsichtigte Verhaltensweisen zu aktivieren, Debug-Modi freizuschalten oder Sicherheitsmechanismen zu umgehen.
Risiko
Zugängliche reservierte Bits schaffen erhebliche Sicherheitsrisiken. Versteckte Debug-Modi können aktiviert werden. Undokumentierte Funktionen können freigeschaltet werden. Sicherheitsmechanismen können umgangen werden. Privilegieneskalation kann möglich werden. Hardwareverhalten kann unerwartet modifiziert werden. Geistiges Eigentum kann offengelegt werden. Werksseitige Testmodi können zugänglich werden.
Lösung
Reservierte Bits sollten schreibgeschützt sein und fest auf den erwarteten Wert verdrahtet sein. Wenn beschreibbar, müssen sie auf den erwarteten Wert zurückgesetzt werden. Implementieren Sie Write-Ignored-Verhalten für reservierte Felder. Dokumentieren Sie die richtige Behandlung für reservierte Bits. Testen Sie, dass reservierte Bits keine versteckten Funktionen freischalten. Verwenden Sie Hardware-Durchsetzung, um sicherzustellen, dass reservierte Bits nicht von Software modifiziert werden können.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Unautorisierter Zugriff - Reservierte Bits können versteckte Funktionen oder Debug-Zugriff freischalten. |
| Integrität | Bereich: Integrität Systemverhalten modifizieren - Manipulation kann Hardwareverhalten auf unbeabsichtigte Weise ändern. |
| Zugriffskontrolle | Bereich: Zugriffskontrolle Schutzmechanismus umgehen - Reservierte Bits können Sicherheitskontrollen deaktivieren. |
Beispielcode und Lösung
Verwundbarer Code
// Verwundbar: Reservierte Bits aktivieren versteckte Funktionalität
module vulnerable_control_register (
input wire clk,
input wire reset_n,
input wire [31:0] write_data,
input wire write_enable,
input wire [3:0] register_address,
output reg [31:0] read_data,
output reg gpio_out,
output reg debug_mode,
output reg bypass_security
);
// Steuerregister mit "reservierten" Bits die etwas bewirken
// Dokumentierte Bits:
// [0] - Feature A aktivieren
// [1] - Feature B aktivieren
// [7:2] - Reserviert (sollte 0 sein)
// [15:8] - Konfigurationswert
// [31:16]- Reserviert (sollte 0 sein)
reg [31:0] control_reg;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
control_reg <= 32'h0;
gpio_out <= 1'b0;
debug_mode <= 1'b0;
bypass_security <= 1'b0;
end
else if (write_enable && register_address == 4'b0001) begin
control_reg <= write_data;
// VERWUNDBAR: "Reservierte" Bits bewirken tatsächlich etwas!
// Reserviertes Bit [2] aktiviert heimlich GPIO
gpio_out <= write_data[2];
// Reserviertes Bit [3] aktiviert Debug-Modus
debug_mode <= write_data[3];
// Reserviertes Bit [16] umgeht Sicherheitsprüfungen!
bypass_security <= write_data[16];
end
end
// Weiteres verwundbares Muster: reservierte Adresse aktiviert versteckte Funktion
always @(posedge clk) begin
case (register_address)
4'b0000: read_data <= status_reg;
4'b0001: read_data <= control_reg;
// Adresse 0x0F ist "reserviert" aber aktiviert Hintertür
4'b1111: gpio_out <= 1'b1; // Versteckte Funktionalität!
default: read_data <= 32'h0;
endcase
end
endmodule
// Verwundbar: Undokumentierte Debug-Funktion über reserviertes Feld
module vulnerable_memory_controller (
input wire clk,
input wire [31:0] command,
input wire [31:0] address,
output reg [31:0] data_out,
output reg command_complete
);
// Befehlsformat:
// [3:0] - Operationscode
// [7:4] - Reserviert (sollte 0 sein)
// [31:8] - Parameter
wire [3:0] opcode = command[3:0];
wire [3:0] reserved = command[7:4]; // "Reservierte" Bits
always @(posedge clk) begin
case (opcode)
4'h1: begin
// Normale Leseoperation
data_out <= memory[address];
command_complete <= 1'b1;
end
4'h2: begin
// Normale Schreiboperation
memory[address] <= command[31:8];
command_complete <= 1'b1;
end
default: begin
// VERWUNDBAR: Reservierte Bits prüfen auf Debug-Hintertür
if (reserved == 4'hA) begin
// Versteckt: Gesamten Speicher ausgeben wenn reserviert = 0xA
dump_all_memory();
end
if (reserved == 4'hB) begin
// Versteckt: Zugriffskontrolle umgehen
bypass_acl <= 1'b1;
end
end
endcase
end
endmodule
// Verwundbar: Firmware mit aktiver Behandlung reservierter Bits
#define CONTROL_REG_ADDR 0x40001000
// Dokumentierte Registerbits
#define CTRL_ENABLE_A (1 << 0)
#define CTRL_ENABLE_B (1 << 1)
// Bits 2-7: Reserviert
#define CTRL_CONFIG_MASK 0xFF00
// Bits 16-31: Reserviert
// VERWUNDBAR: Versteckte Funktionalität in "reservierten" Bits
#define CTRL_DEBUG_MODE (1 << 3) // "Reserviert" aber aktiv
#define CTRL_BYPASS_SEC (1 << 16) // "Reserviert" aber aktiv
void configure_hardware(uint32_t config) {
// In Steuerregister schreiben
// Reservierte Bits werden auch geschrieben, aktivieren versteckte Funktionen
*(volatile uint32_t*)CONTROL_REG_ADDR = config;
}
// Angreifer kann durch Setzen reservierter Bits ausnutzen
void exploit_reserved_bits(void) {
// Undokumentierten Debug-Modus aktivieren
configure_hardware(CTRL_ENABLE_A | CTRL_DEBUG_MODE);
// Sicherheitsprüfungen umgehen
configure_hardware(CTRL_ENABLE_A | CTRL_BYPASS_SEC);
}
Sichere Lösung
// Sicher: Reservierte Bits sind korrekt deaktiviert
module secure_control_register (
input wire clk,
input wire reset_n,
input wire [31:0] write_data,
input wire write_enable,
input wire [3:0] register_address,
output reg [31:0] read_data,
output reg feature_a_enable,
output reg feature_b_enable,
output reg [7:0] config_value
);
// Steuerregister mit wirklich reservierten Bits
// Dokumentierte Bits:
// [0] - Feature A aktivieren
// [1] - Feature B aktivieren
// [7:2] - Reserviert (MUSS NULL SEIN)
// [15:8] - Konfigurationswert
// [31:16]- Reserviert (MUSS NULL SEIN)
reg [31:0] control_reg;
// Maske für nur beschreibbare Bits
localparam WRITABLE_MASK = 32'h0000FF03;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
control_reg <= 32'h0;
feature_a_enable <= 1'b0;
feature_b_enable <= 1'b0;
config_value <= 8'h0;
end
else if (write_enable && register_address == 4'b0001) begin
// Nur dokumentierte Bits beschreiben - reservierte ausmaskieren
control_reg <= write_data & WRITABLE_MASK;
// Nur dokumentierte Bits verwenden
feature_a_enable <= write_data[0];
feature_b_enable <= write_data[1];
config_value <= write_data[15:8];
// Reservierte Bits [7:2] und [31:16] werden ignoriert
// Keine versteckte Funktionalität
end
end
always @(*) begin
case (register_address)
4'b0000: read_data = status_reg;
4'b0001: read_data = control_reg;
// Reservierte Adressen geben Null zurück, tun nichts
default: read_data = 32'h0;
endcase
// Keine versteckte Funktionalität bei irgendeiner Adresse
end
endmodule
// Sicher: Keine Debug-Hintertüren in reservierten Feldern
module secure_memory_controller (
input wire clk,
input wire reset_n,
input wire [31:0] command,
input wire [31:0] address,
input wire command_valid,
output reg [31:0] data_out,
output reg command_complete,
output reg command_error
);
// Befehlsformat:
// [3:0] - Operationscode
// [7:4] - Reserviert (MUSS NULL SEIN)
// [31:8] - Parameter
wire [3:0] opcode = command[3:0];
wire [3:0] reserved = command[7:4];
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
data_out <= 32'h0;
command_complete <= 1'b0;
command_error <= 1'b0;
end
else if (command_valid) begin
// Prüfen ob reservierte Bits null sind
if (reserved != 4'h0) begin
// Reservierte Bits müssen null sein - Befehl ablehnen
command_error <= 1'b1;
command_complete <= 1'b1;
end
else begin
command_error <= 1'b0;
case (opcode)
4'h1: begin
// Normale Leseoperation
data_out <= memory[address];
command_complete <= 1'b1;
end
4'h2: begin
// Normale Schreiboperation
memory[address] <= command[31:8];
command_complete <= 1'b1;
end
default: begin
// Unbekannter Opcode - Fehler
command_error <= 1'b1;
command_complete <= 1'b1;
end
endcase
end
end
end
// Keine versteckte Funktionalität irgendwo
endmodule
// Sicher: Produktionsversion mit entferntem Debug
module secure_control_register_production (
input wire clk,
input wire reset_n,
input wire [31:0] write_data,
input wire write_enable,
output reg [31:0] control_reg
);
// Nur Produktionsbits
localparam PRODUCTION_MASK = 32'h0000FF03;
// Debug-Funktionalität in Produktion vollständig entfernt
// Nicht nur deaktiviert - die Logik existiert nicht
`ifdef PRODUCTION
// Produktions-Build: keine Debug-Logik synthetisiert
always @(posedge clk or negedge reset_n) begin
if (!reset_n)
control_reg <= 32'h0;
else if (write_enable)
control_reg <= write_data & PRODUCTION_MASK;
end
`else
// Entwicklungs-Build: Debug verfügbar
// Dieser Code ist NICHT in der Produktionssynthese enthalten
always @(posedge clk or negedge reset_n) begin
if (!reset_n)
control_reg <= 32'h0;
else if (write_enable)
control_reg <= write_data;
end
`endif
endmodule
// Sicher: Firmware behandelt reservierte Bits korrekt
#define CONTROL_REG_ADDR 0x40001000
// Dokumentierte Registerbits
#define CTRL_ENABLE_A (1 << 0)
#define CTRL_ENABLE_B (1 << 1)
#define CTRL_CONFIG_MASK 0x0000FF00
// Maske aller gültigen Bits
#define CTRL_VALID_MASK (CTRL_ENABLE_A | CTRL_ENABLE_B | CTRL_CONFIG_MASK)
// Reservierte Bits - nur zur Dokumentation
// Diese Bits MÜSSEN als Null geschrieben werden
#define CTRL_RESERVED_MASK (~CTRL_VALID_MASK)
void configure_hardware(uint32_t config) {
// Reservierte Bits vor dem Schreiben ausmaskieren
uint32_t safe_config = config & CTRL_VALID_MASK;
// Überprüfen ob reservierte Bits gesetzt waren (optionale Prüfung)
if (config & CTRL_RESERVED_MASK) {
log_warning("Versuch reservierte Bits zu setzen: 0x%08x", config);
}
// Nur gültige Bits schreiben
*(volatile uint32_t*)CONTROL_REG_ADDR = safe_config;
}
uint32_t read_hardware_config(void) {
uint32_t value = *(volatile uint32_t*)CONTROL_REG_ADDR;
// Reservierte Bits beim Lesen ausmaskieren
return value & CTRL_VALID_MASK;
}
// Validierungsfunktion für Sicherheitsaudit
bool validate_no_reserved_bit_effects(void) {
uint32_t original = read_hardware_config();
// Versuchen jedes reservierte Bit zu setzen
for (int bit = 0; bit < 32; bit++) {
if (CTRL_RESERVED_MASK & (1 << bit)) {
// Dies ist ein reserviertes Bit - verifizieren dass es keine Wirkung hat
*(volatile uint32_t*)CONTROL_REG_ADDR = original | (1 << bit);
// Prüfen ob sich der Systemzustand unerwartet geändert hat
if (detect_state_change()) {
log_error("Reserviertes Bit %d hat Wirkung!", bit);
return false;
}
}
}
// Original wiederherstellen
*(volatile uint32_t*)CONTROL_REG_ADDR = original;
return true;
}
Ausgenutzt in der Praxis
Intel Management Engine Hintertür (2017)
Forscher entdeckten, dass bestimmte undokumentierte Bits in Intel-Prozessoren verwendet werden könnten, um Management-Engine-Funktionen zu aktivieren oder zu deaktivieren, einschließlich potenzieller Sicherheitsumgehungen.
Tools zum Testen und Ausnutzen
-
Hardware-Debugger (JTAG, SWD) zum Testen von Registerzugriff.
-
Fuzzing-Tools für Hardware-Register.
CVE-Beispiele
- CVE-2017-5703 — Undokumentierte CPU-Bits ermöglichten Sicherheitsumgehung.
Referenzen
-
MITRE Corporation. "CWE-1209: Failure to Disable Reserved Bits." https://cwe.mitre.org/data/definitions/1209.html
-
ARM. "Register Specification Guidelines."
-
Intel. "Hardware Security Best Practices."