Unsichere temporäre Datei
Beschreibung
Unsichere temporäre Datei-Schwachstelle tritt auf, wenn Software temporäre Dateien auf unsichere Weise erstellt. Dies umfasst die Verwendung vorhersagbarer Dateinamen, das Erstellen von Dateien in für alle beschreibbaren Verzeichnissen ohne ordnungsgemäße Berechtigungen oder das nicht ordnungsgemäße Absichern der Datei zwischen Erstellung und Verwendung. Angreifer können diese Schwächen durch Symlink-Angriffe, Race Conditions oder durch Vorhersage von Dateinamen ausnutzen, um auf sensible Daten zuzugreifen, bösartige Inhalte einzuschleusen oder Privilegien zu eskalieren.
Risiko
Unsichere temporäre Dateibehandlung ist eine klassische Schwachstelle, die seit Jahrzehnten zu Privilegieneskalation auf Unix-Systemen geführt hat. Angreifer können Symlinks zu sensiblen Dateien erstellen, bevor die Anwendung ihre Temp-Datei erstellt, wodurch die Anwendung kritische Systemdateien überschreibt. Vorhersagbare Temp-Dateinamen ermöglichen Angreifern, Dateien mit bösartigem Inhalt vorab zu erstellen. Für alle lesbare Temp-Dateien offenbaren sensible Daten. Diese Angriffe sind besonders gefährlich in setuid-Programmen oder Diensten, die als Root laufen.
Lösung
Verwenden Sie sichere Temp-Datei-Erstellungsfunktionen, die atomar eindeutige Dateien mit ordnungsgemäßen Berechtigungen erstellen (mkstemp(), tempfile.NamedTemporaryFile mit delete=True). Setzen Sie restriktive Berechtigungen (0600) sofort. Verwenden Sie System-Temp-Verzeichnisse mit ordnungsgemäßen Berechtigungen. Vermeiden Sie vorhersagbare Namen — verwenden Sie kryptografisch zufällige Suffixe. Löschen Sie Temp-Dateien sofort nach Gebrauch. Für sensible Operationen erwägen Sie die Verwendung von tmpfs oder Nur-Speicher-Storage. Verwenden Sie niemals mktemp() oder ähnliche Funktionen, die nur Namen generieren, ohne Dateien zu erstellen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Umfang: Dateiüberschreibung Symlink-Angriffe können Anwendungen veranlassen, beliebige Dateien zu überschreiben, einschließlich Systemkonfiguration. |
| Vertraulichkeit | Umfang: Informationsoffenlegung Vorhersagbare Temp-Dateien mit unsachgemäßen Berechtigungen offenbaren sensible Daten an lokale Angreifer. |
| Zugriffskontrolle | Umfang: Privilegieneskalation Das Ausnutzen der Temp-Datei-Behandlung von setuid-Programmen kann zu Root-Kompromittierung führen. |
Beispielcode + Korrigierter Code
Anfälliger Code
// ANFÄLLIG: Vorhersagbarer Temp-Dateiname
#include <stdio.h>
#include <stdlib.h>
void process_data_vulnerable(const char *data) {
// Vorhersagbarer Name - Angreifer kann Symlink erstellen
char *tmpfile = "/tmp/myapp_temp.txt";
FILE *f = fopen(tmpfile, "w");
if (f) {
fprintf(f, "%s", data);
fclose(f);
}
// Temp-Datei verarbeiten...
unlink(tmpfile);
}
// ANFÄLLIG: mktemp verwenden (veraltet)
void vulnerable_mktemp() {
char template[] = "/tmp/myappXXXXXX";
// mktemp generiert nur Namen, erstellt Datei nicht
// Race Condition zwischen mktemp und open!
char *filename = mktemp(template);
// Angreifer kann hier Datei/Symlink erstellen!
FILE *f = fopen(filename, "w");
// ...
}
// ANFÄLLIG: Vorhersagbarer PID-basierter Name
void vulnerable_pid_name() {
char filename[256];
sprintf(filename, "/tmp/app_%d.tmp", getpid());
// Angreifer kann PID vorhersagen und Symlink erstellen
FILE *f = fopen(filename, "w");
// ...
}
# ANFÄLLIG: Vorhersagbare Temp-Datei
import os
def process_data_vulnerable(data):
# Vorhersagbarer Name in für alle beschreibbarem Verzeichnis
tmpfile = "/tmp/myapp_data.txt"
with open(tmpfile, 'w') as f:
f.write(data)
# Datei verarbeiten...
os.unlink(tmpfile)
# ANFÄLLIG: tempfile unsicher verwenden
import tempfile
def vulnerable_tempfile():
# tempfile.mktemp ist veraltet - Race Condition!
filename = tempfile.mktemp() # Dies nicht verwenden!
# Angreifer kann Datei zwischen mktemp und open erstellen
with open(filename, 'w') as f:
f.write("sensible daten")
# ANFÄLLIG: Unsachgemäße Berechtigungen
def vulnerable_permissions():
tmpfile = "/tmp/app_temp_%d.txt" % os.getpid()
# Standardberechtigungen könnten zu offen sein (umask-abhängig)
with open(tmpfile, 'w') as f:
f.write("geheime daten")
# Datei könnte für alle lesbar sein!
// ANFÄLLIG: Vorhersagbare Temp-Datei in Java
import java.io.*;
public class VulnerableTempFile {
public void processData(String data) throws IOException {
// Vorhersagbarer Ort
File tempFile = new File("/tmp/myapp_temp.txt");
// Race Condition und Symlink-Angriff möglich
try (FileWriter writer = new FileWriter(tempFile)) {
writer.write(data);
}
tempFile.delete();
}
// ANFÄLLIG: createTempFile in gemeinsam genutztem Verzeichnis
public void vulnerableTempFile() throws IOException {
// Erstellt in System-Temp, aber Name könnte vorhersagbar sein
// und Berechtigungen könnten zu offen sein
File temp = File.createTempFile("myapp", ".tmp");
temp.deleteOnExit(); // Nicht zuverlässig!
// Berechtigungen nicht eingeschränkt
try (FileWriter writer = new FileWriter(temp)) {
writer.write("sensibel");
}
}
}
Korrigierter Code
// SICHER: mkstemp für atomare sichere Erstellung verwenden
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
void process_data_safe(const char *data) {
char template[] = "/tmp/myapp_XXXXXX";
// mkstemp erstellt Datei atomar mit 0600 Berechtigungen
int fd = mkstemp(template);
if (fd == -1) {
perror("mkstemp");
return;
}
// Datei ist bereits mit exklusivem Zugang geöffnet
write(fd, data, strlen(data));
// Daten verarbeiten...
close(fd);
unlink(template);
}
// SICHER: mkstemps für Suffix verwenden
void safe_with_suffix() {
char template[] = "/tmp/myapp_XXXXXX.dat";
// 4 = Länge des ".dat" Suffixes
int fd = mkstemps(template, 4);
if (fd == -1) {
perror("mkstemps");
return;
}
// Datei verwenden...
close(fd);
unlink(template);
}
// SICHER: Sicheres temporäres Verzeichnis
void safe_temp_directory() {
char template[] = "/tmp/myapp_XXXXXX";
// Sicheres temporäres Verzeichnis erstellen
char *dirname = mkdtemp(template);
if (!dirname) {
perror("mkdtemp");
return;
}
// Dateien innerhalb des sicheren Verzeichnisses erstellen
char filepath[PATH_MAX];
snprintf(filepath, sizeof(filepath), "%s/data.txt", dirname);
int fd = open(filepath, O_WRONLY | O_CREAT | O_EXCL, 0600);
// Datei verwenden...
close(fd);
unlink(filepath);
rmdir(dirname);
}
// SICHER: O_NOFOLLOW verwenden um Symlink-Angriffe zu verhindern
void safe_nofollow(const char *data) {
char template[] = "/tmp/myapp_XXXXXX";
int fd = mkstemp(template);
if (fd == -1) return;
// Mit O_NOFOLLOW für zusätzliche Sicherheit erneut öffnen
close(fd);
fd = open(template, O_WRONLY | O_NOFOLLOW);
if (fd == -1) {
unlink(template);
return;
}
write(fd, data, strlen(data));
close(fd);
unlink(template);
}
# SICHER: tempfile-Modul korrekt verwenden
import tempfile
import os
def process_data_safe(data):
# NamedTemporaryFile erstellt sichere Temp-Datei
# delete=True (Standard) entfernt Datei beim Schließen
with tempfile.NamedTemporaryFile(mode='w', delete=True) as f:
f.write(data)
f.flush()
# Datei verarbeiten während sie geöffnet ist...
# Dateipfad verfügbar unter f.name
# SICHER: Wenn Datei vorübergehend behalten werden muss
def safe_temp_keep():
# Mit sicheren Berechtigungen erstellen
fd, path = tempfile.mkstemp(suffix='.dat', prefix='myapp_')
try:
# Mit Dateideskriptor schreiben
os.write(fd, b"sensible daten")
os.fsync(fd)
# Datei verarbeiten...
finally:
os.close(fd)
os.unlink(path)
# SICHER: Sicheres temporäres Verzeichnis verwenden
def safe_temp_directory():
# Sicheres temporäres Verzeichnis erstellen
tmpdir = tempfile.mkdtemp(prefix='myapp_')
try:
filepath = os.path.join(tmpdir, 'data.txt')
# Datei mit eingeschränkten Berechtigungen erstellen
fd = os.open(filepath, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
try:
os.write(fd, b"sensible daten")
finally:
os.close(fd)
# Dateien im Verzeichnis verarbeiten...
finally:
# Verzeichnis aufräumen
import shutil
shutil.rmtree(tmpdir)
# SICHER: SpooledTemporaryFile für Speicher-First-Storage
def safe_memory_temp(data):
# Hält Daten im Speicher bis sie max_size überschreiten
with tempfile.SpooledTemporaryFile(max_size=10*1024*1024, mode='w+') as f:
f.write(data)
f.seek(0)
# Daten verarbeiten...
content = f.read()
# Automatisch aufgeräumt
# SICHER: Context-Manager für garantiertes Aufräumen
class SecureTempFile:
def __init__(self, suffix='', prefix='tmp', dir=None):
self.fd = None
self.path = None
self.suffix = suffix
self.prefix = prefix
self.dir = dir
def __enter__(self):
self.fd, self.path = tempfile.mkstemp(
suffix=self.suffix,
prefix=self.prefix,
dir=self.dir
)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.fd is not None:
try:
os.close(self.fd)
except:
pass
if self.path and os.path.exists(self.path):
os.unlink(self.path)
# Verwendung
with SecureTempFile(suffix='.dat') as tmp:
os.write(tmp.fd, b"daten")
# Datei beim Verlassen automatisch gelöscht
// SICHER: Sichere temporäre Dateibehandlung in Java
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
public class SecureTempFile {
public void processDataSecure(String data) throws IOException {
// Temp-Datei mit restriktiven Berechtigungen erstellen
Path tempFile = Files.createTempFile("myapp_", ".tmp",
PosixFilePermissions.asFileAttribute(
PosixFilePermissions.fromString("rw-------")
)
);
try {
Files.writeString(tempFile, data);
// Datei verarbeiten...
} finally {
// Immer löschen
Files.deleteIfExists(tempFile);
}
}
// SICHER: Try-With-Resources verwenden
public void processWithAutoDelete(String data) throws IOException {
Path tempDir = Files.createTempDirectory("myapp_",
PosixFilePermissions.asFileAttribute(
PosixFilePermissions.fromString("rwx------")
)
);
try {
Path tempFile = tempDir.resolve("data.txt");
// Datei mit exklusivem Zugang erstellen
try (OutputStream out = Files.newOutputStream(tempFile,
StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE)) {
out.write(data.getBytes());
}
// Datei verarbeiten...
} finally {
// Temp-Verzeichnis rekursiv löschen
deleteDirectory(tempDir);
}
}
private void deleteDirectory(Path dir) throws IOException {
if (Files.exists(dir)) {
Files.walk(dir)
.sorted((a, b) -> b.compareTo(a)) // Dateien vor Verzeichnissen löschen
.forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
// Fehler protokollieren
}
});
}
}
// SICHER: Memory-Mapped Temp-Datei
public void processInMemory(byte[] data) throws IOException {
// Für sensible Daten nur im Speicher halten
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(data);
// Im Speicher verarbeiten...
byte[] result = baos.toByteArray();
// Sensible Daten löschen
Arrays.fill(data, (byte) 0);
baos.reset();
}
}
Ausgenutzt in der Praxis
Unix /tmp Race Conditions (Historisch)
Unzählige Unix-Privilegieneskalations-Exploits haben vorhersagbare Temp-Dateinamen in setuid-Programmen verwendet, um Symlinks zu erstellen und /etc/passwd oder andere kritische Dateien zu überschreiben.
CVE-2011-4029 - X.Org Temp-Datei
Der X.Org X-Server verwendete vorhersagbare temporäre Dateinamen, die lokalen Angreifern ermöglichten, beliebige Dateien über Symlink-Angriffe zu überschreiben.
Vim Temp-Datei-Schwachstellen
Mehrere Versionen von Vim hatten unsichere Temp-Datei-Behandlung, die lokalen Angreifern ermöglichte, sensible Dateien durch Symlink-Angriffe anzuzeigen oder zu modifizieren.
Tools zum Testen/Ausnutzen
-
inotify-tools — Dateisystemereignisse überwachen.
-
symlink race condition exploits — Symlink-Angriffs-Tools.
-
Statische Analyse-Tools — unsichere Temp-Datei-Nutzung erkennen.
CVE-Beispiele
-
CVE-2011-4029 — X.Org Temp-Datei-Schwachstelle.
-
CVE-2019-3462 — APT Temp-Datei-Behandlung.
-
CVE-2020-8945 — GPGME Temp-Datei-Schwachstelle.
Referenzen
-
MITRE. "CWE-377: Insecure Temporary File." https://cwe.mitre.org/data/definitions/377.html
-
CERT C Secure Coding Standard. "FIO21-C: Do not create temporary files in shared directories."