Unsachgemäße endliche Zustandsautomaten (FSMs) in Hardware-Logik

Beschreibung

Unsachgemäße endliche Zustandsautomaten (FSMs) in Hardware-Logik tritt auf, wenn fehlerhafte endliche Zustandsautomaten in der Hardware-Logik einem Angreifer ermöglichen, das System in einen undefinierten Zustand zu versetzen, um einen Denial of Service (DoS) zu verursachen oder Berechtigungen auf dem System des Opfers zu erlangen. Die Sicherheit und Funktionalität von Systemen hängen stark von der FSM-Implementierung ab. Diese Automaten zeigen Systemsicherheitszustände an, und zahlreiche sichere Datenoperationen beruhen auf ihrer Genauigkeit. Fehlerhafte FSM-Designs, die bestimmte Zustände auslassen, können es Angreifern ermöglichen, Systeme in instabile, nicht wiederherstellbare Zustände zu treiben.

Risiko

Unsachgemäße FSM-Implementierung hat schwerwiegende Sicherheitsauswirkungen. Systeme können in undefinierte Zustände eintreten. Denial of Service kann durch Zustandsmanipulation auftreten. Sicherheitszustandsübergänge können umgangen werden. Privilege Escalation kann möglich sein. Systemabstürze oder -neustarts können auftreten. Deadlocks können Systeme unresponsiv machen. Authentifizierungszustände können umgehbar sein. Zugriffskontrollentscheidungen können kompromittiert werden.

Lösung

Definieren Sie alle möglichen Zustände und behandeln Sie alle unbenutzten Zustände durch Default-Anweisungen. Stellen Sie sicher, dass das System standardmäßig in einen sicheren Zustand wechselt. Verwenden Sie formale Verifikation zur Überprüfung der FSM-Vollständigkeit. Implementieren Sie Erkennung und Wiederherstellung nicht erreichbarer Zustände. Fügen Sie Watchdog-Timer für FSM-Timeouts hinzu. Protokollieren Sie unerwartete Zustandsübergänge. Testen Sie alle möglichen Eingabekombinationen. Verwenden Sie Kodierungen, die ungültige Zustände erkennen.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitUmfang: Verfügbarkeit

DoS: Absturz, Beendigung oder Neustart - Fehlerhafte FSMs können zu unerwarteten Systemzuständen führen, die Abstürze oder Neustarts verursachen.
ZugriffskontrolleUmfang: Zugriffskontrolle

Berechtigungen erlangen oder Identität annehmen - FSM-Schwachstellen können Privilege Escalation oder das Umgehen von Sicherheitszustanden ermöglichen.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: FSM mit fehlenden Zustandsbehandlern

module vulnerable_auth_fsm (
    input wire clk,
    input wire reset_n,
    input wire [2:0] input_event,
    output reg [1:0] current_state,
    output reg authenticated,
    output reg access_granted
);

    // Zustände
    parameter IDLE = 2'b00;
    parameter AUTHENTICATING = 2'b01;
    parameter AUTHENTICATED = 2'b10;
    // Zustand 2'b11 undefiniert - VERWUNDBAR!

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_state <= IDLE;
            authenticated <= 1'b0;
            access_granted <= 1'b0;
        end
        else begin
            case (current_state)
                IDLE: begin
                    if (input_event == 3'h1) begin
                        current_state <= AUTHENTICATING;
                    end
                end

                AUTHENTICATING: begin
                    if (input_event == 3'h2) begin
                        current_state <= AUTHENTICATED;
                        authenticated <= 1'b1;
                    end
                    else if (input_event == 3'h3) begin
                        current_state <= IDLE;
                    end
                end

                AUTHENTICATED: begin
                    access_granted <= 1'b1;
                    if (input_event == 3'h4) begin
                        current_state <= IDLE;
                        authenticated <= 1'b0;
                        access_granted <= 1'b0;
                    end
                end

                // VERWUNDBAR: Kein Handler für Zustand 2'b11
                // Wenn erreicht, ist das FSM-Verhalten undefiniert!
            endcase
        end
    end

endmodule

// VERWUNDBAR: Eingabekombinationen nicht vollständig behandelt
module vulnerable_security_fsm (
    input wire clk,
    input wire reset_n,
    input wire [2:0] security_input,
    output reg [2:0] security_state
);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            security_state <= 3'h0;
        end
        else begin
            case (security_input)
                3'h0: security_state <= 3'h0;  // Sicherer Zustand
                3'h1: security_state <= 3'h1;  // Zustand 1
                3'h2: security_state <= 3'h2;  // Zustand 2
                3'h3: security_state <= 3'h3;  // Zustand 3
                3'h4: security_state <= 3'h4;  // Zustand 4
                3'h5: security_state <= 3'h5;  // Zustand 5
                // VERWUNDBAR: Eingaben 3'h6 und 3'h7 nicht behandelt!
                // System geht in undefinierten Zustand
            endcase
        end
    end

endmodule

// VERWUNDBAR: FSM kann in Deadlock-Zustand eintreten
module vulnerable_protocol_fsm (
    input wire clk,
    input wire reset_n,
    input wire request,
    input wire response,
    input wire timeout,
    output reg [2:0] state,
    output reg transaction_complete
);

    parameter IDLE = 3'h0;
    parameter WAIT_ACK = 3'h1;
    parameter PROCESSING = 3'h2;
    parameter WAIT_RESPONSE = 3'h3;
    parameter COMPLETE = 3'h4;
    parameter ERROR = 3'h5;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            state <= IDLE;
            transaction_complete <= 1'b0;
        end
        else begin
            case (state)
                IDLE: begin
                    if (request) state <= WAIT_ACK;
                end

                WAIT_ACK: begin
                    if (response) state <= PROCESSING;
                    else if (timeout) state <= ERROR;
                end

                PROCESSING: begin
                    state <= WAIT_RESPONSE;
                end

                WAIT_RESPONSE: begin
                    if (response) begin
                        state <= COMPLETE;
                        transaction_complete <= 1'b1;
                    end
                    // VERWUNDBAR: Keine Timeout-Behandlung - kann hier blockieren!
                end

                COMPLETE: begin
                    state <= IDLE;
                    transaction_complete <= 1'b0;
                end

                ERROR: begin
                    // VERWUNDBAR: Kein Ausgang aus dem ERROR-Zustand!
                    // System dauerhaft im Fehlerzustand
                end
            endcase
        end
    end

endmodule
// VERWUNDBAR: Software-FSM mit fehlenden Fällen

typedef enum {
    STATE_INIT,
    STATE_AUTHENTICATING,
    STATE_AUTHENTICATED,
    STATE_ADMIN,
    STATE_COUNT  // 4 Zustände
} system_state_t;

system_state_t current_state = STATE_INIT;

void vulnerable_state_machine(int event) {
    switch (current_state) {
        case STATE_INIT:
            if (event == EVENT_LOGIN) {
                current_state = STATE_AUTHENTICATING;
            }
            break;

        case STATE_AUTHENTICATING:
            if (event == EVENT_AUTH_SUCCESS) {
                current_state = STATE_AUTHENTICATED;
            } else if (event == EVENT_AUTH_FAIL) {
                current_state = STATE_INIT;
            }
            break;

        case STATE_AUTHENTICATED:
            if (event == EVENT_LOGOUT) {
                current_state = STATE_INIT;
            } else if (event == EVENT_ADMIN_REQUEST) {
                current_state = STATE_ADMIN;
            }
            break;

        // VERWUNDBAR: STATE_ADMIN nicht behandelt!
        // Wenn erreicht, keine Zustandsübergänge möglich

        // VERWUNDBAR: Kein Default-Fall für ungültige Zustände
    }
}

Sichere Lösung

// SICHER: FSM mit allen Zuständen behandelt

module secure_auth_fsm (
    input wire clk,
    input wire reset_n,
    input wire [2:0] input_event,
    output reg [1:0] current_state,
    output reg authenticated,
    output reg access_granted,
    output reg fsm_error
);

    // Zustände
    parameter IDLE = 2'b00;
    parameter AUTHENTICATING = 2'b01;
    parameter AUTHENTICATED = 2'b10;
    parameter ERROR = 2'b11;  // Undefinierten Zustand explizit behandeln

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_state <= IDLE;
            authenticated <= 1'b0;
            access_granted <= 1'b0;
            fsm_error <= 1'b0;
        end
        else begin
            case (current_state)
                IDLE: begin
                    authenticated <= 1'b0;
                    access_granted <= 1'b0;
                    if (input_event == 3'h1) begin
                        current_state <= AUTHENTICATING;
                    end
                end

                AUTHENTICATING: begin
                    if (input_event == 3'h2) begin
                        current_state <= AUTHENTICATED;
                        authenticated <= 1'b1;
                    end
                    else if (input_event == 3'h3) begin
                        current_state <= IDLE;
                    end
                end

                AUTHENTICATED: begin
                    access_granted <= 1'b1;
                    if (input_event == 3'h4) begin
                        current_state <= IDLE;
                        authenticated <= 1'b0;
                        access_granted <= 1'b0;
                    end
                end

                // SICHER: Expliziter Handler für undefinierten/Fehlerzustand
                default: begin
                    // In sicheren Zustand zurückkehren
                    current_state <= IDLE;
                    authenticated <= 1'b0;
                    access_granted <= 1'b0;
                    fsm_error <= 1'b1;  // Fehler protokollieren
                end
            endcase
        end
    end

endmodule

// SICHER: Alle Eingabekombinationen behandelt
module secure_security_fsm (
    input wire clk,
    input wire reset_n,
    input wire [2:0] security_input,
    output reg [2:0] security_state,
    output reg invalid_input
);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            security_state <= 3'h0;
            invalid_input <= 1'b0;
        end
        else begin
            invalid_input <= 1'b0;

            case (security_input)
                3'h0: security_state <= 3'h0;
                3'h1: security_state <= 3'h1;
                3'h2: security_state <= 3'h2;
                3'h3: security_state <= 3'h3;
                3'h4: security_state <= 3'h4;
                3'h5: security_state <= 3'h5;
                // SICHER: Alle verbleibenden Eingaben behandeln
                default: begin
                    security_state <= 3'h0;  // In sicheren Zustand zurückkehren
                    invalid_input <= 1'b1;   // Ungültige Eingabe markieren
                end
            endcase
        end
    end

endmodule

// SICHER: FSM mit Timeout und Wiederherstellung
module secure_protocol_fsm (
    input wire clk,
    input wire reset_n,
    input wire request,
    input wire response,
    input wire timeout,
    input wire error_ack,
    output reg [2:0] state,
    output reg transaction_complete,
    output reg error_flag
);

    parameter IDLE = 3'h0;
    parameter WAIT_ACK = 3'h1;
    parameter PROCESSING = 3'h2;
    parameter WAIT_RESPONSE = 3'h3;
    parameter COMPLETE = 3'h4;
    parameter ERROR = 3'h5;

    // Timeout-Zähler für WAIT_RESPONSE
    reg [15:0] wait_counter;
    parameter WAIT_TIMEOUT = 16'hFFFF;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            state <= IDLE;
            transaction_complete <= 1'b0;
            error_flag <= 1'b0;
            wait_counter <= 16'h0;
        end
        else begin
            case (state)
                IDLE: begin
                    error_flag <= 1'b0;
                    wait_counter <= 16'h0;
                    if (request) state <= WAIT_ACK;
                end

                WAIT_ACK: begin
                    if (response) state <= PROCESSING;
                    else if (timeout) state <= ERROR;
                end

                PROCESSING: begin
                    state <= WAIT_RESPONSE;
                    wait_counter <= 16'h0;
                end

                WAIT_RESPONSE: begin
                    wait_counter <= wait_counter + 1;
                    if (response) begin
                        state <= COMPLETE;
                        transaction_complete <= 1'b1;
                    end
                    // SICHER: Timeout-Behandlung für WAIT_RESPONSE
                    else if (wait_counter >= WAIT_TIMEOUT) begin
                        state <= ERROR;
                        error_flag <= 1'b1;
                    end
                end

                COMPLETE: begin
                    state <= IDLE;
                    transaction_complete <= 1'b0;
                end

                ERROR: begin
                    error_flag <= 1'b1;
                    // SICHER: Ausgang aus ERROR-Zustand mit Bestätigung
                    if (error_ack) begin
                        state <= IDLE;
                        error_flag <= 1'b0;
                    end
                end

                // SICHER: Undefinierte Zustände behandeln
                default: begin
                    state <= IDLE;
                    error_flag <= 1'b1;
                end
            endcase
        end
    end

endmodule
// SICHER: Software-FSM mit allen Fällen behandelt

typedef enum {
    STATE_INIT,
    STATE_AUTHENTICATING,
    STATE_AUTHENTICATED,
    STATE_ADMIN,
    STATE_ERROR,
    STATE_COUNT
} system_state_t;

system_state_t current_state = STATE_INIT;

void secure_state_machine(int event) {
    switch (current_state) {
        case STATE_INIT:
            if (event == EVENT_LOGIN) {
                current_state = STATE_AUTHENTICATING;
            }
            break;

        case STATE_AUTHENTICATING:
            if (event == EVENT_AUTH_SUCCESS) {
                current_state = STATE_AUTHENTICATED;
            } else if (event == EVENT_AUTH_FAIL) {
                current_state = STATE_INIT;
            }
            break;

        case STATE_AUTHENTICATED:
            if (event == EVENT_LOGOUT) {
                current_state = STATE_INIT;
            } else if (event == EVENT_ADMIN_REQUEST) {
                current_state = STATE_ADMIN;
            }
            break;

        // SICHER: STATE_ADMIN behandelt
        case STATE_ADMIN:
            if (event == EVENT_LOGOUT) {
                current_state = STATE_INIT;
            } else if (event == EVENT_ADMIN_DONE) {
                current_state = STATE_AUTHENTICATED;
            }
            break;

        case STATE_ERROR:
            // Fehlerwiederherstellung
            if (event == EVENT_RESET) {
                current_state = STATE_INIT;
            }
            break;

        // SICHER: Default-Fall für ungültige Zustände
        default:
            log_error("Ungültiger FSM-Zustand: %d", current_state);
            current_state = STATE_INIT;  // In sicheren Zustand zurückkehren
            break;
    }
}

// FSM-Validierungsfunktion
bool validate_fsm_state(void) {
    if (current_state >= STATE_COUNT) {
        log_security_event("FSM in ungültigem Zustand: %d", current_state);
        current_state = STATE_INIT;
        return false;
    }
    return true;
}

CVE-Beispiele

FSM-Schwachstellen wurden in verschiedenen Hardware-Designs gefunden, bei denen undefinierte Zustände erreicht werden könnten, was zu Sicherheitsumgehungen oder Denial of Service führte.


Verwandte CWEs

  • CWE-684: Fehlerhafte Bereitstellung spezifizierter Funktionalität (übergeordnet)
  • CWE-1199: Allgemeine Schaltungs- und Logik-Design-Probleme (Kategoriemitglied)

Referenzen

  1. MITRE Corporation. "CWE-1245: Improper Finite State Machines (FSMs) in Hardware Logic." https://cwe.mitre.org/data/definitions/1245.html
  2. REF-1060: Farimah Farahmandi und Prabhat Mishra zur FSM-Anomalieerkennung