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

Compare with Current View Page History

« Previous Version 26 Next »

File names on many operating systems, including Windows and POSIX systems, may be used to access non-regular files with special properties. These non-regular files include character special files, block special files, FIFO special files, symbolic links, sockets, and directories. Other file types may be supported on specific platforms.

If a program is expecting to access a regular file, and the file name is either specified by the user, or is not in a secure directory, the program must ensure that the file is a regular file, and is not a symbolic link, directory, or any other kind of non-regular file.

Non-regular Files

Many operating systems support file links including symbolic (soft) links, and virtual drives. Symbolic links can be created in POSIX 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 be a link to a different file. This is especially dangerous when the vulnerable program is running with elevated privileges.

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 rule FIO00-J. Do not overwrite an existing file while attempting to create a new file for more information.

Device Files

File names on many operating systems may be used to access device files. Reserved MS-DOS device names include AUX, CON, PRN, COM1, and LPT1. Character special files and block special files on POSIX systems are used to apply access rights and to direct operations on the files to the appropriate device drivers.

Performing operations on device files that are intended for ordinary character or binary files can result in crashes and denial-of-service attacks. For example, when Windows attempts to interpret the device name as a file resource, it performs an invalid resource access that usually results in a crash [[Howard 2002]].

Device files in UNIX can be a security risk when an attacker can access them in an unauthorized way. For instance, if attackers can read or write to the /dev/kmem device, they may be able to alter their priority, UID, or other attributes of their process or simply crash the system. Similarly, access to disk devices, tape devices, network devices, and terminals being used by other processes all can lead to problems [[Garfinkel 1996]].

On Linux, it is possible to lock certain applications by attempting to open devices rather than files. Consider the following example:

/dev/mouse
/dev/console
/dev/tty0
/dev/zero

A Web browser that failed to check for these devices would allow an attacker to create a Web site with image tags such as <IMG src="file:///dev/mouse"> that would lock the user's mouse.

Noncompliant Code Example

In this noncompliant code example, an attacker could specify the name of a locked device or a FIFO file, causing the program to hang when opening a file.

String file = /* provided by user */
InputStream in = new FileInputStream(file);
// ...
in.close();

Noncompliant Code Example (Java 1.7)

This noncompliant code example uses the try-with-resources statement from Java 1.7 to open the file. While this guarantees the file's successful closure if an exception is thrown, it is subject to the same vulnerabilities as the previous example.

String filename = /* provided by user */
Path file = new File(filename).toPath();
try (InputStream in = Files.newInputStream(file)) {
   // read file
} catch (IOException x) {
  // handle error
}

Noncompliant Code Example (Java 1.7: isRegularFile())

This noncompliant code example first checks that the file is a regular file before opening it.

String filename = /* provided by user */
Path file = new File(filename).toPath();
try {
  BasicFileAttributes attr = Files.readAttributes(file, BasicFileAttributes.class);

  // Check
  if (!attr.isRegularFile()) {
    System.out.println("Not a regular file");
    return;
  }
  // other necessary checks

  // Use
  try (InputStream in = Files.newInputStream(file)) {

      // read file
    };
} catch (IOException x) {
  // handle error
}

This code can still be circumvented by a symbolic link. By default, the readAttributes() method follows symbolic links and reads the file attributes of the final target of the link. The result is that the program may reference a file other than the one intended.

Noncompliant Code Example (Java 1.7: NOFOLLOW_LINKS)

This noncompliant code example checks the file by calling the readAttributes() method with the NOFOLLOW_LINKS link option to prevent the function from following symbolic links and allowing the detection of symbolic links.

String filename = /* provided by user */
Path file = new File(filename).toPath();
try {
  BasicFileAttributes attr = Files.readAttributes(
    file, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS
  );

  // Check
  if (!attr.isRegularFile()) {
    System.out.println("Not a regular file");
    return;
  }
  // other necessary checks

  // Use
  try (InputStream in = Files.newInputStream(file)) {

      // read file
    };
} catch (IOException x) {
  // handle error
}

This code is still vulnerabile to a TOCTOU race condition. For example, an attacker can replace the regular file with a symbolic link or other non-regular file after the code has completed its checks but before it opens the file.

Compliant Solution (Java 1.7: Check-Use-Check)

This compliant solution performs necessary checks and then opens the file. After opening the file, it performs a second check to make sure that the file has not been moved, and that the file it opened is the same one it checked. This reduces the chance that an attacker has changed the file between the check and the file's opening. In both checks, the file's fileKey attribute is examined. This serves as a unique key for identifying files, and is a more reliable indicator of a file's identity than its path.

[[J2SE 2011]] documents the fileKey attribute as follows:

Returns an object that uniquely identifies the given file, or null if a file key is not available. On some platforms or file systems it is possible to use an identifier, or a combination of identifiers to uniquely identify a file. Such identifiers are important for operations such as file tree traversal in file systems that support symbolic links or file systems that allow a file to be an entry in more than one directory. On UNIX file systems, for example, the device ID and inode are commonly used for such purposes.

The file key returned by this method can only be guaranteed to be unique if the file system and files remain static. Whether a file system re-uses identifiers after a file is deleted is implementation dependent and therefore unspecified.

File keys returned by this method can be compared for equality and are suitable for use in collections. If the file system and files remain static, and two files are the same with non-null file keys, then their file keys are equal.

String filename = /* provided by user */
Path file = new File(filename).toPath();
try {
  BasicFileAttributes attr = Files.readAttributes(
    file, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS
  );
  Object fileKey = attr.fileKey();

  // Check
  if (!attr.isRegularFile()) {
    System.out.println("Not a regular file");
    return;
  }
  // other necessary checks

  // Use
  try (InputStream in = Files.newInputStream(file)) {

      // Check
      BasicFileAttributes attr2 = Files.readAttributes(
        file, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS
      );
      Object fileKey2 = attr2.fileKey();
      if (fileKey != fileKey2) {
        System.out.println("File has been tampered with");
      }

      // read file
    };
} catch (IOException x) {
  // handle error
}

This solution is only secure if the file referenced by filename is maintained in a secure directory. FIO07-J. Do not create temporary files in shared directories describes how secure directories can be identified.

If the provided file is not in a secure directory, this compliant solution is still vulnerable. First, a TOCTOU race condition exists between the first check and open. During this race window, an attacker can replace the regular file with a symbolic link or other non-regular file. The second check detects this race condition but does not eliminate it; an attacker can still cause the system to block when opening the file. Second, an attacker could subvert this code by letting the check operate on a normal file, substituting the non-normal file for the open, and then resubstitute the normal file to circumvent the second check. This vulnerability exists because Java lacks any mechanism to obtain file attributes from a file by any means other than the file name, and the binding of the file name to a file object is reasserted every time the file name is used in an operation. Consequently, an attacker can switch out a file for one of the file types shown in the following table with the specified effect.

Type

Note on effect

another regular file

The 2nd check fails

FIFO

Either the file open fails, or the 2nd check fails

symbolic link

The 1st check fails because of the NOFOLLOW_LINKS link option

device

Either the file open fails, or the 2nd check fails. This can still be a problem if the device is one for which just opening (or closing) it causes something to happen.

Risk Assessment

Allowing operations to be performed on non-regular files that are only appropriate for regular files can result in denial-of-service attacks or privileged escalation exploits.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FIO04-J

medium

unlikely

medium

P4

L3

Related Vulnerabilities

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

Related Guidelines

Bibliography

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="c8952947-8d25-4df9-ac66-d5e58ba5ad10"><ac:plain-text-body><![CDATA[

[[Garfinkel 1996

AA. Bibliography#Garfinkel 96]]

Section 5.6, "Device Files"

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="bfb2862f-d73d-431e-9798-fcc59c61f25c"><ac:plain-text-body><![CDATA[

[[Howard 2002

AA. Bibliography#Howard 02]]

Chapter 11, "Canonical Representation Issues"

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="60b3f244-2bcb-4ca9-9145-e18851c1d9dd"><ac:plain-text-body><![CDATA[

[[J2SE 2011

AA. Bibliography#J2SE 11]]

The try-with-resources Statement

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="67bfb9cd-4b51-45d2-aa9b-286fe332a292"><ac:plain-text-body><![CDATA[

[[Open Group 2004

AA. Bibliography#Open Group 04]]

[open()

http://www.opengroup.org/onlinepubs/009695399/functions/open.html]

]]></ac:plain-text-body></ac:structured-macro>


FIO03-J. Create files with appropriate access permissions      12. Input Output (FIO)      FIO05-J. Do not create multiple buffered wrappers on a single InputStream

  • No labels