Safeguard
Application Security•

CWE-61 UNIX Symbolic Link Following: Causes, Checks, Fixes

Symlink following lets a local attacker redirect a privileged program's file access. How CWE-61 works and how to open files safely.

Safeguard Security Team
5 min read

A symbolic link is a small file whose job is to point somewhere else. That is useful for administrators and dangerous for programs that open files without asking what the name resolves to. CWE-61, UNIX Symbolic Link (Symlink) Following, is the case where a product opens a file or directory and does not sufficiently account for a symlink that resolves to a target outside its intended control sphere.

MITRE adds that a product allowing symlinks as part of paths, in internal code or through user input, can let an attacker spoof the link and traverse the file system or access arbitrary files. The link can permit reading, writing or corrupting a file the attacker could not otherwise reach.

It is a specific case of the broader CWE-59, link following. The weakness is not language specific, and it mostly bites privileged programs and shared directories on Unix like systems.

How it happens

The attacker needs write access to a directory the target program uses, such as a temporary directory or a log folder. They create a symlink at a name the program will use, pointing at a file the program can reach and they cannot.

The program then opens that name. The kernel follows the link. The program's privileges apply to the target.

The record's examples:

  • Some versions of Perl followed symbolic links when running with the -e option, allowing local users to overwrite arbitrary files (CVE-1999-1386).
  • A text editor followed symbolic links when creating a rescue copy during an abnormal exit, letting local users overwrite files of other users (CVE-2000-1178).
  • An antivirus update allowed local users to create or append to arbitrary files via a symlink attack on a logfile (CVE-2004-0217).
  • A symlink attack allowing local users to overwrite files (CVE-2003-0517).

A second form is the time of check, time of use race. The program checks that a path is safe, then opens it. In the gap, the attacker swaps the path for a link.

/* Vulnerable: check then use, and open follows links */
if (access("/tmp/app.log", W_OK) == 0) {
    FILE *f = fopen("/tmp/app.log", "a");   /* may now be a symlink */
    fprintf(f, "%s\n", msg);
    fclose(f);
}

Archive extraction is a common modern place for the same idea. If an archive can contain a symlink entry and then a regular file written through that link, the extractor writes outside its target folder. Review any code that unpacks archives, and treat link entries as untrusted.

What an attacker gains

MITRE lists reading and modifying files and directories, which maps to confidentiality and integrity. The practical forms are overwriting configuration, startup or data files, appending attacker chosen content to a sensitive file, or reading files that hold secrets.

How to find it

MITRE lists automated static analysis, which can follow data from input to file operations. Beyond tooling:

  1. Grep for fopen, open, creat, unlink, chmod and chown calls on paths in shared directories.
  2. Look for patterns where a path is checked and then used, such as access then open, or stat then open.
  3. Look for predictable names in world writable locations.
  4. In a test setup, create a symlink at each expected file name pointing at a canary file, run the feature, and see whether the canary changes.
  5. For archive handling, build a test archive with a link entry and see where files land.

How to fix it

Use the file system and the operating system to enforce the rule, not a path string check.

  1. Open with O_NOFOLLOW so the open fails if the final component is a link, and combine it with O_CREAT | O_EXCL when creating.
  2. Use secure temp file functions such as mkstemp, which create files with unpredictable names.
  3. Do the check on the opened descriptor, using fstat, not on the path beforehand.
  4. Keep working files in a directory owned by the service account, with permissions that block other users. MITRE's own mitigation is least privilege and compartmentalization, since denying access to a file or directory can stop an attacker replacing it with a link.
  5. Where you must accept a path, resolve it with realpath and confirm it is inside the allowed root, then open it without following links.
  6. When extracting archives, refuse link entries or verify that every written path stays inside the destination after resolution.
  7. Drop privileges when touching user controlled locations.
/* Fixed: no link following, exclusive create, check the descriptor */
int fd = open("/var/lib/myapp/app.log",
              O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600);
if (fd < 0) { perror("open"); return -1; }
struct stat st;
if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode)) { close(fd); return -1; }
write(fd, msg, strlen(msg));
close(fd);
# Python: refuse to follow a final symlink
import os
fd = os.open("/var/lib/myapp/state.json",
             os.O_WRONLY | os.O_CREAT | os.O_NOFOLLOW, 0o600)

O_NOFOLLOW only guards the last path component. Directories earlier in the path can still be links, so keep the directory itself under your control.

If the vulnerable code is in a tool you depend on, update it. A free in-browser scan at gold.safeguard.sh/scan lists known issues in a dependency file. No login, the file stays on your machine, up to 100 dependencies.

Quick check

What does a symlink attack need from the attacker?

Write access to a directory the target program uses, so they can plant a link at a name it will open. The program's own privileges do the rest.

Does O_NOFOLLOW solve everything?

It stops the final component from being followed. Parent directories can still be links, so also keep working directories private to the service account.

How does this relate to CWE-59?

CWE-59 is the general link following class. CWE-61 is the Unix symbolic link case.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.