Symlink replacement is non-atomic (TOCTOU race window) #23

Open
opened 2026-04-29 23:31:26 +02:00 by heiko · 0 comments
Owner

Summary

In cmd/cert-proxy-client/cert/cert.go:233-236, the symlink update is performed as:

_ = os.Remove(name)
err = os.Symlink(filepath.Base(infixed[name]), name)

Between Remove and Symlink there is a window where:

  1. Readers get ENOENT (brief unavailability of cert/key files)
  2. A local attacker with write access to the directory could place a symlink pointing elsewhere

Exploitation Scenario

A local attacker races the Remove/Symlink window and creates a symlink pointing to a world-readable location. The next cert-proxy-client run writes the private key through that symlink to the attacker-controlled path. Requires write access to the certbase directory.

Use atomic symlink replacement via rename:

tmpLink := name + ".tmp"
os.Remove(tmpLink)
os.Symlink(filepath.Base(infixed[name]), tmpLink)
os.Rename(tmpLink, name)  // atomic on most filesystems

This eliminates both the ENOENT window and the race condition.

## Summary In `cmd/cert-proxy-client/cert/cert.go:233-236`, the symlink update is performed as: ```go _ = os.Remove(name) err = os.Symlink(filepath.Base(infixed[name]), name) ``` Between `Remove` and `Symlink` there is a window where: 1. Readers get `ENOENT` (brief unavailability of cert/key files) 2. A local attacker with write access to the directory could place a symlink pointing elsewhere ## Exploitation Scenario A local attacker races the Remove/Symlink window and creates a symlink pointing to a world-readable location. The next cert-proxy-client run writes the private key through that symlink to the attacker-controlled path. Requires write access to the certbase directory. ## Recommended Fix Use atomic symlink replacement via rename: ```go tmpLink := name + ".tmp" os.Remove(tmpLink) os.Symlink(filepath.Base(infixed[name]), tmpLink) os.Rename(tmpLink, name) // atomic on most filesystems ``` This eliminates both the ENOENT window and the race condition.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
heiko/cert-proxy#23
No description provided.