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

Compare with Current View Page History

« Previous Version 47 Next »

Many common operating systems such 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.

Frequently, there is no need to check for the existence of symbolic links because this problem can be solved using other techniques. When opening an existing file, for example, the simplest solution is often to drop privileges to the privileges of the user. This solution permits the use of links while preventing access to files for which the user of the application is not privileged.

When creating new files, it may be possible to use functions that only create a new file where a file does not already exist. This prevents the application from overwriting an existing file during file creation (see FIO03-A. Do not make assumptions about fopen() and file creation).

In rare cases, it is necessary to check for the existence of symbolic or hard links to ensure that a program is reading from an intended file and not a different file in another directory. In these cases, avoid creating a race condition when checking for the existence of symbolic links (see POS35-C. Avoid race conditions while checking for the existence of a symbolic link).

This non-compliant code example opens the file specified by the string file_name for read/write access and then writes user-supplied data to the file.

int fd = open(file_name, O_RDWR);
if (fd == -1) {
   /* handle error */
}
write(fd, userbuf, userlen);

If the process is running with elevated privileges, an attacker can exploit this code by, for example, replacing the file with a symbolic link 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.

Some systems provide the O_NOFOLLOW flag to help mitigate this problem. The flag will be required by the forthcoming POSIX.1-2008 standard, and so will become more portable over time [[Austin Group 08]]. If the flag is set and the supplied file_name is a symbolic link, then the open will fail.

int fd = open(file_name, O_RDWR | O_NOFOLLOW);
if (fd == -1) {
  /* handle error */
}
write(fd, userbuf, userlen);

Note that this code does not check for hard links,

This solution uses the lstat()-open()-fstat() pattern illustrated in FIO05-A. Identify files using multiple file attributes.

char* file_name = /* some value */

struct stat orig_st;
if (lstat( file_name, &orig_st) != 0) {
  /* handle error */
}

if (!S_ISREG( orig_st.st_mode)) {
  /* file is irregular or symlink */
}

int fd = open(file_name, O_RDWR);
if (fd == -1) {
  /* handle error */
}

struct stat new_st;
if (fstat(fd, &new_st) != 0) {
  /* handle error */
}

if (orig_st.st_dev != new_st.st_dev ||
    orig_st.st_ino != new_st.st_ino) {
  /* file was tampered with during race window */
}

/* ... file is good, operate on fd ... */

This code is still subject to a TOCTOU race condition, but before doing any operation on the file, it verifies that the file opened is the same file as was previously checked (by checking the file's device and i-node.) Thus the code will recognize if an attacker has tampered with the file during the race window, and can operate accordingly.

Note that this code does not check for hard links,

Hard links can be insidious, because if a file has multiple hard links, it is impossible to distinguish which link came first. Likewise, it is impossible to distinguish which link might have originated from a malicious attacker.

One way to deal with hard links is simply to disallow opening of any file with two or more hard links. The following code snippet, when inserted into the previous example will identify if a file has multiple hard links.

if (orig_st.st_nlink > 1) {
  /* file has multiple hard links */
}

Since a hard link may not be created if the link and the linked-to file are on different devices, many platforms will place system-critical files on a different device than user-editable files. For instance, the / directory, which contains critical system files like /etc/passwd would live on one hard drive, while the /home directory, which contains user-editable files, would live on a separate hard drive. This would prevent users from creating hard links to /etc/passwd.

Failing to check for the existence of links can result in a critical system file being overwritten, leading to a data integrity violation.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

POS01-A

medium

likely

high

P6

L2

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

[[Austin Group 08]]
[[Open Group 04]] open()
[[Seacord 05]] Chapter 7, "File I/O"


POS00-A. Avoid race conditions with multiple threads      50. POSIX (POS)       POS02-A. Follow the principle of least privilege

  • No labels