Uneingeschränkt extern zugängliche Sperre
Beschreibung
Uneingeschränkt extern zugängliche Sperre ist eine Schwachstelle, bei der ein Produkt ordnungsgemäß auf die Existenz einer Sperre prüft, aber die Sperre von einem Akteur, der kein gültiger Teil der Produktausführung ist, extern kontrolliert oder beeinflusst werden kann. Dies verhindert, dass das Produkt auf zugehörige Ressourcen einwirkt oder sperrengesteuerte Verhaltensweisen ausführt. Wenn Sperren unbegrenzt von externen Parteien gehalten werden können, wird Denial-of-Service möglich. Dies betrifft alle Ressourcen oder Verhaltensweisen, die durch Sperren reguliert werden, einschließlich exklusiver Sperren, Mutexe, Dateisperren oder gemeinsam genutzter Ressourcen, die als Sperren fungieren.
Risiko
Extern zugängliche Sperren ermöglichen Denial-of-Service-Angriffe, bei denen Angreifer legitime Operationen verhindern können, indem sie Sperren unbegrenzt halten. Wenn Sperrdateien in weltweit beschreibbaren Verzeichnissen mit vorhersagbaren Namen erstellt werden, können Angreifer sie vorab erstellen. Programme, die auf Mutex-Freigaben warten, können für immer hängen, wenn ein Angreifer-Prozess den Mutex hält. Kritische Sicherheitsoperationen wie Protokollierung oder Richtliniendurchsetzung können umgangen werden, indem erforderliche Dateien gesperrt werden. Die Auswirkung geht über die Verfügbarkeit hinaus – Sicherheitskontrollen können umgangen werden, wenn ihre Operationen von unzugänglichen gesperrten Ressourcen abhängen.
Lösung
Nutzen Sie Zugriffskontrollfunktionen, die die Sperren-Funktionalität bietet, um einzuschränken, wer Sperren erwerben kann. Verwenden Sie unvorhersagbare Sperrnamen oder -kennungen wenn möglich. Erstellen Sie Sperrdateien in Verzeichnissen mit restriktiven Berechtigungen. Implementieren Sie Timeouts für Sperren-Akquisition, um unbegrenztes Warten zu verhindern. Erwägen Sie nicht-blockierende Synchronisationsmethoden, die graceful versagen, wenn Sperren nicht verfügbar sind. Validieren Sie den Sperren-Besitz bevor Sie dem Sperren-Zustand vertrauen. Verwenden Sie Betriebssystemfunktionen, die externe Einmischung bei Sperren verhindern. Entwerfen Sie Systeme so, dass sie graceful degradieren, wenn Sperren nicht erworben werden können.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Umfang: Verfügbarkeit DoS: Ressourcenverbrauch (Ändere) - Wenn ein Angreifer eine Sperre kontrollieren kann, kann das Programm unbegrenzt warten, bis der Angreifer die Sperre freigibt, was einen Denial-of-Service für andere Benutzer des Programms verursacht. |
Beispielcode
Anfälliger Code
<?php
// Anfällig: Sperrdatei in weltweit beschreibbarem Verzeichnis
function vulnerableWriteToLog($message) {
$logfile = fopen("/tmp/app.log", "a");
// Anfällig: Blockiert unbegrenzt wartend auf Sperre
// Angreifer kann Sperre auf /tmp/app.log für immer halten
if (flock($logfile, LOCK_EX)) {
fwrite($logfile, $message);
flock($logfile, LOCK_UN);
}
fclose($logfile);
}
// Angreifer-Skript:
// $f = fopen("/tmp/app.log", "a");
// flock($f, LOCK_EX);
// sleep(PHP_INT_MAX); // Sperre für immer halten
?>
# Anfällig: Vorhersagbarer Sperrdateiname
import fcntl
import os
def vulnerable_critical_operation():
# Anfällig: Vorhersagbare Sperrdatei in /tmp
lock_file = open("/tmp/myapp.lock", "w")
# Anfällig: Blockiert unbegrenzt
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
try:
perform_critical_operation()
finally:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
lock_file.close()
# Angreifer kann /tmp/myapp.lock vorab erstellen und exklusive Sperre halten
// Anfällig: Mutex ohne Timeout
#include <pthread.h>
pthread_mutex_t shared_mutex = PTHREAD_MUTEX_INITIALIZER;
void vulnerable_access_resource() {
// Anfällig: Blockiert für immer wenn Mutex von bösartigem Prozess gehalten
pthread_mutex_lock(&shared_mutex);
access_shared_resource();
pthread_mutex_unlock(&shared_mutex);
}
// Wenn shared_mutex in Shared Memory ist, das für Angreifer zugänglich ist,
// können sie ihn sperren und nie freigeben
// Anfällig: Dateisperre ohne Timeout
import java.io.*;
import java.nio.channels.*;
public class VulnerableLocking {
public void writeData(String data) throws IOException {
try (FileOutputStream fos = new FileOutputStream("/tmp/data.txt");
FileChannel channel = fos.getChannel()) {
// Anfällig: Blockiert unbegrenzt wartend auf Sperre
FileLock lock = channel.lock(); // Kein Timeout!
try {
fos.write(data.getBytes());
} finally {
lock.release();
}
}
}
}
Korrigierter Code
<?php
// Korrigiert: Sperrdatei in geschütztem Verzeichnis mit Timeout
function secureWriteToLog($message) {
// Korrigiert: Geschütztes Verzeichnis verwenden
$lockDir = "/var/run/myapp/";
if (!is_dir($lockDir)) {
mkdir($lockDir, 0700, true);
}
$logfile = fopen($lockDir . "app.log", "a");
// Korrigiert: Nicht-blockierende Sperre mit Timeout verwenden
$timeout = 5; // 5 Sekunden Timeout
$start = time();
while (time() - $start < $timeout) {
if (flock($logfile, LOCK_EX | LOCK_NB)) {
fwrite($logfile, $message);
flock($logfile, LOCK_UN);
fclose($logfile);
return true;
}
usleep(100000); // 100ms vor erneutem Versuch warten
}
fclose($logfile);
error_log("Sperre könnte nicht innerhalb Timeout erworben werden");
return false;
}
?>
# Korrigiert: Geschützte Sperrdatei mit Timeout
import fcntl
import os
import time
import errno
def secure_critical_operation(timeout_seconds=5):
# Korrigiert: Geschütztes Verzeichnis mit restriktiven Berechtigungen verwenden
lock_dir = "/var/run/myapp"
os.makedirs(lock_dir, mode=0o700, exist_ok=True)
lock_path = os.path.join(lock_dir, "operation.lock")
lock_file = open(lock_path, "w")
# Korrigiert: Nicht-blockierende Sperre mit Timeout
start_time = time.time()
while True:
try:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
break # Sperre erworben
except IOError as e:
if e.errno != errno.EWOULDBLOCK:
raise
if time.time() - start_time > timeout_seconds:
lock_file.close()
raise TimeoutError("Konnte Sperre nicht innerhalb Timeout erwerben")
time.sleep(0.1) # Vor erneutem Versuch warten
try:
perform_critical_operation()
finally:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
lock_file.close()
# Alternative: Kontextmanager verwenden
import contextlib
@contextlib.contextmanager
def acquire_lock_with_timeout(lock_path, timeout=5):
lock_file = open(lock_path, "w")
start = time.time()
while True:
try:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
break
except IOError:
if time.time() - start > timeout:
lock_file.close()
raise TimeoutError("Sperren-Akquisition-Timeout")
time.sleep(0.1)
try:
yield lock_file
finally:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
lock_file.close()
// Korrigiert: Mutex mit Timeout
#include <pthread.h>
#include <time.h>
#include <errno.h>
pthread_mutex_t shared_mutex = PTHREAD_MUTEX_INITIALIZER;
int secure_access_resource(int timeout_ms) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += timeout_ms / 1000;
ts.tv_nsec += (timeout_ms % 1000) * 1000000;
// Korrigiert: Zeitgesteuerte Sperre verwenden
int result = pthread_mutex_timedlock(&shared_mutex, &ts);
if (result == ETIMEDOUT) {
// Korrigiert: Timeout graceful behandeln
log_error("Mutex könnte nicht innerhalb Timeout erworben werden");
return -1;
}
if (result != 0) {
log_error("Mutex-Sperre fehlgeschlagen: %d", result);
return -1;
}
access_shared_resource();
pthread_mutex_unlock(&shared_mutex);
return 0;
}
// Alternative: Try-Lock-Muster
int secure_access_with_trylock() {
int retries = 50; // 5 Sekunden mit 100ms Intervallen
while (retries > 0) {
if (pthread_mutex_trylock(&shared_mutex) == 0) {
access_shared_resource();
pthread_mutex_unlock(&shared_mutex);
return 0;
}
usleep(100000); // 100ms
retries--;
}
log_error("Konnte Mutex nach Wiederholungsversuchen nicht erwerben");
return -1;
}
// Korrigiert: Dateisperre mit Timeout
import java.io.*;
import java.nio.channels.*;
import java.util.concurrent.*;
public class SecureLocking {
public boolean writeData(String data, long timeoutMs) throws IOException {
File lockDir = new File("/var/run/myapp");
lockDir.mkdirs();
// Korrigiert: Restriktive Berechtigungen setzen (Java 7+)
lockDir.setReadable(false, false);
lockDir.setReadable(true, true);
lockDir.setWritable(false, false);
lockDir.setWritable(true, true);
lockDir.setExecutable(false, false);
lockDir.setExecutable(true, true);
File dataFile = new File(lockDir, "data.txt");
try (FileOutputStream fos = new FileOutputStream(dataFile);
FileChannel channel = fos.getChannel()) {
// Korrigiert: Try-Lock mit Timeout
long deadline = System.currentTimeMillis() + timeoutMs;
while (System.currentTimeMillis() < deadline) {
FileLock lock = channel.tryLock();
if (lock != null) {
try {
fos.write(data.getBytes());
return true;
} finally {
lock.release();
}
}
// Vor erneutem Versuch warten
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
// Korrigiert: Timeout aufgetreten
return false;
}
}
}
CVE-Beispiele
- CVE-2001-0682 — Programm kann nicht ausgeführt werden wenn Angreifer einen Mutex erhält.
- CVE-2002-1914 — Programm blockiert wenn Angreifer kritische Ausgabedatei sperrt.
- CVE-2002-0051 — Kritische Datei mit exklusivem Lesezugriff geöffnet, verhindert Sicherheitsrichtlinien-Anwendung.
- CVE-2000-0338 — Vorhersagbare Sperrdateinamen erlauben Vorab-Erstellung durch Angreifer.
- CVE-2002-1869 — Protokollierung umgangen über exklusiven Dateisperr-Zugriff.
Referenzen
- MITRE Corporation. "CWE-412: Unrestricted Externally Accessible Lock." https://cwe.mitre.org/data/definitions/412.html
- CAPEC-25. "Forced Deadlock." https://capec.mitre.org/data/definitions/25.html