You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Next »

Privileged programs that create files in world-writable directories can overwrite protected system files. An attacker who can predict the name of a file created by a privileged program can create a symbolic link (with the same name as the file used by the program) to point to a protected system file. Unless the privileged program is coded securely, the program will follow the symbolic link instead of opening or creating the file that it is supposed to be using. As a result, a protected system file to which the symbolic link points can be overwritten when the program is executed.

Non-Compliant Code Example: open()

The following statement creates some_file in the /tmp directory.

int fd = open("/tmp/some_file", O_WRONLY | O_CREAT | O_TRUNC, 0600);

If /tmp/some_file already exists then that file is opened and truncated. If /tmp/some_file is a symbolic link, then the target file referenced by the link is truncated.

To exploit this coding error, an attacker need only create a symbolic link called /tmp/some_file before execution of this statement.

Non-Compliant Code Example: O_CREAT and O_EXCL

This vulnerability can be prevented by including the flags O_CREAT and O_EXCL when calling open().

int fd = open("/tmp/some_file", O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600);

This call to open() fails whenever /tmp/some_file already exists, including when it is a symbolic link.
Care should be observed when using O_EXCL with remote file systems as it does not work with NFSv2 (but is supported in NFSv3 and later).

The problem with this solution is that open() can fail if /tmp/some_file already exists. One solution is to generate random file names and attempt to open() each until we find a unique name. Luckily, there are predefined functions that do this for us.

Non-Compliant Code Example: tmpnam()

The C99 tmpnam() function generates a string that is a valid file name and that is not the same as the name of an existing file [[ISO/IEC 9899-1999]]. Files created using strings generated by the tmpnam() function are temporary in that their names should not collide with those generated by conventional naming rules for the implementation. The function is potentially capable of generating TMP_MAX different strings, but any or all of them may already be in use by existing files. If the argument is not a null pointer, it is assumed to point to an array of at least L_tmpnam chars; the tmpnam() function writes its result in that array and returns the argument as its value.

...
if (tmpnam(temp_file_name)) {
  /* temp_file_name may refer to an existing file */
  t_file = fopen(temp_file_name,"wb+");
  if (!t_file) {
     /* Handle Error */
  }
}
...

Unfortunately, this solution is still non-compliant because it violates [[FIO32-C]], [[FI041-C]], [[FI042-C]].

Non-Compliant Code Example: tmpnam_s()

The TR 24731-1 tmpnam_s() function generates a string that is a valid file name and that is not the same as the name of an existing file [[ISO/IEC TR 24731-2006]]. The function is potentially capable of generating TMP_MAX_S different strings, but any or all of them may already be in use by existing files and thus not be suitable return values. The lengths of these strings must be less than the value of the L_tmpnam_s macro.

...
FILE *file_ptr;
char filename[L_tmpnam_s];

if (tmpnam_s(filename, L_tmpnam_s) != 0) {
  /* Handle Error */
}

if (!fopen_s(&file_ptr, filename, "wb+")) {
  /* Handle Error */
}
...

This solution is also non-compliant because it violates [[FIO32-C]] and [[FI042-C]].

Non-Compliant Code Example: mktemp()

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 'X's appended to it, for example /tmp/temp.XXXXXX. The trailing '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 'X's provided.

...
FILE *temp_ptr;
char temp_name[] = "/tmp/temp-XXXXXX";

if (mktemp(temp_name) == NULL) {
  /* Handle Error */
}
temp_ptr = fopen(temp_name,"w+");
if (temp_ptr == NULL) {
  /* Handle Error */
}
...

This solution is also non-compliant because it violates [[FIO32-C]] and [[FI042-C]].

Compliant Solution: mkstemp() (POSIX)

A reasonably secure solution for generating random file names is to use the mkstemp() function. The mkstemp() function is available on systems that support the Open Group Base Specifications Issue 4, Version 2 or later.

A call to mkstemp() replaces the six Xs in the template string with six randomly-selected characters:

char template[] = "/tmp/fileXXXXXX";
if ((fd = mkstemp(template)) == -1) {
   err(1, "random file");
}

The mkstemp() algorithm for selecting file names has proven to be immune to attacks.

char sfn[15] = "/tmp/ed.XXXXXX";
FILE *sfp;
int fd = -1;

if ((fd = mkstemp(sfn)) == -1 || (sfp = fdopen(fd, "w+")) == NULL) {
  if (fd != -1) {
    unlink(sfn);
    close(fd);
  }
  fprintf(stderr, "%s: %s\n", sfn, strerror(errno));
  return (NULL);
}

Implementation Details

In many older implementations, the name is a function of process ID and time--so it is possible for the attacker to guess it and create a decoy in advance. FreeBSD has recently changed the mk*temp() family to get rid of the PID component of the filename and replace the entire thing with base-62 encoded randomness. This raises the number of possible temporary files for the "default" usage of 6 X's significantly, meaning that even mktemp() with 6 X's is reasonably (probabilistically) secure against guessing, except under very frequent usage Kennaway 00.

Compliant Solution: tmpfile_s() (ISO/IEC TR 24731-1 )

The ISO/IEC TR 24731-1 function tmpfile_s() creates a temporary binary file that is different from any other existing file
that is automatically removed when it is closed or at program termination. If the program terminates abnormally, whether an open temporary file is removed is implementation-defined.

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-shared) access and has a file permission that prevents other users on the system from accessing the file.

The tmpfile_s() function is available on systems that support ISO/IEC TR 24731-1 (e.g., Microsoft Visual Studio 2005).

...
if (tmpfile_s(&file_ptr)) {
  /* Handle Error */
}
...

The tmpfile_s() function may not be compliant with [[FI042-C]] for implementations where the temporary file is not removed if the program terminates abnormally.

Risk Assessment

A protected system file to which the symbolic link points can be overwritten when a vulnerable program is executed.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FIO32-C

2 (high)

2 (probable)

2 (medium)

P8

L2

References

  • No labels