Today I Learned - Protected Symlinks
A DFIR.ch blog post explaining the Linux kernel's protected_symlinks (fs.protected_symlinks) TOCTOU mitigation, then demonstrating a bypass: symlinks placed inside a subdirectory of /tmp (which doesn't inherit the sticky bit) are still followed across privilege boundaries. The author walks through reproducing CVE-2023-33865, a symlink vulnerability in RenderDoc's /tmp/RenderDoc log directory, showing how a local attacker can get an arbitrary file written with another user's privileges despite protected_symlinks being enabled, and traces the exact kernel source-code condition responsible.
What it is
A DFIR.ch walkthrough of a gap in the Linux kernel's fs.protected_symlinks mitigation, demonstrated against CVE-2023-33865 (the RenderDoc /tmp/RenderDoc symlink bug). The takeaway that generalizes beyond RenderDoc: protected_symlinks=1 only guards symlinks inside a directory that is both sticky and world-writable. A world-writable subdirectory that does not carry the sticky bit — as any attacker-created subdir of /tmp will not — sits outside the protection entirely.
How it works
When a privileged/other-user process follows a symlink, may_follow_link in the kernel returns "allowed" early on any of four conditions. The last one is the hole: the check only trips when the parent directory is both sticky and world-writable:
if ((nd->dir_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH))
return 0;/tmp has the sticky bit, but a subdirectory created under it (mkdir /tmp/RenderDoc) does not inherit it. So a symlink placed inside /tmp/RenderDoc fails this test and is followed even across user boundaries.
The reproduction: Thomas creates /tmp/RenderDoc, plants a symlink named like RenderDoc's log file pointing at /home/sarah/dfir.ch; when Sarah writes to that path, the write lands in her home dir. The same write attempted directly in /tmp (sticky) returns Permission denied.
Preconditions / Affected
protected_symlinksset to1(its default on many systems) — the point is that even with it enabled you are exposed.- A privileged or other-user process that opens/writes a predictable path inside an attacker-controllable, world-writable, non-sticky subdirectory.
- Local attacker able to create the subdirectory and symlinks first (a plain unprivileged user).
What to do
The page states no patch or sysctl fix — the behavior is by design. The actionable engineering lesson: do not rely on protected_symlinks to cover application-created working directories. Applications that create shared subdirectories under /tmp (like RenderDoc) must set the sticky bit themselves and/or use per-user or unpredictable paths. When reviewing code, treat any world-writable subdir without S_ISVTX as unprotected against symlink races.
Caveats
- The author confirms the mitigation does still work where the malicious symlink must live in a directory that keeps the sticky bit: the Sudo/
sudoeditrace (symlink in/var/tmp/) is blocked, returning permission denied. So this is a scoped gap, not a wholesale defeat. - The bypass depends on the vulnerable app creating a non-sticky subdirectory; it does not weaken protection for symlinks placed directly in
/tmpitself.
flowchart TD
A[Process follows symlink] --> B{protected_symlinks on?}
B -->|no| OK[Follow allowed]
B -->|yes| C{same owner as follower?}
C -->|yes| OK
C -->|no| D{parent dir sticky AND world-writable?}
D -->|no: e.g. /tmp/RenderDoc| OK
D -->|yes: e.g. /tmp| BLOCK[Blocked]CVEs named here
- CVE-2023-33865
Technologies named here
- RenderDoc
- Linux kernel
- sudo / sudoedit
- Sudo
- Sysctl Explorer
Techniques named here
- Symlink-based TOCTOU race
- Sticky-bit non-inheritance bypass of protected_symlinks
Related in Offensive Security