Programmers frequently create temporary files in directories that are writable by everyone (examples being are /tmp
and /var/tmp
on UNIX , and C:\TEMP
on Windows) and may be purged regularly (for example, every night or during reboot).
Temporary files are commonly used :
...
for auxiliary storage for data that does not need to, or otherwise cannot, reside in memory
...
and also as a means of communicating with other processes by transferring data through the file system
...
Temporary files are also used to communicate between two or more collaborating processes. For example, one process will create a temporary file in a shared directory with a well-known name, or a temporary name that is then communicated between to collaborating processes. The file then can be used to share information among these collaborating processes.
This is a dangerous practice, because a well-known file in a shared directory can be easily hijacked or manipulated by an attacker. Mitigation strategies include the following:
1. Use other low-level IPC mechanisms such as sockets or shared memory.
2. Use higher level IPC mechanisms such as remote procedure calls.
3. Use a secure directory or a jail that can only be accessed by application instances (making sure that multiple instances of the application running on the same platform do not compete).
There are many different interprocess communication (IPC) mechanisms, some of which require the use of temporary files, while others do not. An example of an IPC mechanism that uses temporary files is the POSIX mmap()
function. Berkley Sockets, POSIX Local IPC Sockets, and System V Shared Memory do not require temporary files. Because the multiuser nature of shared directories poses an inherent security risk, the use of shared temporary files for IPC is discouraged.
...
The following table lists common temporary file functions and their respective conformance to the above criteria:
Conformance of file functions to criteria for temporary files
| | | | | | |
---|---|---|---|---|---|---|
Unpredictable Name | Not portably | Yes | Not portably | Yes | Not portably | Not portably |
Unique Name | Yes | Yes | Yes | Yes | Yes | Yes |
Atomic | No | No | Yes | Yes | No | Yes |
Exclusive Access | Possible | Possible | No | If supported by OS | Possible | If supported by OS |
Appropriate Permissions | Possible | Possible | No | If supported by OS | Possible | Not portably |
File Removed | No | No | Yes* | Yes* | No | No |
...
These Windows file-locking mechanisms are called mandatory locks because every process attempting access to a locked file region is subject to the restriction. Linux implements mandatory locks and advisory locks. An advisory lock is not enforced by the operating system, which severely diminishes its value from a security perspective. Unfortunately, the mandatory file lock in Linux is also largely impractical for the following reasons:
- mandatory Mandatory locking works only on local file systems and does not extend to network file systems (such as NFS or AFS)
- file File systems must be mounted with support for mandatory locking, and this is disabled by default
- locking Locking relies on the group ID bit that can be turned off by another process (thereby defeating the lock)
...
Removing temporary files when they are no longer required allows file names and other resources (such as secondary storage) to be recycled. In the case of abnormal termination, there is no sure method that can guarantee the removal of orphaned files. For this reason, temporary file cleaner utilities, which are invoked manually by a system administrator or periodically run by a daemon to sweep temporary directories and remove old files, are widely used. However, these utilities are themselves vulnerable to file-based exploits, and often require the use of shared directories. During normal operation, it is the responsibility of the program to ensure that temporary files are removed either explicitly , or through the use of library routines such as tmpfile_s
which guarantee temporary file deletion upon program termination.
...
This noncompliant code example creates a file with a hard-coded file_name
(presumably in a shared directory such as /tmp
or C:\Temp
).
...
Because the name is hard coded and consequently neither unique nor unpredictable, an attacker need only place a symbolic link in lieu of the file, and the target file referenced by the link is opened and truncated.
...
This call to open()
fails whenever file_name
already exists, including when it is a symbolic link. This is secure, but a temporary file is presumably still required. UnfortunatelyAdditionally, the method used by tmpnam()
to generate file names is not guaranteed to be unpredictable, which leaves room for an attacker to guess the file name ahead of time.
Wiki Markup |
---|
Care should be observed when using {{O_EXCL}} with remote file systems, as it does not work with NFS version 2. NFS version 3 added support for {{O_EXCL}} mode in {{open()}}; see IETF RFC 1813 \[[Callaghan 95|AA. C References#Callaghan 95]\], in particularparticularly the {{EXCLUSIVE}} value to the {{mode}} argument of {{CREATE}}. |
Wiki Markup |
---|
Moreover, the {{open()}} function, as specified by the Open Group Base Specifications Issue 6 \[[Open Group 04|AA. C References#Open Group 04]\], does not include support for shared or exclusive locks. However, BSD systems support two additional flags that allow you to obtain these locks: |
...
Code Block | ||
---|---|---|
| ||
char file_name[L_tmpnam_s]; int fd; if (tmpnam_s(file_name, L_tmpnam_s) != 0) { /* Handle error */ } /* A TOCTOU race condition exists here */ fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600); if (fd < 0) { /* Handle error */ } |
Non-normative Nonnormative text in TR 24731-1 also recommends the following:
...
The POSIX function mktemp()
takes a given file name template and overwrites a portion of it to create a file name. The template may be any file name with some number of Xs X's appended to it (for example, /tmp/temp.XXXXXX
). The trailing Xs X's are replaced with the current process number and/or a unique letter combination. The number of unique file names mktemp()
can return depends on the number of Xs X's provided.
Code Block | ||
---|---|---|
| ||
char file_name[] = "tmp-XXXXXX"; int fd; if (!mktemp(file_name)) { /* Handle error */ } /* A TOCTOU race condition exists here */ fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600); if (fd < 0) { /* Handle error */ } |
...
Wiki Markup |
---|
It should be possible to open at least {{TMP_MAX}} temporary files during the lifetime of the program (this limit may be shared with {{tmpfile()}}). C99, Section 7.19.4.4, allows for the value of the macro {{TMP_MAX}} to be as small as 25 \[[ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\]. |
...
The file is opened for update with "wb+"
mode, which means "truncate to zero length or create binary file for update." To the extent that the underlying system supports the concepts, the file is opened with exclusive (non-sharednonshared) access and has a file permission that prevents other users on the system from accessing the file.
...
A call to mkstemp()
replaces the six Xs X's in the template string with six randomly selected characters and returns a file descriptor for the file (opened for reading and writing), as in this compliant solution.
Code Block | ||
---|---|---|
| ||
const char *sdn = "/home/usr1/"; char sfn[] = "/home/usr1/temp-XXXXXX"; FILE *sfp; if (!secure_dir(sdn)) { /* Handle error */ } int fd = mkstemp(sfn); if (fd == -1) { /* Handle error */ } /* * Unlink immediately to hide the file name. * The race condition here is inconsequential if the file * is created with exclusive permissions (glibc >= 2.0.7) */ if (unlink(sfn) == -1) { /* Handle Errorerror */ } sfp = fdopen(fd, "w+"); if (sfp == NULL) { close(fd); /* Handle error */ } /* use temporary file */ fclose(sfp); /* also closes fd */ |
This solution is not serially reusable, however, because the mkstemp()
function replaces the "XXXXXX"
in template
the first time it is invoked. This is not a problem , as long as template
is reinitialized before calling mkstemp()
again. If template
is not reinitialized, the mkstemp()
function will return -1
and leave template
unmodified because template
did not contain six X's.
...
Wiki Markup |
---|
In many older [implementations|BB. Definitions#implementation], the name is a function of process ID and time, so it is possible for the attacker to predict the name and create a decoy in advance. FreeBSD changed the {{mk*temp()}} family to eliminate the PID component of the file name and replace the entire field with base-62 encoded randomness. This raises the number of possible temporary files for the typical use of 6six XsX's significantly, meaning that even {{mktemp()}} with 6six XsX's is reasonably (probabilistically) secure against guessing, except under frequent usage \[[Kennaway 00|AA. C References#Kennaway 00]\]. |
...
FIO43-EX1: The TR24731-1 tmpfile_s()
function can be used if all the targeted implementations create temporary files; in secure directories.
Risk Assessment
...
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
FIO43-C | high | probable | medium | P12 | L1 |
Question: Is the race condition caused by calling
unlink()
to hide the temporary file name less of a concern than the risk of not callingunlink()
?
- The
unlink()
function doesn't follow symlinks, and doesn't really have much of an affect on hard links. So, I guess your options for attacking something like that would be:SIGSTOP
orSIGTSTP
it before the unlink, maybe unlink it yourself and wait (a while) until something created something with the same name, or try to use that name somehow. Probably not that useful, but maybe in a specific attack it could work with a lot of effort.- You could sorta do a symlink attack with an intermediate path component, for example, if it was
/tmp/tmp2/ed.XXXXXX
, you couldrm tmp2
and then symlink it to/etc
or something. It would thenrm /etc/ed.XXXXXX
, but that probably wouldn't buy you much.John McDonald
Automated Detection
Compass/ROSE can detect violations of this recommendation. Specifically, Rose reports use of tmpnam()
, tmpnam_s()
, tmpfile()
, and mktemp()
.
...