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

AuswirkungDetails
IntegritätUmfang: Dateiüberschreibung

Symlink-Angriffe können Anwendungen veranlassen, beliebige Dateien zu überschreiben, einschließlich Systemkonfiguration.
VertraulichkeitUmfang: Informationsoffenlegung

Vorhersagbare Temp-Dateien mit unsachgemäßen Berechtigungen offenbaren sensible Daten an lokale Angreifer.
ZugriffskontrolleUmfang: 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


CVE-Beispiele


Referenzen

  1. MITRE. "CWE-377: Insecure Temporary File." https://cwe.mitre.org/data/definitions/377.html

  2. CERT C Secure Coding Standard. "FIO21-C: Do not create temporary files in shared directories."