Fehlende Sperrenprüfung
Beschreibung
Fehlende Sperrenprüfung ist eine Schwachstelle, bei der ein Produkt nicht prüft, ob eine Sperre vorhanden ist, bevor sensible Operationen auf einer Ressource durchgeführt werden. Die Schwachstelle tritt auf, wenn Software versäumt zu verifizieren, dass schützende Sperrmechanismen vorhanden sind, bevor auf kritische Ressourcen zugegriffen oder diese modifiziert werden, wodurch Möglichkeiten für unbefugten Zugriff, Datenkorruption oder Race Conditions entstehen. Im Gegensatz zu unsachgemäßer Sperrung, bei der Sperren falsch erworben werden, beinhaltet diese Schwachstelle das vollständige Versäumnis, den Sperrenstatus vor dem Fortfahren mit Operationen zu verifizieren.
Risiko
Fehlende Sperrenprüfungen ermöglichen Angreifern den Zugriff auf oder die Modifikation von Ressourcen, die exklusiv kontrolliert werden sollten. Bei sicherheitskritischen Operationen kann das Umgehen der Sperrenverifikation zu Privilegieneskalation oder Umgehung von Sicherheitskontrollen führen. Gleichzeitiger Zugriff auf unverifizierte Ressourcen kann die Datenintegrität beschädigen. In Dateisystemen können Operationen auf nicht gesperrten Dateien andere Prozesse storen. Datenbankoperationen ohne Sperrenverifikation können Transaktionsanomalien verursachen. Die Auswirkung reicht von Datenkorruption bis zur vollständigen Systemkompromittierung, abhängig von der Sensibilität der Ressource.
Lösung
Implementieren Sie einen zuverlässigen Sperrmechanismus und verifizieren Sie immer den Sperrenstatus vor der Durchführung sensibler Operationen. Verwenden Sie sprachbereitgestellte Sperrprimitive, die Verifikation und Akquisition atomar kombinieren. Entwerfen Sie APIs, die einen Nachweis des Sperrenbesitzes erfordern, bevor Ressourcenzugriff erlaubt wird. Implementieren Sie Sperrenverifikation am Anfang jeder Funktion, die auf geschützte Ressourcen zugreift. Verwenden Sie Assertions oder Laufzeitprüfungen zur Verifikation von Sperreninvarianten während der Entwicklung. Erwägen Sie die Verwendung von sperrengeschützten Accessor-Mustern, die unverifizierten Zugriff durch Design unmöglich machen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Umfang: Integrität, Verfügbarkeit Anwendungsdaten modifizieren - Angreifer können Anwendungsdaten ändern, indem sie ohne ordnungsgemäße Sperrenverifikation auf Ressourcen zugreifen. DoS: Instabilität - System kann durch gleichzeitigen unkontrollierten Zugriff instabil werden. DoS: Absturz, Beendigung oder Neustart - Unerwartete Zustandskorruption kann Systemausfälle verursachen. |
Beispielcode
Anfälliger Code
// Anfällig: Keine Sperrenprüfung vor Zugriff auf geschützte Ressource
#include <pthread.h>
typedef struct {
int data;
pthread_mutex_t lock;
int is_locked;
} ProtectedResource;
// Anfällig: Verifiziert nicht, dass Sperre gehalten wird
void vulnerable_modify_data(ProtectedResource *res, int new_value) {
// Anfällig: Keine Prüfung ob Sperre tatsächlich gehalten wird
// Nimmt an, dass Aufrufer Sperre hält, aber verifiziert nicht
res->data = new_value;
}
// Anfällig: Inkonsistente Sperrenprüfung
void vulnerable_operation(ProtectedResource *res) {
// Sperrt manchmal, manchmal nicht
if (some_condition) {
pthread_mutex_lock(&res->lock);
}
// Anfällig: Fährt fort ohne zu wissen ob gesperrt
res->data++;
if (some_condition) {
pthread_mutex_unlock(&res->lock);
}
}
# Anfällig: Keine Sperrenverifikation vor Dateioperationen
import fcntl
class VulnerableFileHandler:
def __init__(self, filepath):
self.filepath = filepath
self.file = None
def open_file(self):
self.file = open(self.filepath, 'r+')
# Anfällig: Prüft nicht ob Datei gesperrt ist
def write_data(self, data):
# Anfällig: Keine Verifikation, dass wir die Sperre halten
# Anderer Prozess könnte die Sperre haben
self.file.write(data)
self.file.flush()
# Anfällig: Nimmt Sperre ohne Prüfung an
def modify_sensitive_data(self, data):
# Sollte verifizieren, dass Sperre gehalten wird, tut es aber nicht
self.file.seek(0)
self.file.write(data)
// Anfällig: Keine Sperrenverifikation in geschützter Methode
public class VulnerableProtectedResource {
private Object data;
private final Object lock = new Object();
private volatile boolean isLocked = false;
public void acquireLock() {
synchronized(lock) {
isLocked = true;
}
}
public void releaseLock() {
synchronized(lock) {
isLocked = false;
}
}
// Anfällig: Verifiziert nicht, dass Sperre gehalten wird
public void modifyData(Object newData) {
// Anfällig: Keine Prüfung für isLocked
// Aufrufer könnte vergessen haben, Sperre zu erwerben
this.data = newData;
}
// Anfällig: Prüfung ist nicht atomar mit Operation
public void unsafeModify(Object newData) {
if (isLocked) {
// Anfällig: Sperre könnte zwischen Prüfung und Modifikation freigegeben werden
this.data = newData;
}
}
}
Korrigierter Code
// Korrigiert: Immer verifizieren, dass Sperre gehalten wird vor Operationen
#include <pthread.h>
#include <assert.h>
typedef struct {
int data;
pthread_mutex_t lock;
pthread_t lock_owner;
int is_locked;
} ProtectedResource;
// Korrigiert: Sperrenbesitz vor Modifikation verifizieren
int secure_modify_data(ProtectedResource *res, int new_value) {
// Korrigiert: Verifizieren, dass dieser Thread die Sperre hält
if (!res->is_locked || !pthread_equal(res->lock_owner, pthread_self())) {
return -1; // Fehler: Sperre wird nicht von diesem Thread gehalten
}
res->data = new_value;
return 0;
}
// Korrigiert: Ordnungsgemäße Sperrenakquisition mit Besitzverfolgung
int acquire_lock(ProtectedResource *res) {
int result = pthread_mutex_lock(&res->lock);
if (result == 0) {
res->is_locked = 1;
res->lock_owner = pthread_self();
}
return result;
}
int release_lock(ProtectedResource *res) {
// Korrigiert: Verifizieren, dass wir die Sperre besitzen vor Freigabe
if (!pthread_equal(res->lock_owner, pthread_self())) {
return -1;
}
res->is_locked = 0;
return pthread_mutex_unlock(&res->lock);
}
// Korrigiert: Kombiniertes Lock-Check-und-Operate-Muster
int secure_operation(ProtectedResource *res, int delta) {
if (acquire_lock(res) != 0) {
return -1;
}
// Sperre wird hier definitiv gehalten
res->data += delta;
release_lock(res);
return 0;
}
# Korrigiert: Sperre vor Operationen verifizieren
import fcntl
import threading
class SecureFileHandler:
def __init__(self, filepath):
self.filepath = filepath
self.file = None
self.lock_held = False
self._internal_lock = threading.Lock()
def open_file(self):
self.file = open(self.filepath, 'r+')
def acquire_lock(self):
with self._internal_lock:
fcntl.flock(self.file.fileno(), fcntl.LOCK_EX)
self.lock_held = True
def release_lock(self):
with self._internal_lock:
if self.lock_held:
fcntl.flock(self.file.fileno(), fcntl.LOCK_UN)
self.lock_held = False
# Korrigiert: Sperre vor Schreiben verifizieren
def write_data(self, data):
with self._internal_lock:
if not self.lock_held:
raise RuntimeError("Sperre nicht gehalten - zuerst acquire_lock() aufrufen")
self.file.write(data)
self.file.flush()
# Korrigiert: Kontextmanager für sichere sperrengeschützte Operationen
def __enter__(self):
self.open_file()
self.acquire_lock()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.release_lock()
if self.file:
self.file.close()
# Verwendung: Sperrenverifikation ist automatisch
with SecureFileHandler('/pfad/zur/datei') as handler:
handler.write_data("sichere Daten") # Sperre garantiert gehalten
// Korrigiert: Sperrenverifikation durch Design erzwungen
import java.util.concurrent.locks.ReentrantLock;
public class SecureProtectedResource {
private Object data;
private final ReentrantLock lock = new ReentrantLock();
// Korrigiert: Private Methode die verifiziert, dass Sperre gehalten wird
private void verifyLockHeld() {
if (!lock.isHeldByCurrentThread()) {
throw new IllegalStateException("Sperre muss gehalten werden um diese Operation durchzuführen");
}
}
// Korrigiert: Öffentliche Methode die Sperrenverifikation erfordert
public void modifyData(Object newData) {
verifyLockHeld(); // Korrigiert: Immer verifizieren
this.data = newData;
}
public Object getData() {
verifyLockHeld(); // Korrigiert: Auch Lesezugriffe erfordern Verifikation
return this.data;
}
// Korrigiert: Sichere öffentliche Schnittstelle die Sperrung behandelt
public void safeModify(Object newData) {
lock.lock();
try {
// Sperre wird hier definitiv gehalten
this.data = newData;
} finally {
lock.unlock();
}
}
// Korrigiert: Gesperrte Operation Callback-Muster bereitstellen
public <T> T withLock(java.util.function.Supplier<T> operation) {
lock.lock();
try {
return operation.get();
} finally {
lock.unlock();
}
}
}
// Verwendung
// resource.withLock(() -> {
// resource.modifyData(newValue); // Sperre garantiert
// return resource.getData();
// });
CVE-Beispiele
- CVE-2004-1056 — Produkt prüft nicht ordnungsgemäß, ob eine Sperre vorhanden ist, wodurch Angreifer auf Funktionalität zugreifen können, die geschützt sein sollte.
Referenzen
- MITRE Corporation. "CWE-414: Missing Lock Check." https://cwe.mitre.org/data/definitions/414.html
- CERT C Coding Standard. "CON00-C. Avoid race conditions with multiple threads." https://wiki.sei.cmu.edu/confluence/display/c/CON00-C.+Avoid+race+conditions+with+multiple+threads