Unsachgemäße Zugriffskontrolle für Registerschnittstelle

Beschreibung

Unsachgemäße Zugriffskontrolle für Registerschnittstelle tritt auf, wenn ein Produkt speicherabgebildete I/O-Register verwendet, die Hardware-Zugriff von Software ermöglichen, aber keine ordnungsgemäßen Zugriffskontrollen für diese Register hat. Peripheriegeräte in Systems-on-Chip (SoC) und anderen Geräten werden üblicherweise über speicherabgebildete Registerschnittstellen angesprochen. Ohne angemessenen Schutz könnte bösartige Software sicherheitskritische Hardware-Daten kompromittieren, was zu Verlust von Vertraulichkeit und Integrität führt.

Risiko

Ungeschützte Registerschnittstellen haben schwerwiegende Sicherheitsauswirkungen. Kryptographische Schlüssel können lesbar sein. Sicherheitskonfigurationen können modifiziert werden. Hardware-Zustand kann korrumpiert werden. Schutzmechanismen können umgangen werden. Privilegieneskalation wird möglich. Sicherheitskritische Daten können verändert werden. Unerwartete Hardware-Zustände können ausgelöst werden. Ausführungslogik kann böswillig modifiziert werden.

Lösung

Etablieren Sie ordnungsgemäße Richtlinien für Hardware-Registerzugriff von Software. Stellen Sie sicher, dass Zugriffskontrollrichtlinien gemäß Spezifikationen korrekt implementiert werden. Implementieren Sie Privilegstufen-Prüfungen für Registerzugriff. Deaktivieren Sie Rücklese-Pfade für sensible Daten wie kryptographische Schlüssel. Verwenden Sie hardware-erzwungene Zugriffskontrolle. Validieren Sie alle Registeradressen und -werte. Implementieren Sie ordnungsgemäßen CSR-Schutz in Prozessoren.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Vertraulichkeit

Speicher lesen - Angreifer können geschützte Informationen durch ungeschützte Register lesen.
IntegritätBereich: Integrität

Speicher modifizieren - Sicherheitszustand, Einstellungen und kritische Daten können korrumpiert oder modifiziert werden.
ZugriffskontrolleBereich: Zugriffskontrolle

Privilegien erlangen - Unautorisierte Privilegieneskalation durch Registermanipulation.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Kryptographischer Beschleuniger mit lesbaren Schlüsseln

module vulnerable_crypto_accelerator (
    input wire clk,
    input wire reset_n,
    input wire [7:0] reg_addr,
    input wire [31:0] reg_write_data,
    input wire reg_write,
    input wire reg_read,
    output reg [31:0] reg_read_data
);

    // Schlüsselspeicher
    reg [255:0] crypto_key;

    // Status- und Kontrollregister
    reg [31:0] status_reg;
    reg [31:0] control_reg;

    // Registeradressen
    parameter KEY_REG_0 = 8'h00;
    parameter KEY_REG_1 = 8'h04;
    parameter KEY_REG_2 = 8'h08;
    parameter KEY_REG_3 = 8'h0C;
    parameter KEY_REG_4 = 8'h10;
    parameter KEY_REG_5 = 8'h14;
    parameter KEY_REG_6 = 8'h18;
    parameter KEY_REG_7 = 8'h1C;
    parameter STATUS_REG = 8'h20;
    parameter CONTROL_REG = 8'h24;

    // Schlüssel schreiben
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            crypto_key <= 256'h0;
        end
        else if (reg_write) begin
            case (reg_addr)
                KEY_REG_0: crypto_key[31:0] <= reg_write_data;
                KEY_REG_1: crypto_key[63:32] <= reg_write_data;
                KEY_REG_2: crypto_key[95:64] <= reg_write_data;
                KEY_REG_3: crypto_key[127:96] <= reg_write_data;
                KEY_REG_4: crypto_key[159:128] <= reg_write_data;
                KEY_REG_5: crypto_key[191:160] <= reg_write_data;
                KEY_REG_6: crypto_key[223:192] <= reg_write_data;
                KEY_REG_7: crypto_key[255:224] <= reg_write_data;
                CONTROL_REG: control_reg <= reg_write_data;
            endcase
        end
    end

    // VERWUNDBAR: Schlüsselregister sind lesbar!
    always @(*) begin
        case (reg_addr)
            // VERWUNDBAR: Schlüssel kann von jeder Software zurückgelesen werden
            KEY_REG_0: reg_read_data = crypto_key[31:0];
            KEY_REG_1: reg_read_data = crypto_key[63:32];
            KEY_REG_2: reg_read_data = crypto_key[95:64];
            KEY_REG_3: reg_read_data = crypto_key[127:96];
            KEY_REG_4: reg_read_data = crypto_key[159:128];
            KEY_REG_5: reg_read_data = crypto_key[191:160];
            KEY_REG_6: reg_read_data = crypto_key[223:192];
            KEY_REG_7: reg_read_data = crypto_key[255:224];
            STATUS_REG: reg_read_data = status_reg;
            CONTROL_REG: reg_read_data = control_reg;
            default: reg_read_data = 32'h0;
        endcase
    end

endmodule

// VERWUNDBAR: RISC-V CSR ohne ordnungsgemäße Privilegprüfung
module vulnerable_csr_module (
    input wire clk,
    input wire reset_n,
    input wire [11:0] csr_addr,
    input wire [31:0] csr_write_data,
    input wire csr_write,
    input wire csr_read,
    input wire [1:0] privilege_level,  // 0=U, 1=S, 3=M
    output reg [31:0] csr_read_data,
    output reg illegal_access
);

    // Machine-Level CSRs (erfordern M-Modus)
    reg [31:0] mstatus;
    reg [31:0] mepc;
    reg [31:0] mcause;
    reg [31:0] mtvec;

    // CSR-Adressen
    parameter MSTATUS = 12'h300;
    parameter MEPC = 12'h341;
    parameter MCAUSE = 12'h342;
    parameter MTVEC = 12'h305;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            mstatus <= 32'h0;
            mepc <= 32'h0;
            mcause <= 32'h0;
            mtvec <= 32'h0;
            illegal_access <= 1'b0;
        end
        else if (csr_write) begin
            // VERWUNDBAR: MEPC von Privilegprüfung ausgeschlossen!
            if (csr_addr == MEPC) begin
                // VERWUNDBAR: Jede Privilegstufe kann MEPC schreiben
                mepc <= csr_write_data;
            end
            // Andere CSRs ordnungsgemäß geschützt
            else if (csr_addr[11:10] == 2'b11 && privilege_level != 2'd3) begin
                illegal_access <= 1'b1;
            end
            else begin
                case (csr_addr)
                    MSTATUS: mstatus <= csr_write_data;
                    MCAUSE: mcause <= csr_write_data;
                    MTVEC: mtvec <= csr_write_data;
                endcase
            end
        end
    end

    // Angriff: Benutzermodus schreibt MEPC, um Exception-Rücksprung
    // auf beliebigen Code umzuleiten und Privilegieneskalation zu erreichen

endmodule
// VERWUNDBAR: Software greift auf ungeschützte Register zu

#include <stdint.h>

#define CRYPTO_BASE 0x40000000
#define CRYPTO_KEY_REG_0 (CRYPTO_BASE + 0x00)
#define CRYPTO_KEY_REG_1 (CRYPTO_BASE + 0x04)
// ...

// VERWUNDBAR: Jeder Prozess kann den Schlüssel lesen
void vulnerable_read_key(uint8_t* key_buffer) {
    volatile uint32_t* key_regs = (volatile uint32_t*)CRYPTO_KEY_REG_0;

    // VERWUNDBAR: Keine Zugriffskontrollprüfung
    // Bösartiger Prozess kann kryptographischen Schlüssel lesen
    for (int i = 0; i < 8; i++) {
        ((uint32_t*)key_buffer)[i] = key_regs[i];
    }
}

// VERWUNDBAR: Jeder Prozess kann Sicherheitsregister modifizieren
void vulnerable_modify_security_config(void) {
    volatile uint32_t* security_ctrl = (volatile uint32_t*)0x40001000;

    // VERWUNDBAR: Keine Privilegprüfung
    *security_ctrl = 0;  // Sicherheitsfunktionen deaktivieren
}

Sichere Lösung

// SICHER: Kryptographischer Beschleuniger mit geschützten Schlüsseln

module secure_crypto_accelerator (
    input wire clk,
    input wire reset_n,
    input wire [7:0] reg_addr,
    input wire [31:0] reg_write_data,
    input wire reg_write,
    input wire reg_read,
    input wire [1:0] privilege_level,  // 0=Benutzer, 1=Supervisor, 2=Hypervisor, 3=sicher
    input wire secure_access,           // Hardware-erzwungenes Sicher-Zugriffssignal
    output reg [31:0] reg_read_data,
    output reg access_denied
);

    // Schlüsselspeicher
    reg [255:0] crypto_key;
    reg key_loaded;

    // Status- und Kontrollregister
    reg [31:0] status_reg;
    reg [31:0] control_reg;

    // Registeradressen
    parameter KEY_REG_0 = 8'h00;
    parameter KEY_REG_1 = 8'h04;
    parameter KEY_REG_2 = 8'h08;
    parameter KEY_REG_3 = 8'h0C;
    parameter KEY_REG_4 = 8'h10;
    parameter KEY_REG_5 = 8'h14;
    parameter KEY_REG_6 = 8'h18;
    parameter KEY_REG_7 = 8'h1C;
    parameter STATUS_REG = 8'h20;
    parameter CONTROL_REG = 8'h24;

    // SICHER: Schlüsseladressen sind nur-schreiben, erfordern sicheren Zugriff
    wire is_key_addr = (reg_addr >= KEY_REG_0) && (reg_addr <= KEY_REG_7);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            crypto_key <= 256'h0;
            key_loaded <= 1'b0;
            access_denied <= 1'b0;
        end
        else begin
            access_denied <= 1'b0;

            if (reg_write) begin
                // SICHER: Schlüsselschreibvorgänge erfordern sicheren Zugriff
                if (is_key_addr) begin
                    if (!secure_access) begin
                        access_denied <= 1'b1;
                    end
                    else begin
                        case (reg_addr)
                            KEY_REG_0: crypto_key[31:0] <= reg_write_data;
                            KEY_REG_1: crypto_key[63:32] <= reg_write_data;
                            KEY_REG_2: crypto_key[95:64] <= reg_write_data;
                            KEY_REG_3: crypto_key[127:96] <= reg_write_data;
                            KEY_REG_4: crypto_key[159:128] <= reg_write_data;
                            KEY_REG_5: crypto_key[191:160] <= reg_write_data;
                            KEY_REG_6: crypto_key[223:192] <= reg_write_data;
                            KEY_REG_7: begin
                                crypto_key[255:224] <= reg_write_data;
                                key_loaded <= 1'b1;
                            end
                        endcase
                    end
                end
                // Kontrollregister erfordert mindestens Supervisor
                else if (reg_addr == CONTROL_REG) begin
                    if (privilege_level < 2'd1) begin
                        access_denied <= 1'b1;
                    end
                    else begin
                        control_reg <= reg_write_data;
                    end
                end
            end
        end
    end

    // SICHER: Schlüsselregister sind NICHT lesbar
    always @(*) begin
        access_denied = 1'b0;

        if (reg_read) begin
            case (reg_addr)
                // SICHER: Schlüsselregister geben Null zurück, nicht echten Schlüssel
                KEY_REG_0, KEY_REG_1, KEY_REG_2, KEY_REG_3,
                KEY_REG_4, KEY_REG_5, KEY_REG_6, KEY_REG_7: begin
                    reg_read_data = 32'h0;  // Nur-schreiben
                    access_denied = 1'b1;   // Zugriffsversuch markieren
                end
                STATUS_REG: reg_read_data = status_reg;
                CONTROL_REG: begin
                    if (privilege_level < 2'd1) begin
                        reg_read_data = 32'h0;
                        access_denied = 1'b1;
                    end
                    else begin
                        reg_read_data = control_reg;
                    end
                end
                default: reg_read_data = 32'h0;
            endcase
        end
        else begin
            reg_read_data = 32'h0;
        end
    end

endmodule

// SICHER: RISC-V CSR mit ordnungsgemäßer Privilegprüfung
module secure_csr_module (
    input wire clk,
    input wire reset_n,
    input wire [11:0] csr_addr,
    input wire [31:0] csr_write_data,
    input wire csr_write,
    input wire csr_read,
    input wire [1:0] privilege_level,
    output reg [31:0] csr_read_data,
    output reg illegal_access
);

    // Machine-Level CSRs
    reg [31:0] mstatus;
    reg [31:0] mepc;
    reg [31:0] mcause;
    reg [31:0] mtvec;

    parameter MSTATUS = 12'h300;
    parameter MEPC = 12'h341;
    parameter MCAUSE = 12'h342;
    parameter MTVEC = 12'h305;

    // SICHER: Erforderliches Privileg aus CSR-Adresse extrahieren
    wire [1:0] required_privilege = csr_addr[9:8];

    // SICHER: Alle M-Modus-CSRs erfordern M-Modus-Privileg
    wire privilege_sufficient = (privilege_level >= required_privilege);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            mstatus <= 32'h0;
            mepc <= 32'h0;
            mcause <= 32'h0;
            mtvec <= 32'h0;
            illegal_access <= 1'b0;
        end
        else begin
            illegal_access <= 1'b0;

            if (csr_write) begin
                // SICHER: Privileg für ALLE CSRs einschließlich MEPC prüfen
                if (!privilege_sufficient) begin
                    illegal_access <= 1'b1;
                end
                else begin
                    case (csr_addr)
                        MSTATUS: mstatus <= csr_write_data;
                        MEPC: mepc <= csr_write_data;  // SICHER: Jetzt geschützt
                        MCAUSE: mcause <= csr_write_data;
                        MTVEC: mtvec <= csr_write_data;
                        default: illegal_access <= 1'b1;
                    endcase
                end
            end

            if (csr_read) begin
                if (!privilege_sufficient) begin
                    illegal_access <= 1'b1;
                    csr_read_data <= 32'h0;
                end
                else begin
                    case (csr_addr)
                        MSTATUS: csr_read_data <= mstatus;
                        MEPC: csr_read_data <= mepc;
                        MCAUSE: csr_read_data <= mcause;
                        MTVEC: csr_read_data <= mtvec;
                        default: csr_read_data <= 32'h0;
                    endcase
                end
            end
        end
    end

endmodule
// SICHER: Software mit ordnungsgemäßer Registerzugriffskontrolle

#include <stdint.h>

#define CRYPTO_BASE 0x40000000

// SICHER: Schlüssel-Laden nur über sichere API
int secure_load_key(const uint8_t* key, size_t key_len) {
    // SICHER: Aufrufer-Privileg prüfen
    if (get_current_privilege() < PRIVILEGE_SECURE) {
        log_security_event("Unprivilegierter Schlüssel-Ladeversuch");
        return -EPERM;
    }

    // SICHER: Schlüssellänge validieren
    if (key_len != 32) {
        return -EINVAL;
    }

    // SICHER: Sicheren Hardware-Pfad verwenden
    volatile uint32_t* key_regs = (volatile uint32_t*)(CRYPTO_BASE + 0x00);

    // Interrupts während Schlüssel-Laden deaktivieren
    uint32_t irq_state = disable_interrupts();

    for (int i = 0; i < 8; i++) {
        key_regs[i] = ((uint32_t*)key)[i];
    }

    // Speicherbarriere
    __sync_synchronize();

    restore_interrupts(irq_state);

    return 0;
}

// SICHER: Schlüssel-Lesen gibt Fehler zurück
int secure_read_key(uint8_t* key_buffer) {
    // SICHER: Schlüssel sind niemals lesbar
    log_security_event("Versuch, kryptographischen Schlüssel zu lesen");
    return -EPERM;
}

// SICHER: Sicherheitskonfiguration erfordert Privileg
int secure_modify_security_config(uint32_t config) {
    if (get_current_privilege() < PRIVILEGE_KERNEL) {
        return -EPERM;
    }

    // Konfigurationswert validieren
    if (!is_valid_security_config(config)) {
        return -EINVAL;
    }

    volatile uint32_t* security_ctrl = (volatile uint32_t*)0x40001000;
    *security_ctrl = config;

    return 0;
}

CVE-Beispiele

  • RISC-V-Prozessorimplementierungen, bei denen das MEPC-CSR fälschlicherweise von Privilegprüfungen ausgeschlossen wurde, was Privilegieneskalation im Benutzermodus ermöglichte
  • Verschiedene SoC-Schwachstellen, bei denen kryptographische Schlüsselregister von unprivilegierter Software gelesen werden könnten

Verwandte CWEs

  • CWE-284: Unsachgemäße Zugriffskontrolle (Eltern)
  • CWE-1220: Unzureichende Granularität der Zugriffskontrolle (verwandt)
  • CAPEC-680: Ausnutzung unsachgemäß kontrollierter Register (Angriffsmuster)

Referenzen

  1. MITRE Corporation. "CWE-1262: Improper Access Control for Register Interface." https://cwe.mitre.org/data/definitions/1262.html
  2. RISC-V. "Privileged Architecture Specification"
  3. ARM. "TrustZone Security Technology"