Verwendung eines vorhersagbaren Algorithmus im Zufallszahlengenerator

Beschreibung

Verwendung eines vorhersagbaren Algorithmus im Zufallszahlengenerator tritt auf, wenn ein Gerät einen Algorithmus verwendet, der Pseudozufallszahlen auf vorhersagbare Weise generiert. Pseudozufallszahlengeneratoren (PRNGs) haben endlich viele mögliche Zustände, was schließlich zu wiederholenden Mustern führt. Diese Vorhersagbarkeit ermöglicht verschiedene Angriffe wie Reverse Engineering oder Manipulation und kann die Zufälligkeit beeinträchtigen oder interne Zustände offenlegen. Sicherheitskritische Anwendungen erfordern echte Zufallszahlengeneratoren (TRNGs), die physikalische Phänomene nutzen.

Risiko

Vorhersagbare Zufallszahlen haben schwerwiegende Sicherheitsauswirkungen. Kryptographische Schlüssel können vorhersagbar sein. Sitzungstoken können erraten werden. Nonces können sich wiederholen, was Verschlüsselung bricht. Authentifizierungsmechanismen können umgehbar sein. Zukünftige Zufallswerte können aus vergangenen Beobachtungen berechnet werden. Verschlüsselung kann effektiv aufgehoben werden. PRNG-Zustand kann aus der Ausgabe wiederhergestellt werden.

Lösung

Verwenden Sie echte Zufallszahlengeneratoren (TRNGs) für sicherheitskritische Anwendungen. TRNGs nutzen physikalische Phänomene wie elektrisches Rauschen als Quellen zur Generierung von Zufallszahlen. Kombinieren Sie mehrere Entropiequellen für Robustheit. Verwenden Sie kryptographisch sichere PRNGs, die von TRNGs gesät werden. Implementieren Sie NIST SP 800-90A/B-konforme Zufallszahlengenerierung. Testen Sie Zufälligkeit mit statistischen Testsuiten. Vermeiden Sie Linear Feedback Shift Register (LFSRs) für Sicherheitszwecke.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Vertraulichkeit

Anwendungsdaten lesen – Vorhersagbare Zufallszahlen ermöglichen Angreifern die Bestimmung kryptographischer Schlüssel, Token und anderer sicherheitskritischer Werte. Hohe Wahrscheinlichkeit.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Linear Feedback Shift Register (LFSR) zur Zufallszahlengenerierung

module vulnerable_lfsr_rng (
    input wire clk,
    input wire reset_n,
    input wire request_random,
    output reg [31:0] random_output,
    output reg random_valid
);

    // VERWUNDBAR: LFSR ist deterministisch und vorhersagbar
    reg [31:0] lfsr_state;

    // Polynom: x^32 + x^22 + x^2 + x + 1
    wire feedback = lfsr_state[31] ^ lfsr_state[21] ^ lfsr_state[1] ^ lfsr_state[0];

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            // VERWUNDBAR: Fester Seed
            lfsr_state <= 32'h12345678;
            random_valid <= 1'b0;
        end
        else if (request_random) begin
            // VERWUNDBAR: Nächster Zustand vollständig durch aktuellen Zustand bestimmt
            lfsr_state <= {lfsr_state[30:0], feedback};
            random_output <= lfsr_state;
            random_valid <= 1'b1;
        end
    end

    // Angriff: 32 aufeinanderfolgende Ausgaben beobachten, um vollen Zustand wiederherzustellen
    // Alle zukünftigen Ausgaben können dann vorhergesagt werden

endmodule

// VERWUNDBAR: Einfacher zählerbasierter "Zufall"
module vulnerable_counter_rng (
    input wire clk,
    input wire request,
    output reg [31:0] random_output
);

    reg [31:0] counter;

    always @(posedge clk) begin
        counter <= counter + 1;

        if (request) begin
            // VERWUNDBAR: Nur ein Zähler - komplett vorhersagbar!
            random_output <= counter;
        end
    end

endmodule

// VERWUNDBAR: Nur zeitbasierter Seed
module vulnerable_time_seeded_rng (
    input wire clk,
    input wire reset_n,
    input wire [31:0] time_counter,
    output reg [31:0] random_output
);

    reg [31:0] lfsr;
    wire feedback = lfsr[31] ^ lfsr[22] ^ lfsr[2] ^ lfsr[1];

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            // VERWUNDBAR: Seed von Systemzeit - niedrige Entropie
            // Angreifer der ungefähre Reset-Zeit kennt, kann Seed erraten
            lfsr <= time_counter;
        end
        else begin
            lfsr <= {lfsr[30:0], feedback};
            random_output <= lfsr;
        end
    end

endmodule
// VERWUNDBAR: Standard-rand() für Sicherheitszwecke verwenden

#include <stdlib.h>
#include <time.h>

// VERWUNDBAR: rand() ist nicht kryptographisch sicher
void vulnerable_generate_token(char* token, size_t len) {
    srand(time(NULL));  // Vorhersagbarer Seed!

    for (size_t i = 0; i < len; i++) {
        token[i] = 'A' + (rand() % 26);  // Vorhersagbare Sequenz
    }
}

// VERWUNDBAR: PHP-artiges mt_rand() ist vorhersagbar
// CVE-2021-3692: PHP-Framework verwendet mt_rand() zur Token-Generierung
void vulnerable_session_id(char* session_id) {
    mt_srand(time(NULL));

    for (int i = 0; i < 32; i++) {
        session_id[i] = "0123456789abcdef"[mt_rand() % 16];
    }
}

// VERWUNDBAR: Linearer Kongruenzgenerator
uint32_t lcg_state = 1;

uint32_t vulnerable_lcg_random(void) {
    // Klassischer LCG - vollständig vorhersagbar aus jeder Ausgabe
    lcg_state = lcg_state * 1103515245 + 12345;
    return (lcg_state >> 16) & 0x7FFF;
}

Sichere Lösung

// SICHER: Echter Zufallszahlengenerator mit Ringoszillator

module secure_trng (
    input wire clk,
    input wire reset_n,
    input wire request_random,
    output reg [31:0] random_output,
    output reg random_valid,
    output reg entropy_low
);

    // Ringoszillator-Entropiequelle (vereinfacht)
    // Echte Implementierung verwendet mehrere Oszillatoren und Abtastung
    wire [7:0] ring_osc_outputs;

    // Mehrere Ringoszillatoren mit verschiedenen Verzögerungen
    ring_oscillator #(.STAGES(3)) ro0 (.out(ring_osc_outputs[0]));
    ring_oscillator #(.STAGES(5)) ro1 (.out(ring_osc_outputs[1]));
    ring_oscillator #(.STAGES(7)) ro2 (.out(ring_osc_outputs[2]));
    ring_oscillator #(.STAGES(9)) ro3 (.out(ring_osc_outputs[3]));
    ring_oscillator #(.STAGES(11)) ro4 (.out(ring_osc_outputs[4]));
    ring_oscillator #(.STAGES(13)) ro5 (.out(ring_osc_outputs[5]));
    ring_oscillator #(.STAGES(15)) ro6 (.out(ring_osc_outputs[6]));
    ring_oscillator #(.STAGES(17)) ro7 (.out(ring_osc_outputs[7]));

    // Entropie-Akkumulator
    reg [255:0] entropy_pool;
    reg [4:0] sample_count;

    // Gesundheitsprüfung
    reg [7:0] last_sample;
    reg [7:0] stuck_count;
    parameter STUCK_THRESHOLD = 100;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            entropy_pool <= 256'h0;
            sample_count <= 5'h0;
            random_valid <= 1'b0;
            entropy_low <= 1'b0;
            stuck_count <= 8'h0;
        end
        else begin
            // Ringoszillatoren abtasten
            if (ring_osc_outputs == last_sample) begin
                stuck_count <= stuck_count + 1;
                if (stuck_count > STUCK_THRESHOLD) begin
                    entropy_low <= 1'b1;  // Gesundheitsprüfung fehlgeschlagen
                end
            end else begin
                stuck_count <= 8'h0;
                entropy_low <= 1'b0;
            end
            last_sample <= ring_osc_outputs;

            // Entropie akkumulieren
            entropy_pool <= {entropy_pool[247:0], ring_osc_outputs};
            sample_count <= sample_count + 1;

            if (request_random && sample_count == 5'h1F && !entropy_low) begin
                // SICHER: Hardware-Entropie verwenden, korrekt aufbereitet
                random_output <= condition_entropy(entropy_pool);
                random_valid <= 1'b1;
                sample_count <= 5'h0;
            end else begin
                random_valid <= 1'b0;
            end
        end
    end

    // Entropie-Aufbereitung (vereinfacht - in der Praxis SHA-256 verwenden)
    function [31:0] condition_entropy;
        input [255:0] raw_entropy;
        begin
            // Kryptographische Aufbereitung zum Entfernen von Verzerrungen
            condition_entropy = sha256_compress(raw_entropy)[31:0];
        end
    endfunction

endmodule

// SICHER: CSPRNG gespeist von TRNG
module secure_csprng (
    input wire clk,
    input wire reset_n,
    input wire [255:0] trng_seed,
    input wire seed_valid,
    input wire request_random,
    output reg [31:0] random_output,
    output reg random_valid
);

    // AES-CTR oder ChaCha20 als CSPRNG verwenden
    reg [255:0] state;
    reg [127:0] counter;
    reg seeded;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            state <= 256'h0;
            counter <= 128'h0;
            seeded <= 1'b0;
            random_valid <= 1'b0;
        end
        else if (seed_valid) begin
            // Von TRNG seeden
            state <= trng_seed;
            seeded <= 1'b1;
        end
        else if (request_random && seeded) begin
            // Mit AES-CTR generieren
            random_output <= aes_encrypt(counter, state[127:0]) ^
                            aes_encrypt(counter + 1, state[255:128]);
            counter <= counter + 2;
            random_valid <= 1'b1;

            // Periodisch neu seeden
            if (counter[31:0] == 32'hFFFFFFFF) begin
                seeded <= 1'b0;  // Neuen Seed anfordern
            end
        end else begin
            random_valid <= 1'b0;
        end
    end

endmodule
// SICHER: Kryptographisch sichere Zufallszahlengenerierung

#include <stdint.h>
#include <stdbool.h>

// Auf Linux/Unix
#include <sys/random.h>

// SICHER: Betriebssystem-bereitgestellten CSPRNG verwenden
int secure_generate_random(uint8_t* buffer, size_t len) {
    // getrandom() verwendet Kernel-Entropiepool
    ssize_t result = getrandom(buffer, len, 0);
    return (result == (ssize_t)len) ? 0 : -1;
}

// SICHER: OpenSSL CSPRNG
#include <openssl/rand.h>

int secure_generate_token(char* token, size_t len) {
    uint8_t random_bytes[len];

    if (RAND_bytes(random_bytes, len) != 1) {
        return -1;  // Fehler
    }

    for (size_t i = 0; i < len; i++) {
        token[i] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
                   [random_bytes[i] % 62];
    }

    // Sensible Daten löschen
    OPENSSL_cleanse(random_bytes, len);
    return 0;
}

// SICHER: Kryptographisch sichere Session-ID generieren
int secure_session_id(char* session_id, size_t len) {
    uint8_t random_bytes[len / 2];

    if (RAND_bytes(random_bytes, sizeof(random_bytes)) != 1) {
        return -1;
    }

    for (size_t i = 0; i < sizeof(random_bytes); i++) {
        sprintf(session_id + (i * 2), "%02x", random_bytes[i]);
    }

    OPENSSL_cleanse(random_bytes, sizeof(random_bytes));
    return 0;
}

// SICHER: Hardware-RNG-Zugriff (falls verfügbar)
#ifdef __x86_64__
#include <immintrin.h>

int secure_hardware_random(uint64_t* value) {
    int retries = 10;

    while (retries--) {
        if (_rdrand64_step(value)) {
            return 0;
        }
    }
    return -1;  // RDRAND fehlgeschlagen
}
#endif

// Windows
#ifdef _WIN32
#include <bcrypt.h>

int secure_generate_random_win(uint8_t* buffer, size_t len) {
    NTSTATUS status = BCryptGenRandom(NULL, buffer, len,
                                       BCRYPT_USE_SYSTEM_PREFERRED_RNG);
    return BCRYPT_SUCCESS(status) ? 0 : -1;
}
#endif

CVE-Beispiele

  • CVE-2021-3692: PHP-Framework verwendet mt_rand() zur Token-Generierung
  • Verschiedene Schwachstellen in IoT-Geräten, die LFSRs für Sicherheit verwenden

Verwandte CWEs

  • CWE-330: Use of Insufficiently Random Values (übergeordnet)
  • CWE-1213: Random Number Issues (Kategoriemitglied)
  • CWE-1205: Security Primitives and Cryptography Issues (Kategoriemitglied)
  • CWE-338: Use of Cryptographically Weak PRNG (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1241: Use of Predictable Algorithm in Random Number Generator." https://cwe.mitre.org/data/definitions/1241.html
  2. NIST SP 800-90A: Recommendation for Random Number Generation Using Deterministic RBGs
  3. NIST SP 800-90B: Recommendation for the Entropy Sources Used for Random Bit Generation
  4. REF-1370: LFSR-Implementierung im OpenPiton SoC (HACK@DAC'21)