VYPR
Medium severity5.0NVD Advisory· Published Jun 12, 2026

CVE-2026-54055

CVE-2026-54055

Description

A TOCTOU race condition in kitty's file transmission protocol allows a child process to write arbitrary files via symlink substitution, patched in 0.47.2.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A TOCTOU race condition in kitty's file transmission protocol allows a child process to write arbitrary files via symlink substitution, patched in 0.47.2.

Vulnerability

In kitty versions prior to 0.47.2, the file transmission protocol's DestFile class (in file_transmission.py) uses os.open() without the O_NOFOLLOW flag, creating a TOCTOU race condition. A symlink check performed at init time (os.stat with follow_symlinks=False) is not re-checked at file creation, allowing an attacker to substitute a symlink between the check and the open() call. This affects all versions before the patch [1].

Exploitation

An attacker must have a malicious process running inside a kitty terminal. The user must interactively approve a file transfer for a seemingly benign filename. After the user presses accept, the attacker's process sends file data and creates a symlink from the approved path to an arbitrary target path. The race window is small but exploitable. The write occurs with the user's own permissions, so no privilege escalation beyond what the user already has [1].

Impact

A successful exploit allows the attacker to overwrite any file the user can write to, without the user's awareness or consent. This could result in corruption of configuration files, shell initialization scripts, or other user-owned data, potentially leading to arbitrary code execution in the user's session. The attacker does not gain root privileges, but can compromise the user's account [1].

Mitigation

The vulnerability is fixed in kitty version 0.47.2, released on 2026-06-12. The fix adds the O_NOFOLLOW flag to the os.open() call, preventing symlink following. Users should update immediately. No workarounds are available 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

2
  • Kovidgoyal/Kittyinferred2 versions
    <0.47.2+ 1 more
    • (no CPE)range: <0.47.2
    • (no CPE)range: <0.47.2

Patches

2
8996aa798c77

dnd kitten: Create regular files with O_EXCL to avoid symlink attacks

https://github.com/kovidgoyal/kittyKovid GoyalJun 3, 2026Fixed in 0.47.2via llm-release-walk
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().
    
4aa4a5c0567a

File transfer protocol: use O_NOFOLLOW when opening regular files

https://github.com/kovidgoyal/kittyKovid GoyalJun 3, 2026Fixed in 0.47.2via llm-release-walk
2 files changed · +3 1
  • docs/changelog.rst+2 0 modified
    @@ -188,6 +188,8 @@ Detailed list of changes
     
     - When watching for changed config files do not recursively watch all sub directories of the directory containing the config file (:iss:`10102`)
     
    +- File transfer protocol: use O_NOFOLLOW when opening regular files
    +
     
     0.47.1 [2026-05-28]
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
  • kitty/file_transmission.py+1 1 modified
    @@ -547,7 +547,7 @@ def write_data(self, all_files: dict[str, 'DestFile'], data: bytes | memoryview,
                 if self.actual_file is None:
                     self.make_parent_dirs()
                     self.unlink_existing_if_needed()
    -                flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC | getattr(os, 'O_CLOEXEC', 0) | getattr(os, 'O_BINARY', 0)
    +                flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC | getattr(os, 'O_CLOEXEC', 0) | getattr(os, 'O_BINARY', 0) | getattr(os, 'O_NOFOLLOW', 0)
                     self.actual_file = open(os.open(self.name, flags, self.permissions), mode='r+b', closefd=True)
                 af = self.actual_file
                 if decompressed or is_last:
    

Vulnerability mechanics

Root cause

"The `os.open()` call in the file transmission protocol lacks the `O_NOFOLLOW` flag, enabling a TOCTOU race where an attacker can substitute a symlink between the initial symlink check and the file open, causing the write to target an arbitrary file."

Attack vector

A malicious child process running inside the kitty terminal sends a file transmission command with a benign file name (e.g., `/tmp/report.txt`). After the user confirms the transfer (the dialog shows only the benign file name [ref_id=1]), the child process triggers the creation of a `DestFile` object — at this point a `stat` check sees no symlink and sets `needs_unlink = False`. The attacker then creates a symlink pointing from the benign path to a sensitive target (e.g., `~/.bashrc`). When the child process sends the `Action.data` command, `write_data()` calls `os.open()` without `O_NOFOLLOW` and follows the symlink, overwriting the unintended file. The timing is controlled by the attacker because each step is triggered by separate, asynchronously-processed OSC escape sequences [CWE-367, ref_id=1].

Affected code

The vulnerability resides in `kitty/file_transmission.py` in the `DestFile` class, specifically the `write_data()` method at line 550-551. The `os.open()` call omitted the `O_NOFOLLOW` flag, meaning the open operation would follow symlinks. The symlink check performed in `DestFile.__init__()` at line 454-457 is done only once at initialization, creating a TOCTOU window — an attacker can create a symlink after the check but before the file is actually opened.

What the fix does

The primary fix in commit `4aa4a5c0567a925...` [patch_id=5749247] adds `os.O_NOFOLLOW` to the flag list in `kitty/file_transmission.py`, so the kernel will refuse to open the file if it resolves to a symlink, eliminating the race window entirely. A secondary defense-in-depth fix in commit `8996aa798c774c...` [patch_id=5749248] introduces `CreateExclusiveAt()` (which uses `O_EXCL`) for the drag-and-drop kitten, preventing an attacker from racing to pre-create a directory entry with the same name. Together these two patches ensure that even if the stat-check is bypassed, the subsequent open will either reject a symlink or fail when a file already exists.

Preconditions

  • inputThe attacker must have the ability to execute arbitrary code as a child process inside a kitty terminal session.
  • inputThe attacker must be able to send file-transmission OSC escape sequences at specific times to create the TOCTOU window.
  • authThe user must confirm the file transfer dialog (prompt bypass — the dialog only shows the benign file name).

Generated on Jun 12, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

1

News mentions

0

No linked articles in our index yet.