CWE-62 describes a program that opens a file or directory by name without accounting for the chance that the name is a hard link to a target outside the area the program is meant to control. The program then reads, writes or deletes a file it was never supposed to touch.
It matters most for anything that runs with more privilege than the people who can write to its working directories: daemons, installers, cleanup jobs and setuid tools on Unix systems.
How it happens
A hard link is a second name for the same file on disk. Unlike a symbolic link, it is not a pointer that can be spotted by asking "is this a link?". The directory entry looks like any other file.
The pattern looks like this. A privileged program uses a file in a directory that a lower-privileged user can write to, such as a spool or temp location. The attacker deletes or renames that file and creates a hard link with the same name pointing at a sensitive file, such as /etc/passwd, which is the example MITRE gives. When the program opens the file by name, it is now operating on the sensitive file with its own privileges.
A program can check for symbolic links and still miss this. One of the observed examples, CVE-2001-1494 (see CVE-2001-1494), is described as a hard link attack that is interesting because the program checks against soft links.
Here is a vulnerable pattern in Python:
# Vulnerable: runs as root, directory is writable by other users
path = "/var/spool/app/report.txt"
with open(path, "w") as f:
f.write(report)
If report.txt is a hard link to a system file, this truncates and overwrites it.
What an attacker gains
MITRE lists confidentiality and integrity impact: the ability to read files or directories and to modify files or directories. In practice that means overwriting or deleting arbitrary files, and in some cases privilege escalation, because replacing a file a privileged program trusts lets the attacker act with that program's rights.
The observed examples show the range. CVE-2003-0578 describes a server that creates hard links and unlinks files as root, so local users can gain privileges by deleting and overwriting arbitrary files. CVE-2002-0793 covers hard link and possibly symbolic link following in an embedded operating system. CVE-1999-0783 describes a denial of service by hard linking a device special file to a file on an NFS file system. You can read each one on its Gold page: CVE-2003-0578, CVE-2002-0793 and CVE-1999-0783.
How to find it
The MITRE record lists no specific detection methods for this weakness, so this comes down to review and simple checks.
- Search for code that runs with elevated privileges and opens, writes, chmods or deletes files in directories that unprivileged users can write to.
- Check whether the code opens by name and then trusts the result without examining the file it actually opened.
- Look at the permissions on every directory those programs use. A world-writable directory is the usual starting condition.
How to fix it
MITRE's mitigation is architectural: follow the principle of least privilege when assigning access rights, and keep good compartmentalization so there are protected areas that can be trusted. Denying an attacker access to a file or its directory prevents them from replacing it with a link to a sensitive file.
In plain steps:
- Keep files used by privileged code in directories only that code (or a trusted account) can write to.
- Run the program with the lowest privilege it needs, so a bad open does less damage.
- Where you must work in a shared directory, open first and then inspect the open file descriptor rather than the name.
import os, stat
fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
st = os.fstat(fd)
if not stat.S_ISREG(st.st_mode) or st.st_nlink > 1:
os.close(fd)
raise PermissionError("unexpected file type or extra hard links")
The check runs on the descriptor you already hold, so the file cannot be swapped between the check and the use. A link count above one on a file that should have a single name is a reason to stop.
Quick check
Why does a check for symbolic links not stop this attack?
A hard link is another directory entry for the same file, not a pointer to a path. Nothing marks it as a link, so a symlink check passes.
What is the first fix MITRE suggests?
Least privilege and compartmentalization, so an attacker cannot write to the place where the file lives and cannot swap it for a link.