Creation of Temporary File in Directory with Insecure Permissions

Description

Creation of Temporary File in Directory with Insecure Permissions is a vulnerability that occurs when an application creates temporary files in a directory whose permissions allow unintended actors to determine the file's existence or otherwise access that file. Common shared directories like /tmp on Unix or C:\Windows\Temp on Windows are accessible to all users, making any files created there potentially visible to attackers. Even if individual file permissions are secure, the directory permissions may allow attackers to enumerate files or perform symbolic link attacks.

Risk

Creating temporary files in world-accessible directories exposes information about application behavior. Attackers can determine which applications are in use by monitoring the directory for new files. By correlating temporary files with running processes, adversaries may deduce user activities and plan targeted attacks. Even if file contents are protected, file names and timing of creation can leak sensitive information. Symbolic link attacks in shared directories can redirect file operations to attacker-controlled locations. This reconnaissance capability aids in privilege escalation and targeted exploitation.

Solution

Store sensitive temporary files in non-world-readable directories, preferably per-user directories within the user's home folder. Create a private directory with restrictive permissions (0700) for the application's temporary files. Avoid using system-wide temporary directories for sensitive operations. Use modern language APIs that support specifying custom directories for temporary files. Ensure the parent directory has appropriate permissions before creating temporary files. Consider using memory-based temporary storage for highly sensitive data.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Attackers gain information about what the user is doing through visibility of application temporary files.

Example Code

Vulnerable Code

// Vulnerable: Creates temp file in world-readable /tmp
void vulnerable_shared_dir() {
    char template[] = "/tmp/myapp.XXXXXX";
    int fd = mkstemp(template);

    if (fd >= 0) {
        // Even with 0600 permissions, attackers can:
        // - See the file exists in /tmp
        // - Know the application is running
        // - Attempt timing attacks
        write(fd, secret_data, strlen(secret_data));
        close(fd);
        unlink(template);
    }
}

// Vulnerable: Predictable subdirectory in /tmp
void vulnerable_predictable_dir() {
    char dirname[] = "/tmp/myapp";
    mkdir(dirname, 0755);  // World-readable directory!

    char filepath[256];
    snprintf(filepath, sizeof(filepath), "%s/data.tmp", dirname);

    int fd = open(filepath, O_CREAT | O_WRONLY, 0600);
    // Directory exposes that myapp is running
}
// Vulnerable: Using system temp directory
import java.io.*;
import java.nio.file.*;

public class VulnerableTempDir {
    public void processSecret(String data) throws IOException {
        // Vulnerable: System temp dir is world-accessible
        Path temp = Files.createTempFile("sensitive", ".tmp");

        // File is in /tmp or C:\Windows\Temp
        // Other users can see it exists
        Files.writeString(temp, data);
    }

    public void createWorkDir() throws IOException {
        // Vulnerable: Creates directory in shared /tmp
        Path workDir = Files.createTempDirectory("myapp");

        // Directory listing shows myapp is running
        Path dataFile = workDir.resolve("data.txt");
        Files.writeString(dataFile, "secret");
    }
}
# Vulnerable: Using shared /tmp directory
import tempfile
import os

def vulnerable_default_temp():
    # Vulnerable: tempfile.gettempdir() returns /tmp
    with tempfile.NamedTemporaryFile(delete=False) as f:
        f.write(b"sensitive data")
        # File visible in /tmp to all users
        return f.name

def vulnerable_tmp_socket():
    # Vulnerable: Socket in world-accessible directory
    import socket

    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.bind("/tmp/myapp.sock")  # Any user can see this exists

Fixed Code

// Fixed: Create temp file in private user directory
#include <pwd.h>
#include <unistd.h>

void secure_private_dir() {
    struct passwd *pw = getpwuid(getuid());
    char dirpath[PATH_MAX];
    char filepath[PATH_MAX];

    // Fixed: Use private directory in user's home
    snprintf(dirpath, sizeof(dirpath), "%s/.myapp/tmp", pw->pw_dir);

    // Create private directory
    mkdir(pw->pw_dir, 0700);  // Ensure home dir exists
    snprintf(dirpath, sizeof(dirpath), "%s/.myapp", pw->pw_dir);
    mkdir(dirpath, 0700);
    snprintf(dirpath, sizeof(dirpath), "%s/.myapp/tmp", pw->pw_dir);
    mkdir(dirpath, 0700);

    // Create temp file in private directory
    snprintf(filepath, sizeof(filepath), "%s/data.XXXXXX", dirpath);
    int fd = mkstemp(filepath);

    if (fd >= 0) {
        write(fd, secret_data, strlen(secret_data));
        close(fd);
        unlink(filepath);
    }
}

// Fixed: Use XDG runtime directory if available
void secure_runtime_dir() {
    const char *runtime_dir = getenv("XDG_RUNTIME_DIR");
    char template[PATH_MAX];

    if (runtime_dir && access(runtime_dir, W_OK) == 0) {
        // Fixed: XDG_RUNTIME_DIR is user-private
        snprintf(template, sizeof(template), "%s/myapp.XXXXXX", runtime_dir);
    } else {
        // Fallback to home directory
        struct passwd *pw = getpwuid(getuid());
        snprintf(template, sizeof(template), "%s/.myapp.XXXXXX", pw->pw_dir);
    }

    int fd = mkstemp(template);
    // ...
}
// Fixed: Use private directory for temp files
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.Set;

public class SecureTempDir {
    private Path privateTempDir;

    public SecureTempDir() throws IOException {
        // Fixed: Create private temp directory in user home
        Path userHome = Paths.get(System.getProperty("user.home"));
        Path appDir = userHome.resolve(".myapp").resolve("tmp");

        if (!Files.exists(appDir)) {
            Set<PosixFilePermission> perms =
                PosixFilePermissions.fromString("rwx------");
            Files.createDirectories(appDir,
                PosixFilePermissions.asFileAttribute(perms));
        }

        this.privateTempDir = appDir;
    }

    public void processSecret(String data) throws IOException {
        // Fixed: Create temp file in private directory
        Set<PosixFilePermission> perms =
            PosixFilePermissions.fromString("rw-------");

        Path temp = Files.createTempFile(privateTempDir, "sensitive", ".tmp",
            PosixFilePermissions.asFileAttribute(perms));

        try {
            Files.writeString(temp, data);
            processFile(temp);
        } finally {
            Files.deleteIfExists(temp);
        }
    }
}
# Fixed: Use private directory for temp files
import os
import tempfile
import stat

def get_private_temp_dir():
    """Get or create a private temp directory for this app."""
    # Try XDG_RUNTIME_DIR first (user-private on Linux)
    runtime_dir = os.environ.get('XDG_RUNTIME_DIR')
    if runtime_dir and os.access(runtime_dir, os.W_OK):
        private_dir = os.path.join(runtime_dir, 'myapp')
    else:
        # Fall back to home directory
        private_dir = os.path.expanduser('~/.myapp/tmp')

    # Create with restrictive permissions
    os.makedirs(private_dir, mode=0o700, exist_ok=True)

    # Verify permissions
    mode = os.stat(private_dir).st_mode
    if mode & (stat.S_IRWXG | stat.S_IRWXO):
        raise SecurityError("Temp directory has insecure permissions")

    return private_dir

def secure_temp_file():
    # Fixed: Use private directory
    private_dir = get_private_temp_dir()

    with tempfile.NamedTemporaryFile(dir=private_dir, delete=True) as f:
        f.write(b"sensitive data")
        process_file(f.name)

def secure_unix_socket():
    import socket

    # Fixed: Socket in private directory
    private_dir = get_private_temp_dir()
    socket_path = os.path.join(private_dir, 'myapp.sock')

    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.bind(socket_path)
    os.chmod(socket_path, 0o600)

CVE Examples

  • CVE-2022-27818 — Rust hotkey daemon creates domain socket in /tmp accessible to any user.
  • CVE-2021-21290 — Java framework uses File.createTempFile() with insecure default permissions in shared directory.

References

  1. MITRE Corporation. "CWE-379: Creation of Temporary File in Directory with Insecure Permissions." https://cwe.mitre.org/data/definitions/379.html
  2. freedesktop.org. "XDG Base Directory Specification." https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html