Fedora Account System
Red Hat Associate
Red Hat Customer
AI_ONLY_REPORT package: glib2-2.88.0-1.1.hum1 ------ Summary: TOCTOU Symlink Race in `G_FILE_CREATE_REPLACE_DESTINATION` Fallback Path: When temporary-file replacement fails, the fallback path unlinks and reopens the destination without exclusive creation, allowing a local attacker to race in a symlink and redirect writes to an unintended target file under the victim process's permissions. Requirements to exploit: A local attacker must be able to create entries in the destination directory, induce or coincide with failure of the temporary-file replacement path so `fallback_strategy` is taken, and win the race between `g_unlink()` and `g_open()`. Security impact depends on the victim process being able to write a target file the attacker could not otherwise modify. Component affected: `glib2-2.88.0-1.1.hum1` in `gio/glocalfileoutputstream.c`, `handle_overwrite_open()`, when `g_file_replace()` is used with `G_FILE_CREATE_REPLACE_DESTINATION` Version affected: `glib2-2.88.0-1.1.hum1` Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: Not notified. CVSS: CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:L - 5.3 (MEDIUM) AV:L - exploitation requires local access to the system and filesystem namespace where the target path is replaced. AC:H - the attacker must trigger a failure-only fallback path and win a narrow unlink/recreate race. PR:L - the attacker needs the ability to create files or symlinks in the destination directory. UI:N - no separate user interaction is required once the vulnerable code path is exercised. S:U - the impact stays within the vulnerable component's security scope. C:N - the issue does not directly expose file contents. I:H - a successful race can redirect writes into an unintended file writable by the victim process. A:L - redirected writes can corrupt files or cause limited service disruption. Impact: Moderate. Under Red Hat's severity guidance, this issue can compromise integrity, and to a lesser extent availability, of resources under certain circumstances, but it is materially harder to exploit than an Important flaw because it is local-only, depends on an uncommon fallback path, and requires both attacker control of the destination directory and successful race timing. Embargo: no Reason: The issue is local, configuration-dependent, and moderately severe, with straightforward operational mitigations. It does not fit the usual profile for an embargoed remote compromise issue. Acknowledgement: Aisle Research Vulnerability Details: The normal replacement flow attempts to create a temporary file and then replace the destination. The security issue arises only when that temporary-file step fails and execution falls back to unlinking and reopening the original pathname. A focused paraphrase of the vulnerable flow is: ```c tmpfd = g_mkstemp_full(tmp_filename, ...); if (tmpfd == -1) goto fallback_strategy; fallback_strategy: if (replace_destination_set) { unlink(filename); fd = open(filename, O_CREAT | O_WRONLY); } ``` If an attacker recreates `filename` as a symlink after the unlink and before the reopen, the subsequent open follows that symlink and later writes land in the symlink target. That behavior contradicts the documented `G_FILE_CREATE_REPLACE_DESTINATION` contract to replace instead of following links, and it is also inconsistent with the existing `test_replace_symlink` expectation that the symlink target should not be touched. The normal path is not the issue; the exposure is specific to the failure-only fallback. Steps to reproduce: 1. Build a small `LD_PRELOAD` shim that makes `open()` or `openat()` fail with `ENOSPC` only for paths containing `/.goutputstream-`, while leaving other opens unchanged. This deterministically forces `g_mkstemp_full()` to fail and drives execution into the fallback path. 2. Create a `victim_path` inside a directory writable by both the victim process and the attacker. Create a separate existing regular file with recognizable contents to use as `protected_target`. 3. Run an attacker loop that waits for `victim_path` to disappear and immediately recreates it as a symlink pointing to `protected_target`. 4. Run a victim program that calls `g_file_replace(victim_file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, ...)`, writes controlled bytes, and closes the stream. 5. Observe that the controlled bytes are written into `protected_target`, demonstrating that the fallback reopen followed the attacker-created symlink. Mitigation: Avoid using `g_file_replace(..., G_FILE_CREATE_REPLACE_DESTINATION, ...)` on paths inside directories writable by less-privileged users until a fix is available. Do not perform privileged replacement writes in shared writable directories where another user can recreate the destination pathname. Preventing temporary-file creation failures reduces reachability, but it should be treated as defense in depth rather than a complete fix. Proposed Fix: Add `O_EXCL` when recreating the destination in the fallback path and fail cleanly if another filesystem object appears at that pathname before reopen. ```diff diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c @@ -1215,10 +1215,16 @@ handle_overwrite_open (...) if (readable) open_flags = O_RDWR | O_CREAT | O_BINARY | O_CLOEXEC; + open_flags = O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC; else open_flags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC; + open_flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC; fd = g_open (filename, open_flags, mode); if (fd == -1) { + if (errno == EEXIST) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_EXISTS, + _("Target file was recreated during replace")); + goto error; + } ... } ``` ------ This report was generated using AI technology. Always review AI-generated content prior to use