Race Condition Vulnerability
Introduction
A race condition occurs when two concurrent threads or processes access a shared resource in a way that produces different results depending on the interleaving of their execution. The outcome depends on timing — whoever “wins the race” determines what happens.
In the security context, race condition vulnerabilities exploit the window of time between a check and a subsequent use of the same resource. This class of attack is formally named TOCTOU (Time-of-Check to Time-of-Use). The core idea: the resource that was checked is not the same (or is no longer in the same state) as the resource that is ultimately used.
Another well-known attack in this family is the Dirty COW vulnerability, covered in the next post.
Race Condition Vulnerability
The canonical TOCTOU scenario in Unix involves Set-UID programs interacting with the /tmp directory (which is world-writable):
access()— checks file permissions using the real UID (RUID). Returns 0 if the calling user has permission.open()— opens the file using the effective UID (EUID). Returns the lowest available file descriptor on success.
When a Set-UID root program calls access() to verify the real user has permission, then calls open() which operates with the elevated EUID, there is a window between the two calls. If an attacker can swap the file via a symlink during that window — replacing the innocuous file with a symlink pointing to a privileged file like /etc/passwd — the open() call will open the privileged file with root’s effective UID.

Launch Attack
Target: /etc/passwd. Adding a root-equivalent account grants full system control. The password field in /etc/passwd stores a one-way hash. Options for the hash:
- Reference an existing hash from
/etc/shadowfor a known password. - Generate a hash via
adduserand copy it. - Use
U6aMy0wojraho— the known hash of an empty password.
The attack structure:
- Target program — a vulnerable Set-UID root binary that calls
access()thenopen()(the TOCTOU pattern). - Attack process — a tight loop that repeatedly creates and removes a symlink at the checked path, alternating between pointing to a benign file (to pass
access()) and/etc/passwd(to catch theopen()call).usleep()is tuned to maximize the race win rate. - Shell script — orchestrates both processes and runs them concurrently until the race is won.
Countermeasures
The four standard defenses, each targeting a different aspect of the vulnerability:
-
Eliminate the window between check and use — Atomic Operations
Use
open()withO_EXCL | O_CREATflags, which atomically checks existence and creates the file. If the file already exists (even as a symlink), the call fails. This prevents the window from being exploitable. However, Linux does not universally support atomic check-and-open for all cases. -
Prevent actions within the window — Principle of Least Privilege
Instead of using
access()to verify the real user’s permission and then callingopen()with elevated EUID, useseteuid(getuid())to permanently drop privileges to the real UID before the file operation. This eliminates the privilege gap that makes the attack dangerous. The program only runs with elevated privileges when genuinely needed. -
Make winning the race harder — Repeated Check and Use
Call
access()multiple times before and afteropen()and verify the file hasn’t changed (e.g., by comparing inode numbers). This raises the bar — the attacker must win every race simultaneously — but does not eliminate the vulnerability. A sufficiently fast attacker with enough attempts can still succeed. -
Limit damage after the race is won — Sticky Symlink Protection
Ubuntu provides a kernel-level protection for world-writable sticky directories (such as
/tmp). A sticky directory has the sticky bit set (chmod +t), which normally prevents users from deleting or renaming files they don’t own. With sticky symlink protection enabled, the kernel refuses to follow a symlink in a sticky directory if the symlink’s owner differs from the file’s owner or the directory’s owner. This makes/tmp-based symlink attacks ineffective.
Reference
Introduction
- Race condition problem이란 when two concurrent threads of execution access a shared resource in a way that unintentionally produces different results depending on the sequence or timing of the processes of threads이다.
- Race condition vulnerability는 간단하게 말하면 check와 use사이의 window의 허점을 노리는 공격이다.
- 이것을 time-of-check to time-of-use(TOCTTOU)라고 부른다.
- 또 다른 공격으로는 Dirty COW라는 공격이 있는데 이거는 다음 포스팅에서 다룰 예정이다.
Race Condition Vulnerability
- real ID가 normal user이고 effective user ID가 root일때 /tmp 디렉토리와 같은 world-writable한 곳에 접근을 하기 위한 코드에서 발생 할 수 있는 취약점이다.
- access()는 real ID와 file의 permission을 비교해서 permission을 가지고 있으면 0을 리턴한다. 즉, 정상 작동하면 0을 return한다는 말이다.
- open()함수는 real ID가 아닌 effective ID를 가지고 file의 permission을 확인해서 연다.
- open()함수는 정상적으로 파일을 열었을 시에는 사용되지 않고 있는 file descriptor중에서 가장 낮은 번호의 양수 값을 리턴한다.
- 이 공격에서의 핵심은 root 권한을 가진 privileged program이 real ID를 확인하는 access를 통과한 후 effective ID를 확인하는 open에서 생기는 허점을 노리는 공격이다. check와 use사이의 window에서 symlink를 통해 race를 이기고 공격을 하는 것이다.

Launch Attack
- protected file중에서 /etc/passwd에 root 아이디를 추가하면 모든 통제권을 가질 수 있게 된다.
- /etc/passwd에 사용자 비밀번호가 들어갈때에는 one-way hash value가 들어간다. 이때 adduser로 사용자를 생성할때 /etc/shadow에 입력한 비밀번호의 one-way hash value가 있을거고 그것을 /etc/passwd 파일에서 참고한다.
- 따라서 새로운 root 유저를 생성할때 비밀번호를 내가 아는 부분의 /etc/shadow 비밀번호를 참고할 것인지, adduser로 hash value생성후 가져올 것인지 아니면 U6aMy0wojraho라는 엔터값에 대한 hash value를 쓸 것인지 정해야한다.
- target program은 access와 open을 하는 구조(TOCTTOU)여야하고 attack_process 프로그램은 the window에서 symlinkn를 걸었다 풀었다 반복하는 프로그램이어야 한다. 적절한 usleep()코드로 background에서 계속 작용하면 된다. 마지막으로 이 모든 것을 자동적으로 실행시켜줄 shell script하나 짜서 공격하면 된다.
Coiuntermeasure
- how do we eliminate the window between check and use?
- Atomic Operation
- 이거는 os level의 support가 필요하다. open() system call의 경우 O_EXCL과 O_CREAT를 사용하면 file이 이미 있으면 열지 않게 된다. 따라서 symlink는 아예 사용조차 못하게 되는 것이다. 하지만 linux system에는 이 기능을 지원하지 않고 있다.
- Atomic Operation
- how do we prevent others from doing anything inside the window?
- Principle of Least Privilege
- access를 통해 확인 하는 것이 아니라 seteuid(getuid())를 통해서 애초에 과도한 권한 설정을 막는것이다.
- Principle of Least Privilege
- how do we make it difficult for attackers to win the “race”?
- Repeating Check and Use
- 단순히 access, check의 횟수를 반복시키는것이다. 하지만 이것역시 여러번 race에서 이기면 되는 것이라서 강력한 countermeasure라고 하기는 힘들 것 같다.
- Repeating Check and Use
- how do we prevent attackers from causing damages after they have won the “race”?
- Sticky Symlink Protection
- Ubuntu에서 제공하는 protection mechanism이다.
- 이거는 world-writable sticky directories(Such as /tmp)에만 적용된다.
- sticky directory란 linux file system에서 sticky bit을 쓰는 directory를 의미한다.
- sticky bit가 set되어 있으면 symlink’s owner가 file’s owner 혹은 the directory’s owner가 아니면 rename이나 delete가 되지 않게 해준다.
- Sticky Symlink Protection