Fehlender kryptografischer Schritt
Beschreibung
Fehlender kryptografischer Schritt ist eine Schwachstelle, die auftritt, wenn ein Produkt einen erforderlichen Schritt in einem kryptografischen Algorithmus nicht implementiert, was zu schwächerer Verschlüsselung oder Sicherheit führt, als der Algorithmus bieten soll. Dies kann passieren, wenn Entwickler rechenintensive Schritte auslassen, um die Leistung zu verbessern, Algorithmusanforderungen missverstehen oder mit unvollständigen Spezifikationen arbeiten. Die resultierende Implementierung scheint zu funktionieren, bietet aber reduzierte Sicherheitsgarantien. Beispiele umfassen HMAC-Implementierungen, die keine Nachrichten verarbeiten können, die länger als ein einzelner Block sind, Verschlüsselung ohne ordnungsgemäße Initialisierungsvektor-Handhabung oder Signaturverifizierung, die kritische Validierungsschritte überspringt.
Risiko
Fehlende kryptografische Schritte können die Sicherheit katastrophal schwächen, während sie korrekt zu funktionieren scheinen. Die unvollständige Implementierung kann grundlegende Funktionstests bestehen, während sie für kryptografische Angriffe anfällig ist. Ausgelassene Schritte umfassen oft solche, die kritische Sicherheitseigenschaften bieten - das Überspringen eines Authentifizierungsschritts entfernt den Integritätsschutz, das Auslassen von Schlüsselableitungsschritten kann zu vorhersagbaren Schlüsseln führen, und fehlende Verifizierungsschritte können ermöglichen, dass gefälschte Daten akzeptiert werden. Das Risiko wird verstärkt, weil diese Schwachstellen durch normale Tests möglicherweise nicht erkennbar sind und kryptografische Expertise erfordern, um identifiziert zu werden. Angreifer mit Kenntnis der spezifischen Auslassung können sie ausnutzen und möglicherweise die Verschlüsselung vollständig brechen oder die Authentifizierung umgehen. Die Auswirkung hängt davon ab, welcher Schritt ausgelassen wurde, kann aber von verminderter Sicherheit bis zum vollständigen kryptografischen Versagen reichen.
Lösung
Implementieren Sie kryptografische Algorithmen genau wie in ihren Standards spezifiziert, ohne Optimierungsabkürzungen, die Schritte überspringen. Verwenden Sie gut getestete kryptografische Bibliotheken anstatt Algorithmen von Grund auf neu zu implementieren. Wenn Sie kryptografische Primitive implementieren, folgen Sie den Spezifikationen genau und verifizieren Sie Implementierungen gegen Testvektoren. Führen Sie kryptografische Code-Reviews durch Sicherheitsexperten durch, die die Algorithmusanforderungen verstehen. Testen Sie Implementierungen mit Randfällen einschließlich sehr länger Nachrichten, leerer Eingaben und Grenzbedingungen. Vermeiden Sie die Versuchung, "teure" Schritte für Leistung zu überspringen - wenn Leistung kritisch ist, wählen Sie schnellere Algorithmen anstatt Implementierungen zu schwächen. Dokumentieren Sie alle Abweichungen von Standardimplementierungen und analysieren Sie deren Sicherheitsauswirkungen. Verwenden Sie formale Verifizierungsmethoden für kritischen kryptografischen Code, wo möglich.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Umfang: Zugriffskontrolle Fehlende Authentifizierungs- oder Verifizierungsschritte können Angreifern ermöglichen, Schutzmechanismen zu umgehen und gültig aussehende Daten oder Anmeldedaten zu fälschen. |
| Vertraulichkeit, Integrität | Umfang: Vertraulichkeit, Integrität Unvollständige Verschlüsselungsimplementierungen können anfällig für kryptanalytische Angriffe sein, die Klartext wiederherstellen. Fehlende Integritätsschritte ermöglichen unerkannte Datenmodifikation. |
| Nicht-Abstreitbarkeit | Umfang: Nicht-Abstreitbarkeit Fehlende Schritte in Implementierungen digitaler Signaturen können Signaturfälschung ermöglichen, was die Rechenschaftspflicht untergräbt und Angreifern ermöglicht, ihre Aktivitäten zu verbergen. |
Beispielcode
Anfälliger Code (Verilog/C)
Die folgenden Beispiele demonstrieren fehlende kryptografische Schritte:
// Anfällig: HMAC-Implementierung ohne iteratives Hashing für lange Nachrichten
module vulnerable_hmac_engine(
input wire clk,
input wire rst,
input wire [255:0] key,
input wire [511:0] message, // Kann nur 512 Bits verarbeiten!
input wire start,
output reg [255:0] hmac_out,
output reg done
);
// Anfällig: Kann keine Nachrichten länger als 512 Bits verarbeiten
// HMAC erfordert iteratives Hashing für längere Nachrichten
reg [255:0] inner_hash;
reg [255:0] outer_hash;
always @(posedge clk) begin
if (rst) begin
done <= 0;
end else if (start) begin
// Schlüssel mit ipad XOR-verknüpfen
// Hash von (key XOR ipad) || message
inner_hash <= sha256(key ^ 256'h3636...36, message);
// Schlüssel mit opad XOR-verknüpfen
// Hash von (key XOR opad) || inner_hash
outer_hash <= sha256(key ^ 256'h5c5c...5c, inner_hash);
hmac_out <= outer_hash;
done <= 1;
// Anfällig: Nachrichten > 512 Bits werden abgeschnitten oder verursachen Fehler!
// Die iterative Funktion zur Verarbeitung von Nachrichtenblöcken fehlt
end
end
endmodule
// Anfällig: Fehlende Nachrichtenpolsterung in Hash-Funktion
#include <string.h>
#include <stdint.h>
// Anfällig: SHA-256-Implementierung ohne Padding-Schritt
void vulnerable_sha256(const uint8_t *message, size_t len, uint8_t *hash) {
uint32_t state[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};
// Vollständige 512-Bit-Blöcke verarbeiten
size_t blocks = len / 64;
for (size_t i = 0; i < blocks; i++) {
sha256_transform(state, message + (i * 64));
}
// Anfällig: Padding-Schritt fehlt!
// SHA-256 erfordert:
// 1. Bit '1' an Nachricht anhängen
// 2. Nullen anhängen bis Nachricht 448 Bits mod 512 ist
// 3. Originale Nachrichtenlänge als 64-Bit Big-Endian anhängen
// Kopiert nur den Zustand ohne ordnungsgemäße Finalisierung
memcpy(hash, state, 32);
// Ergebnis ist falsch für Nachrichten, die nicht genau ein Vielfaches von 64 Bytes sind!
}
// Anfällig: RSA-Signatur ohne ordnungsgemäßes Padding
int vulnerable_rsa_sign(const uint8_t *message, size_t msg_len,
const RSA_KEY *key, uint8_t *signature) {
uint8_t hash[32];
sha256(message, msg_len, hash);
// Anfällig: Direkte modulare Exponentiation ohne Padding!
// PKCS#1 v1.5 oder PSS-Padding fehlt
// Dies ermöglicht Signaturfälschungsangriffe
// Sollte sein: padded = EMSA_PKCS1_v1_5_ENCODE(hash, key_size)
// Stattdessen nur roher Hash verwendet
bignum_mod_exp(signature, hash, key->d, key->n);
return 0;
}
// Anfällig: Fehlende Schlüsselableitungsfunktion
int vulnerable_derive_key(const char *password, uint8_t *key) {
// Anfällig: Direkter Hash des Passworts
// Ordnungsgemäße KDF fehlt (PBKDF2, scrypt, argon2)
sha256(password, strlen(password), key);
// Kein Salt! Keine Iterationen! Anfällig für Rainbow Tables!
return 0;
}
# Anfällig: Fehlende Authentifizierungs-Tag-Verifizierung
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def vulnerable_decrypt_gcm(key, nonce, ciphertext, tag):
# Anfällig: Entschlüsseln ohne Verifizierung des Authentifizierungs-Tags!
cipher = Cipher(algorithms.AES(key), modes.GCM(nonce))
decryptor = cipher.decryptor()
# Nur entschlüsseln ohne zu authentifizieren
plaintext = decryptor.update(ciphertext)
# Anfällig: Tag wird nie verifiziert!
# Sollte aufrufen: decryptor.finalize_with_tag(tag)
# Das Fehlen dieses Schritts ermöglicht, dass modifizierter Geheimtext akzeptiert wird
return plaintext # Könnten gefälschte Daten sein!
# Anfällig: Challenge-Response ohne Challenge-Verifizierung
def vulnerable_authenticate(client_socket, expected_public_key):
# Challenge generieren
challenge = os.urandom(32)
client_socket.send(challenge)
# Antwort empfangen
response = client_socket.recv(256)
# Anfällig: Fehlende Verifizierung dass Antwort für DIESE Challenge ist!
# Sollte verifizieren: signature = sign(challenge, private_key)
# Prüft stattdessen nur ob irgendeine gültige Signatur
try:
expected_public_key.verify(response, b'anything') # Falsch!
return True
except:
return False
Korrigierter Code (Verilog/C)
// Korrigiert: HMAC-Implementierung mit iterativem Hashing
module secure_hmac_engine(
input wire clk,
input wire rst,
input wire [255:0] key,
input wire [7:0] message_byte,
input wire message_valid,
input wire message_last,
input wire start,
output reg [255:0] hmac_out,
output reg done
);
// Korrigiert: Implementiert iteratives Hashing für Nachrichten beliebiger Länge
reg [511:0] block_buffer;
reg [6:0] block_offset;
reg [255:0] hash_state;
reg processing_inner;
// SHA-256 Initialisierungszustand
localparam [255:0] SHA256_INIT = {
32'h6a09e667, 32'hbb67ae85, 32'h3c6ef372, 32'ha54ff53a,
32'h510e527f, 32'h9b05688c, 32'h1f83d9ab, 32'h5be0cd19
};
always @(posedge clk) begin
if (rst) begin
done <= 0;
block_offset <= 0;
hash_state <= SHA256_INIT;
end else if (start) begin
// Mit (key XOR ipad) initialisieren
block_buffer[511:256] <= key ^ 256'h3636363636...;
block_offset <= 32;
processing_inner <= 1;
end else if (message_valid && processing_inner) begin
// Korrigiert: Nachrichtenbytes akkumulieren
block_buffer[511 - (block_offset * 8) -: 8] <= message_byte;
block_offset <= block_offset + 1;
// Korrigiert: Vollständige Blöcke iterativ verarbeiten
if (block_offset == 63) begin
hash_state <= sha256_transform(hash_state, block_buffer);
block_offset <= 0;
end
end else if (message_last && processing_inner) begin
// Korrigiert: Ordnungsgemäßes Padding anwenden
hash_state <= sha256_finalize(hash_state, block_buffer,
block_offset, total_length);
// Äußeren Hash starten
processing_inner <= 0;
// (key XOR opad) || inner_hash verarbeiten
end
// ... äußeren Hash ähnlich vervollständigen
end
endmodule
// Korrigiert: SHA-256 mit ordnungsgemäßem Padding
#include <string.h>
#include <stdint.h>
void secure_sha256(const uint8_t *message, size_t len, uint8_t *hash) {
uint32_t state[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};
// Vollständige 512-Bit-Blöcke verarbeiten
size_t blocks = len / 64;
for (size_t i = 0; i < blocks; i++) {
sha256_transform(state, message + (i * 64));
}
// Korrigiert: Ordnungsgemäße Padding-Implementierung
uint8_t padding_block[128] = {0}; // Kann 2 Blöcke benötigen
size_t remaining = len % 64;
// Verbleibende Bytes kopieren
memcpy(padding_block, message + (blocks * 64), remaining);
// Korrigiert: Bit '1' anhängen
padding_block[remaining] = 0x80;
// Korrigiert: Bestimmen ob ein oder zwei Padding-Blöcke nötig sind
size_t padding_blocks;
if (remaining >= 56) {
padding_blocks = 2; // Extra-Block für Länge nötig
} else {
padding_blocks = 1;
}
// Korrigiert: Länge als 64-Bit Big-Endian am Ende anhängen
uint64_t bit_len = len * 8;
size_t len_offset = (padding_blocks * 64) - 8;
for (int i = 0; i < 8; i++) {
padding_block[len_offset + i] = (bit_len >> (56 - i * 8)) & 0xff;
}
// Padding-Block(s) verarbeiten
for (size_t i = 0; i < padding_blocks; i++) {
sha256_transform(state, padding_block + (i * 64));
}
// Korrigiert: Ordnungsgemäße Ausgabeformatierung
for (int i = 0; i < 8; i++) {
hash[i*4 + 0] = (state[i] >> 24) & 0xff;
hash[i*4 + 1] = (state[i] >> 16) & 0xff;
hash[i*4 + 2] = (state[i] >> 8) & 0xff;
hash[i*4 + 3] = state[i] & 0xff;
}
}
// Korrigiert: RSA-Signatur mit ordnungsgemäßem Padding
int secure_rsa_sign(const uint8_t *message, size_t msg_len,
const RSA_KEY *key, uint8_t *signature) {
uint8_t hash[32];
sha256(message, msg_len, hash);
// Korrigiert: PKCS#1 PSS-Padding anwenden
uint8_t padded[key->size];
if (pkcs1_pss_encode(hash, sizeof(hash), key->size, padded) != 0) {
return -1;
}
// Die ordnungsgemäß gepolsterte Nachricht signieren
bignum_mod_exp(signature, padded, key->d, key->n);
return 0;
}
// Korrigiert: Ordnungsgemäße Schlüsselableitung
int secure_derive_key(const char *password, const uint8_t *salt,
size_t salt_len, uint8_t *key) {
// Korrigiert: Ordnungsgemäße KDF mit Salt und Iterationen verwenden
return PKCS5_PBKDF2_HMAC(
password, strlen(password),
salt, salt_len,
100000, // Iterationen
EVP_sha256(),
32, // Schlüssellänge
key
);
}
# Korrigiert: Ordnungsgemäße GCM-Entschlüsselung mit Tag-Verifizierung
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def secure_decrypt_gcm(key, nonce, ciphertext, tag):
cipher = Cipher(algorithms.AES(key), modes.GCM(nonce, tag))
decryptor = cipher.decryptor()
plaintext = decryptor.update(ciphertext)
# Korrigiert: Authentifizierungs-Tag während Finalisierung verifizieren
# Dies wird InvalidTag auslösen wenn Authentifizierung fehlschlägt
decryptor.finalize()
return plaintext
# Korrigiert: Challenge-Response mit ordnungsgemäßer Verifizierung
def secure_authenticate(client_socket, expected_public_key):
# Einzigartige Challenge generieren
challenge = os.urandom(32)
client_socket.send(challenge)
# Antwort empfangen
signature = client_socket.recv(256)
# Korrigiert: Verifizieren dass Signatur spezifisch für DIESE Challenge ist
try:
expected_public_key.verify(
signature,
challenge, # Die tatsächlichen Challenge-Daten
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return True
except InvalidSignature:
return False
Die Korrektur implementiert alle erforderlichen kryptografischen Schritte einschließlich Padding, Verifizierung und ordnungsgemäßer Schlüsselableitung.
Ausgenutzt in der Praxis
SSH-Authentifizierungs-Bypass (Verschiedene, 2001)
CVE-2001-1585 dokumentierte eine SSH-Implementierung ohne Challenge-Response-Verifizierung, die Authentifizierungs-Bypass unter Verwendung nur des öffentlichen Schlüssels ermöglichte.
Unvollständige HMAC-Implementierungen (Verschiedene Hardware)
Hardware-Implementierungen mit begrenztem Speicher haben HMAC-Engines ausgeliefert, die keine Nachrichten verarbeiten können, die größer als ihre Puffergröße sind, was die Authentifizierung schwächt.
Tools zum Testen/Ausnutzen
-
Cryptographic Test Vectors — NIST-Testvektoren zur Validierung von Implementierungen.
-
Wycheproof — Googles Testvektoren für kryptografische Bibliotheken.
-
Cryptofuzz — Differentielles Fuzzing für kryptografische Bibliotheken.
CVE-Beispiele
- CVE-2001-1585 — Fehlender Challenge-Response-Schritt in SSH-Authentifizierung.
Referenzen
-
MITRE Corporation. "CWE-325: Missing Cryptographic Step." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/325.html
-
NIST. "Secure Hash Standard." FIPS 180-4. https://csrc.nist.gov/publications/detail/fips/180/4/final
-
RFC 2104. "HMAC: Keyed-Hashing for Message Authentication." https://tools.ietf.org/html/rfc2104