Insecure Temporary File

Description

Insecure Temporary File vulnerability occurs when software creates temporary files in an insecure manner. This includes using predictable file names, creating files in world-writable directories without proper permissions, or not properly securing the file between creation and use. Attackers can exploit these weaknesses through symlink attacks, race conditions, or by predicting file names to access sensitive data, inject malicious content, or escalate privileges.

Risk

Insecure temporary file handling is a classic vulnerability that has led to privilege escalation on Unix systems for decades. Attackers can create symlinks to sensitive files before the application creates its temp file, causing the application to overwrite critical system files. Predictable temp file names allow attackers to pre-create files with malicious content. World-readable temp files expose sensitive data. These attacks are particularly dangerous in setuid programs or services running as root.

Solution

Use secure temp file creation functions that atomically create unique files with proper permissions (mkstemp(), tempfile.NamedTemporaryFile with delete=True). Set restrictive permissions (0600) immediately. Use system temp directories with proper permissions. Avoid predictable names—use cryptographically random suffixes. Delete temp files immediately after use. For sensitive operations, consider using tmpfs or memory-only storage. Never use mktemp() or similar functions that only generate names without creating files.

Common Consequences

ImpactDetails
IntegrityScope: File Overwrite

Symlink attacks can cause applications to overwrite arbitrary files, including system configuration.
ConfidentialityScope: Information Disclosure

Predictable temp files with improper permissions expose sensitive data to local attackers.
Access ControlScope: Privilege Escalation

Exploiting setuid programs' temp file handling can lead to root compromise.

Example Code + Solution Code

Vulnerable Code

// VULNERABLE: Predictable temp file name
#include <stdio.h>
#include <stdlib.h>

void process_data_vulnerable(const char *data) {
    // Predictable name - attacker can create symlink
    char *tmpfile = "/tmp/myapp_temp.txt";

    FILE *f = fopen(tmpfile, "w");
    if (f) {
        fprintf(f, "%s", data);
        fclose(f);
    }
    // Process temp file...
    unlink(tmpfile);
}

// VULNERABLE: Using mktemp (deprecated)
void vulnerable_mktemp() {
    char template[] = "/tmp/myappXXXXXX";

    // mktemp only generates name, doesn't create file
    // Race condition between mktemp and open!
    char *filename = mktemp(template);

    // Attacker can create file/symlink here!

    FILE *f = fopen(filename, "w");
    // ...
}

// VULNERABLE: Predictable PID-based name
void vulnerable_pid_name() {
    char filename[256];
    sprintf(filename, "/tmp/app_%d.tmp", getpid());

    // Attacker can predict PID and create symlink
    FILE *f = fopen(filename, "w");
    // ...
}
# VULNERABLE: Predictable temp file
import os

def process_data_vulnerable(data):
    # Predictable name in world-writable directory
    tmpfile = "/tmp/myapp_data.txt"

    with open(tmpfile, 'w') as f:
        f.write(data)

    # Process file...
    os.unlink(tmpfile)

# VULNERABLE: Using tempfile insecurely
import tempfile

def vulnerable_tempfile():
    # tempfile.mktemp is deprecated - race condition!
    filename = tempfile.mktemp()  # Don't use this!

    # Attacker can create file between mktemp and open
    with open(filename, 'w') as f:
        f.write("sensitive data")

# VULNERABLE: Improper permissions
def vulnerable_permissions():
    tmpfile = "/tmp/app_temp_%d.txt" % os.getpid()

    # Default permissions might be too open (umask dependent)
    with open(tmpfile, 'w') as f:
        f.write("secret data")

    # File might be world-readable!
// VULNERABLE: Predictable temp file in Java
import java.io.*;

public class VulnerableTempFile {

    public void processData(String data) throws IOException {
        // Predictable location
        File tempFile = new File("/tmp/myapp_temp.txt");

        // Race condition and symlink attack possible
        try (FileWriter writer = new FileWriter(tempFile)) {
            writer.write(data);
        }

        tempFile.delete();
    }

    // VULNERABLE: createTempFile in shared directory
    public void vulnerableTempFile() throws IOException {
        // Creates in system temp, but name might be predictable
        // and permissions might be too open
        File temp = File.createTempFile("myapp", ".tmp");
        temp.deleteOnExit();  // Not reliable!

        // Permissions not restricted
        try (FileWriter writer = new FileWriter(temp)) {
            writer.write("sensitive");
        }
    }
}

Fixed Code

// SAFE: Using mkstemp for atomic secure creation
#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 atomically creates file with 0600 permissions
    int fd = mkstemp(template);
    if (fd == -1) {
        perror("mkstemp");
        return;
    }

    // File is already open with exclusive access
    write(fd, data, strlen(data));

    // Process data...

    close(fd);
    unlink(template);
}

// SAFE: Using mkstemps for suffix
void safe_with_suffix() {
    char template[] = "/tmp/myapp_XXXXXX.dat";

    // 4 = length of ".dat" suffix
    int fd = mkstemps(template, 4);
    if (fd == -1) {
        perror("mkstemps");
        return;
    }

    // Use file...
    close(fd);
    unlink(template);
}

// SAFE: Secure temporary directory
void safe_temp_directory() {
    char template[] = "/tmp/myapp_XXXXXX";

    // Create secure temporary directory
    char *dirname = mkdtemp(template);
    if (!dirname) {
        perror("mkdtemp");
        return;
    }

    // Create files inside secure directory
    char filepath[PATH_MAX];
    snprintf(filepath, sizeof(filepath), "%s/data.txt", dirname);

    int fd = open(filepath, O_WRONLY | O_CREAT | O_EXCL, 0600);
    // Use file...

    close(fd);
    unlink(filepath);
    rmdir(dirname);
}

// SAFE: Using O_NOFOLLOW to prevent symlink attacks
void safe_nofollow(const char *data) {
    char template[] = "/tmp/myapp_XXXXXX";

    int fd = mkstemp(template);
    if (fd == -1) return;

    // Reopen with O_NOFOLLOW for extra safety
    close(fd);
    fd = open(template, O_WRONLY | O_NOFOLLOW);

    if (fd == -1) {
        unlink(template);
        return;
    }

    write(fd, data, strlen(data));
    close(fd);
    unlink(template);
}
# SAFE: Using tempfile module correctly
import tempfile
import os

def process_data_safe(data):
    # NamedTemporaryFile creates secure temp file
    # delete=True (default) removes file when closed
    with tempfile.NamedTemporaryFile(mode='w', delete=True) as f:
        f.write(data)
        f.flush()
        # Process file while it's open...
        # File path available at f.name

# SAFE: When you need to keep the file temporarily
def safe_temp_keep():
    # Create with secure permissions
    fd, path = tempfile.mkstemp(suffix='.dat', prefix='myapp_')

    try:
        # Write using file descriptor
        os.write(fd, b"sensitive data")
        os.fsync(fd)

        # Process file...

    finally:
        os.close(fd)
        os.unlink(path)

# SAFE: Using secure temporary directory
def safe_temp_directory():
    # Create secure temporary directory
    tmpdir = tempfile.mkdtemp(prefix='myapp_')

    try:
        filepath = os.path.join(tmpdir, 'data.txt')

        # Create file with restricted permissions
        fd = os.open(filepath, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)

        try:
            os.write(fd, b"sensitive data")
        finally:
            os.close(fd)

        # Process files in directory...

    finally:
        # Clean up directory
        import shutil
        shutil.rmtree(tmpdir)

# SAFE: SpooledTemporaryFile for memory-first storage
def safe_memory_temp(data):
    # Keeps data in memory until it exceeds max_size
    with tempfile.SpooledTemporaryFile(max_size=10*1024*1024, mode='w+') as f:
        f.write(data)
        f.seek(0)
        # Process data...
        content = f.read()
    # Automatically cleaned up

# SAFE: Context manager for guaranteed cleanup
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)

# Usage
with SecureTempFile(suffix='.dat') as tmp:
    os.write(tmp.fd, b"data")
    # File automatically deleted on exit
// SAFE: Secure temporary file handling in Java
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;

public class SecureTempFile {

    public void processDataSecure(String data) throws IOException {
        // Create temp file with restrictive permissions
        Path tempFile = Files.createTempFile("myapp_", ".tmp",
            PosixFilePermissions.asFileAttribute(
                PosixFilePermissions.fromString("rw-------")
            )
        );

        try {
            Files.writeString(tempFile, data);
            // Process file...

        } finally {
            // Always delete
            Files.deleteIfExists(tempFile);
        }
    }

    // SAFE: Using try-with-resources
    public void processWithAutoDelete(String data) throws IOException {
        Path tempDir = Files.createTempDirectory("myapp_",
            PosixFilePermissions.asFileAttribute(
                PosixFilePermissions.fromString("rwx------")
            )
        );

        try {
            Path tempFile = tempDir.resolve("data.txt");

            // Create file with exclusive access
            try (OutputStream out = Files.newOutputStream(tempFile,
                    StandardOpenOption.CREATE_NEW,
                    StandardOpenOption.WRITE)) {
                out.write(data.getBytes());
            }

            // Process file...

        } finally {
            // Recursively delete temp directory
            deleteDirectory(tempDir);
        }
    }

    private void deleteDirectory(Path dir) throws IOException {
        if (Files.exists(dir)) {
            Files.walk(dir)
                .sorted((a, b) -> b.compareTo(a))  // Delete files before directories
                .forEach(path -> {
                    try {
                        Files.delete(path);
                    } catch (IOException e) {
                        // Log error
                    }
                });
        }
    }

    // SAFE: Memory-mapped temp file
    public void processInMemory(byte[] data) throws IOException {
        // For sensitive data, keep in memory only
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(data);

        // Process in memory...
        byte[] result = baos.toByteArray();

        // Clear sensitive data
        Arrays.fill(data, (byte) 0);
        baos.reset();
    }
}

Exploited in the Wild

Unix /tmp Race Conditions (Historical)

Countless Unix privilege escalation exploits have used predictable temp file names in setuid programs to create symlinks and overwrite /etc/passwd or other critical files.

CVE-2011-4029 - X.Org Temp File

The X.Org X server used predictable temporary file names allowing local attackers to overwrite arbitrary files via symlink attacks.

Vim Temp File Vulnerabilities

Multiple versions of Vim had insecure temp file handling that allowed local attackers to view or modify sensitive files through symlink attacks.


Tools to test/exploit


CVE Examples


References

  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."