CVE-2026-54056
Description
A symlink following vulnerability in Kitty's kitten dnd allows remote attackers to overwrite arbitrary files via crafted drag-and-drop sequences.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A symlink following vulnerability in Kitty's kitten dnd allows remote attackers to overwrite arbitrary files via crafted drag-and-drop sequences.
Vulnerability
In Kitty versions 0.47.0 and 0.47.1, the kitten dnd component (used for drag-and-drop over SSH) contains a symlink following vulnerability when processing remote drag-and-drop staging. On case-sensitive filesystems, duplicate remote basenames are not de-duplicated. The vulnerable code path resides in kittens/dnd/drop.go and tools/utils/file_at_fd.go. When a remote text/uri-list drop is staged, a regular-file write uses utils.CreateAt() / openat(O_RDWR|O_CREAT|O_TRUNC) without the O_NOFOLLOW flag, allowing a previously staged symlink to be followed and written outside the staging directory [1].
Exploitation
An attacker with the ability to send remote drag-and-drop events (e.g., via SSH) can first create a staged symlink within the temporary staging directory pointing to a target file writable by the local kitty user. The attacker then sends a second entry with the same basename as a regular file. Due to the lack of deduplication on case-sensitive filesystems, the second write follows the existing symlink and overwrites or truncates the target file before the final overwrite confirmation dialog is presented [1].
Impact
Successful exploitation allows a remote attacker to overwrite or truncate arbitrary files that the local kitty user has write access to. This could lead to data loss, denial of service, or potential privilege escalation if critical system or user files are overwritten [1].
Mitigation
Kitty version 0.47.2 patches the issue by adding the O_NOFOLLOW flag to the relevant file operations, preventing symlink following. Users should upgrade to 0.47.2 or later. No workarounds are documented for unpatched versions [1].
AI Insight generated on Jun 12, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1- Range: >=0.47.0,<=0.47.1
Patches
18996aa798c77dnd kitten: Create regular files with O_EXCL to avoid symlink attacks
2 files changed · +7 −1
kittens/dnd/drop.go+1 −1 modified@@ -823,7 +823,7 @@ func (dnd *dnd) on_remote_drop_data(cmd DC) (err error) { e.item_type = cmd.Xp switch cmd.Xp { case 0: - f, err := utils.CreateAt(e.base_dir.handle, e.name, 0o666) + f, err := utils.CreateExclusiveAt(e.base_dir.handle, e.name, 0o666) if err != nil { return err }
tools/utils/file_at_fd.go+6 −0 modified@@ -79,6 +79,12 @@ func CreateAt(dirFile *os.File, name string, permissions os.FileMode) (*os.File, return openAt(dirFile, name, unix.O_RDWR|unix.O_CREAT|unix.O_TRUNC, permissions) } +// CreateExclusiveAt creates a file relative to the directory pointed to by +// dirFile. Fails if a directory entry with the same name already exists. +func CreateExclusiveAt(dirFile *os.File, name string, permissions os.FileMode) (*os.File, error) { + return openAt(dirFile, name, unix.O_RDWR|unix.O_CREAT|unix.O_EXCL, permissions) +} + // Create the specified directory, open it and return the file object. If the // directory already exists, it is opened and returned, without changing its // permissions, matching the behavior of CreateAt().
Vulnerability mechanics
Root cause
"`utils.CreateAt()` calls `openat()` with `O_RDWR|O_CREAT|O_TRUNC` but without `O_NOFOLLOW`, so a staged symlink followed by a same-name regular-file entry allows writing outside the staging directory."
Attack vector
An attacker acting as a malicious remote drag-and-drop source sends two entries with the same basename: first a symlink entry pointing to a victim-writable file outside the staging directory, then a regular-file entry with the same name whose payload will be written. On case-sensitive filesystems, duplicate basenames are not deduplicated, so the second entry calls `utils.CreateAt()` which follows the staged symlink (because `O_NOFOLLOW` is missing) and overwrites the target file before the final overwrite confirmation dialog runs. [CWE-367] [ref_id=1]
Affected code
The vulnerability is in the `kitten dnd` remote drag-and-drop staging logic in `kittens/dnd/drop.go` (the `on_remote_drop_data` function) and the `utils.CreateAt()` helper in `tools/utils/file_at_fd.go`. `CreateAt()` calls `openat()` with `O_RDWR|O_CREAT|O_TRUNC` but without `O_NOFOLLOW`, so a staged symlink created by a remote entry is followed when a subsequent regular-file entry with the same name is written, allowing the write to escape the staging directory. [patch_id=5750827] [ref_id=1]
What the fix does
The patch introduces `CreateExclusiveAt()` in `tools/utils/file_at_fd.go` which calls `openat()` with `O_EXCL` added to the flags, causing the open to fail if a file or symlink with the same name already exists in the staging directory. The call site in `kittens/dnd/drop.go` is changed from `utils.CreateAt()` to `utils.CreateExclusiveAt()`. This prevents the vulnerable follow-the-symlink write because opening the regular file will now return an error when a symlink entry of the same name is already staged, breaking the attack before any data is written outside the staging directory. [patch_id=5750827]
Preconditions
- authThe local kitty user initiates a remote drag-and-drop action (e.g., over SSH) to a controlled remote source.
- configThe local filesystem is case-sensitive (e.g., typical Linux ext4/XFS); on case-insensitive filesystems the duplicate-name protection renames entries, preventing the collision.
- inputThe attacker can send multiple drag-and-drop entries with the same basename (a symlink entry followed by a regular-file entry).
Reproduction
The advisory in [ref_id=1] includes a complete Go unit test PoC (`TestRemoteDnDDuplicateSymlinkRegularWriteEscapesStaging`) that creates a staging directory, a target file outside it, then simulates a malicious remote DnD source sending a symlink entry named `same-name` pointing to the target, followed by a regular-file entry also named `same-name` with payload `POC`. The test passes (the target is overwritten), confirming the symlink-following write escape. The PoC writes only inside Go's temporary test directory and is safe to run.
Generated on Jun 12, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
1News mentions
0No linked articles in our index yet.