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

AuswirkungDetails
IntegritätUmfang: 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

  1. MITRE Corporation. "CWE-414: Missing Lock Check." https://cwe.mitre.org/data/definitions/414.html
  2. 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