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

Compare with Current View Page History

« Previous Version 7 Next »

Many common operating systems usch as Windows and UNIX support file links including hard links, symbolic (soft) links, and virtual drives. Hard links can be created in UNIX with the ln command, or in Windows operating systems by calling the CreateHardLink() function. Symbolic links can be created in UNIX using the ln -s command or in Windows by using directory junctions in NTFS or the Linkd.exe (Win 2K resource kit) or "junction" freeware. Virtual drives can also be created in Windows using the subst command.

File links can create security issues for programs that fail to consider the possibility that the file being opened may actually be a link to a different file. This is especially dangerous when the vulnerable program is running with elevated privileges.

To ensure that a program is reading from an intended file, and not a different file in another directory, it is necessary to check for the existence of symbolic or hard links.

This non-compliant code example open the file specified by the string "/home/rcs/.conf" for read/write exclusive access, and then writes user supplied data to the file.

   if ((fd = open("/home/rcs/.conf", O_EXCL|O_RDWR, 0600)) == -1) {
       /* handle error */
   }
   write(fd, userbuf, userlen);

If the process is running with elevated privileges, an attacker can exploit this code, for example, by creating a link from .conf to the {{/etc/passwd }} authentication file. The attacker can then overwrite data stored in the password file to create a new root account with no password. As a result, this attack can be used to gain root privileges on a vulnerable system.

The only function available on POSIX systems to collect information about a symbolic link rather than its target is the lstat() function. This non-compliant code example uses the lstat() function to collection information about the file, and then checks the st_mode field to determine if the file is a symbolic link.

struct stat lstat_info;
int fd;
if (lstat("some_file", &lstat_info) == -1) {
  /* handle error */
}
if (!S_ISLNK(lstat_info.st_mode)) {
   if ((fd = open("some_file", O_EXCL|O_RDWR, 0600)) == -1) {
       /* handle error */
   }
}
write(fd, userbuf, userlen);

This non-compliant code example eliminates the race condition by:

  1. calling the lstat() the filename.
  2. calling open() to open the file.
  3. call fstat() on the file descriptor returned by open()
  4. compare the fine information returned by the calls to lstat() and fstat() to ensure that the files are the same.
struct stat lstat_info, fstat_info;
int fd;
if (lstat("some_file", &lstat_info) == -1) {
  /* handle error */ 
}
if ((fd = open("some_file", O_EXCL|O_RDWR, 0600)) == -1) {
  /* handle error */
}
if (fstat(fd, &fstat_info) == -1) {
  /* handle error */
}
if (lstat_info.st_mode == fstat_info.st_mode &&
    lstat_info.st_ino == fstat_info.st_ino  &&
    lstat_info.st_dev == fstat_info.st_dev) {
  write(fd, userbuf, userlen);
}

Tihs eliminate the TOCTOU condition because fstat() is applied to file descriptors, not file names, so the file passed to fstat() must be identical to the file that was opened. The lstat() function does not follow symbolic links, but fstat() does. Comparing modes using the st_mode field is sufficient to check for a symbolic link.
Comparing i-nodes using the st_ino fields and devices using the st_dev fields ensures that the file passed to lstat() is the same as the file passed to fstat() [[FIO05-A. Identify files using multiple file attributes]].

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

POS01-A

2 (medium)

3 (likely)

2 (medium)

P12

L1

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

[[ISO/IEC 9899-1999]] Section 7.19.7.2, "The fgets function"
[[Seacord 05]] Chapter 7, "File I/O"

  • No labels