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
| Impact | Details |
|---|---|
| Integrity | Scope: File Overwrite Symlink attacks can cause applications to overwrite arbitrary files, including system configuration. |
| Confidentiality | Scope: Information Disclosure Predictable temp files with improper permissions expose sensitive data to local attackers. |
| Access Control | Scope: 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
-
inotify-tools — monitor file system events.
-
symlink race condition exploits — symlink attack tools.
-
Static analysis tools — detect insecure temp file usage.
CVE Examples
-
CVE-2011-4029 — X.Org temp file vulnerability.
-
CVE-2019-3462 — APT temp file handling.
-
CVE-2020-8945 — GPGME temp file vulnerability.
References
-
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."