Verwendung mehrerer Ressourcen mit doppeltem Bezeichner
Beschreibung
Die Verwendung mehrerer Ressourcen mit doppeltem Bezeichner ist eine Schwachstelle, bei der Software mehrere Ressourcen verwendet, die denselben Bezeichner in einem Kontext teilen, der eindeutige Bezeichner erfordert. Das Produkt geht davon aus, dass jede Ressource einen eindeutigen Bezeichner hat, erzwingt diese Annahme jedoch nicht. Dies führt zu Situationen, in denen Operationen, die für eine Ressource bestimmt sind, eine andere Ressource mit demselben Bezeichner betreffen können. Dies kann zu Sicherheitsumgehungen, Datenkorruption oder unvorhersehbarem Anwendungsverhalten führen, wenn das System nicht zwischen Ressourcen unterscheiden kann.
Risiko
Doppelte Bezeichner schaffen erhebliche Sicherheits- und Zuverlässigkeitsrisiken. Angreifer können diese Schwachstelle ausnutzen, um Sicherheitskontrollen zu umgehen, die Eindeutigkeit der Bezeichner voraussetzen - beispielsweise durch Erstellen einer bösartigen Ressource mit demselben Bezeichner wie eine legitime, wodurch das System die falsche Ressource verwendet. In Validierungsszenarien können doppelte Formularnamen oder Konfigurationseinträge dazu führen, dass das System die Validierung vollständig überspringt oder falsche Regeln anwendet. Dateisysteme mit doppelten Dateinamen in Archiven können dazu führen, dass Dateien überschrieben oder die falsche Datei ausgeführt wird. Das Risiko ist besonders schwerwiegend, wenn Bezeichner für Zugriffskontrollentscheidungen verwendet werden.
Lösung
Validieren Sie, dass Bezeichner eindeutig sind, bevor neue Ressourcen akzeptiert werden. Implementieren Sie Eindeutigkeitseinschränkungen auf Datenbank- oder Speicherebene. Wenn doppelte Bezeichner erkannt werden, verweigern Sie die Operation auf jeder Ressource mit einem nicht eindeutigen Bezeichner und melden Sie den Fehler entsprechend. Verwenden Sie UUIDs oder andere garantiert eindeutige Bezeichnerschemata, wo möglich. Erzwingen Sie Eindeutigkeit während der Ressourcenerstellung, anstatt sie vorauszusetzen. Protokollieren und alarmieren Sie bei Erkennung doppelter Bezeichner, da dies auf einen Angriffsversuch hinweisen kann.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Bereich: Zugriffskontrolle Umgehung des Schutzmechanismus - Sicherheitsprüfungen, die auf Annahmen eindeutiger Bezeichner basieren, können umgangen werden. |
| Integrität | Bereich: Integrität Änderung von Anwendungsdaten - Operationen können unbeabsichtigte Ressourcen betreffen, wenn Bezeichner kollidieren. |
| Sonstiges | Bereich: Sonstiges Qualitätsverschlechterung - Das Anwendungsverhalten wird unvorhersehbar, wenn Ressourcen nicht eindeutig identifiziert werden können. |
Beispielcode
Verwundbarer Code
<!-- Verwundbar: Struts-Konfiguration mit doppelten Formularnamen -->
<form-validation>
<formset>
<!-- Erste ProjectForm-Definition mit strenger Validierung -->
<form name="ProjectForm">
<field property="name" depends="required,maxlength">
<arg0 key="form.project.name"/>
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<var>
<var-name>maxlength</var-name>
<var-value>100</var-value>
</var>
</field>
<field property="budget" depends="required,double">
<arg0 key="form.project.budget"/>
</field>
</form>
<!-- Verwundbar: Doppelter Formularname mit schwächerer Validierung -->
<form name="ProjectForm">
<!-- Dieses Formular hat keine Validierungsregeln! -->
<!-- Struts könnte dieses verwenden und die Validierung umgehen -->
</form>
</formset>
</form-validation>
// Verwundbar: Java-Map mit doppelten Schlüsseln
import java.util.*;
public class VulnerableConfig {
// Verwundbar: Konfigurationsladen erkennt keine Duplikate
public Map<String, String> loadConfig(List<ConfigEntry> entries) {
Map<String, String> config = new HashMap<>();
for (ConfigEntry entry : entries) {
// Verwundbar: Spätere Duplikate überschreiben frühere Werte still
config.put(entry.getKey(), entry.getValue());
// Angreifer-kontrollierter letzter Eintrag für einen Schlüssel gewinnt
}
return config;
}
// Verwundbar: Benutzersuche mit doppelten Benutzernamen
public User findUser(String username) {
// Wenn mehrere Benutzer denselben Benutzernamen haben, welcher wird zurückgegeben?
List<User> users = database.query(
"SELECT * FROM users WHERE username = ?", username);
// Verwundbar: Gibt nur den ersten Treffer zurück
return users.isEmpty() ? null : users.get(0);
// Angreifer könnte Duplikat erstellt haben, um Daten eines anderen Benutzers zu erhalten
}
}
# Verwundbar: Archivextraktion mit doppelten Dateinamen (CVE-2013-4787-Muster)
import zipfile
import os
def vulnerable_extract_archive(archive_path, dest_dir):
with zipfile.ZipFile(archive_path, 'r') as zf:
for info in zf.infolist():
# Verwundbar: Keine Prüfung auf doppelte Dateinamen
# Zweite Datei mit gleichem Namen überschreibt erste
zf.extract(info, dest_dir)
# Angriffsszenario:
# Archiv enthält:
# legitimate_app.dll (signiert, verifiziert)
# legitimate_app.dll (unsigniert, bösartig)
#
# Verifizierungssystem:
# 1. Verifiziert erste legitimate_app.dll - besteht Signaturprüfung
# 2. Extrahiert zweite legitimate_app.dll - überschreibt verifizierte
# 3. Führt bösartige unsignierte Version aus
def vulnerable_verify_and_install(archive_path):
# Verifiziere alle Dateien im Archiv
for filename in get_archive_files(archive_path):
if not verify_signature(archive_path, filename):
raise SecurityError("Ungültige Signatur")
# Verwundbar: Extrahieren nach Verifizierung
# Doppelte Dateinamen verursachen Überschreiben mit unverifiziertem Inhalt
extract_archive(archive_path, INSTALL_DIR)
// Verwundbar: Ressourcenpool mit doppelten IDs
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char* name;
int permission_level;
} Resource;
Resource resources[100];
int resource_count = 0;
// Verwundbar: Keine Eindeutigkeitsprüfung der Ressourcen-ID
int add_resource(int id, char* name, int perm_level) {
// Verwundbar: Erlaubt doppelte IDs
resources[resource_count].id = id;
resources[resource_count].name = strdup(name);
resources[resource_count].permission_level = perm_level;
resource_count++;
return 0;
}
// Verwundbar: Find gibt ersten Treffer zurück, ignoriert Duplikate
Resource* find_resource(int id) {
for (int i = 0; i < resource_count; i++) {
if (resources[i].id == id) {
return &resources[i]; // Erster Treffer zurückgegeben
}
}
return NULL;
}
// Angriff: Erstelle Ressource mit hoher Berechtigung mit gleicher ID wie eine mit niedriger Berechtigung
// Wenn Prüfungen find_resource verwenden und die mit hoher Berechtigung zurückgegeben wird...
// Oder umgekehrt - erstelle Version mit niedriger Berechtigung, um Prüfungen zu umgehen
Korrigierter Code
<!-- Korrigiert: Eindeutige Formularnamen durch Schema oder Validierung erzwungen -->
<form-validation>
<formset>
<form name="ProjectForm">
<field property="name" depends="required,maxlength">
<arg0 key="form.project.name"/>
<arg1 name="maxlength" key="${var:maxlength}" resource="false"/>
<var>
<var-name>maxlength</var-name>
<var-value>100</var-value>
</var>
</field>
<field property="budget" depends="required,double">
<arg0 key="form.project.budget"/>
</field>
</form>
<!-- Unterschiedliche Namen für unterschiedliche Formulare -->
<form name="ProjectFormSimple">
<!-- Vereinfachte Validierung für anderen Kontext -->
</form>
</formset>
</form-validation>
<!-- Verwende Validierungstool, das Duplikate beim Start erkennt -->
// Korrigiert: Duplikate erkennen und ablehnen
import java.util.*;
public class SecureConfig {
// Korrigiert: Doppelte Schlüssel erkennen
public Map<String, String> loadConfigSecure(List<ConfigEntry> entries)
throws DuplicateKeyException {
Map<String, String> config = new HashMap<>();
for (ConfigEntry entry : entries) {
String key = entry.getKey();
// Korrigiert: Auf vorhandenen Schlüssel prüfen vor dem Hinzufügen
if (config.containsKey(key)) {
throw new DuplicateKeyException(
"Doppelter Konfigurationsschlüssel: " + key);
}
config.put(key, entry.getValue());
}
return config;
}
// Korrigiert: Eindeutigkeit auf Datenbankebene erzwingen
public User findUserSecure(String username) throws DuplicateUserException {
List<User> users = database.query(
"SELECT * FROM users WHERE username = ?", username);
// Korrigiert: Duplikate erkennen und melden
if (users.size() > 1) {
throw new DuplicateUserException(
"Mehrere Benutzer mit Benutzernamen: " + username);
}
return users.isEmpty() ? null : users.get(0);
}
// Korrigiert: Datenbankeinschränkung stellt Eindeutigkeit sicher
public void createUser(String username) {
// Datenbank hat UNIQUE-Einschränkung auf Benutzernamen-Spalte
// INSERT schlägt fehl, wenn Duplikat existiert
database.execute(
"INSERT INTO users (username) VALUES (?)", username);
}
}
# Korrigiert: Sichere Archivextraktion mit Duplikaterkennung
import zipfile
import os
def secure_extract_archive(archive_path, dest_dir):
with zipfile.ZipFile(archive_path, 'r') as zf:
# Korrigiert: Set von Dateinamen erstellen, um Duplikate zu erkennen
seen_names = set()
for info in zf.infolist():
normalized_name = os.path.normpath(info.filename)
# Korrigiert: Auf doppelte Dateinamen prüfen
if normalized_name in seen_names:
raise SecurityError(
f"Doppelter Dateiname im Archiv: {normalized_name}")
seen_names.add(normalized_name)
# Nur extrahieren, nachdem keine Duplikate verifiziert wurden
zf.extractall(dest_dir)
# Korrigiert: Atomares Verifizieren-und-Extrahieren
def secure_verify_and_install(archive_path):
# Zuerst auf Duplikate prüfen
filenames = get_archive_files(archive_path)
if len(filenames) != len(set(filenames)):
raise SecurityError("Archiv enthält doppelte Dateinamen")
# Alle Dateien verifizieren
for filename in filenames:
if not verify_signature(archive_path, filename):
raise SecurityError(f"Ungültige Signatur: {filename}")
# Nach Verifizierung extrahieren - keine Duplikate, die Überschreiben verursachen
extract_archive(archive_path, INSTALL_DIR)
// Korrigiert: Eindeutige Ressourcen-IDs erzwungen
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char* name;
int permission_level;
} Resource;
Resource resources[100];
int resource_count = 0;
// Korrigiert: Eindeutigkeit vor dem Hinzufügen prüfen
int add_resource_secure(int id, char* name, int perm_level) {
// Korrigiert: Prüfen, ob ID bereits existiert
for (int i = 0; i < resource_count; i++) {
if (resources[i].id == id) {
return -1; // Fehler: doppelte ID
}
}
resources[resource_count].id = id;
resources[resource_count].name = strdup(name);
resources[resource_count].permission_level = perm_level;
resource_count++;
return 0;
}
// Korrigiert: Eindeutigkeit verifizieren und Duplikate behandeln
Resource* find_resource_secure(int id, int* count) {
Resource* result = NULL;
*count = 0;
for (int i = 0; i < resource_count; i++) {
if (resources[i].id == id) {
if (result == NULL) {
result = &resources[i];
}
(*count)++;
}
}
// Aufrufer kann count prüfen, um Duplikate zu erkennen
return result;
}
int access_resource(int id) {
int count;
Resource* res = find_resource_secure(id, &count);
// Korrigiert: Operation verweigern, wenn Duplikate existieren
if (count > 1) {
log_error("Doppelte Ressourcen-ID erkannt: %d", id);
return -1;
}
if (res == NULL) {
return -1;
}
return process_resource(res);
}
CVE-Beispiele
- CVE-2013-4787: Android "Master Key"-Schwachstelle - das mobile Betriebssystem verifizierte kryptographische Signaturen auf archivierten Dateien, installierte aber andere Dateien mit identischen Namen.
- CVE-2017-12617: Apache Tomcat Schwachstelle bei der Behandlung doppelter Parameter.
Referenzen
- MITRE Corporation. "CWE-694: Use of Multiple Resources with Duplicate Identifier." https://cwe.mitre.org/data/definitions/694.html
- CWE-102: Struts Duplicate Validation Forms.
- CWE-462: Duplicate Key in Associative List.