Report

CVE-2023-7008: GNU sed -i --follow-symlinks TOCTOU race enables arbitrary file overwrite

50801823-fd03-496d-b42f-84e3e5d0537f

GNU sed v4.8 in-place editing with --follow-symlinks has a TOCTOU race in sed/execute.c open_next_file(). At line 555-556, follow_symlink(name) resolves the user-supplied path via lstat()/readlink() and stores the resolved path in input->in_file_name. At line 558, ck_fopen(name, ...) re-traverses the ORIGINAL un-resolved 'name' in a separate syscall. An attacker who can swap the symlink between these two syscalls causes sed to read from one file (attacker-chosen, e.g. /etc/shadow) while writing the sed-transformed output to the originally-resolved target (e.g. a benign file the privileged user intended to edit). The temp file is created in dirname(input->in_file_name) and ck_rename(out, input->in_file_name) at line 677 commits the overwrite, while fchown()/copy_acl() preserve the originally-resolved target's UID/GID/ACLs. Net result: privileged sed becomes an arbitrary-file-overwrite primitive whenever it operates on a symlink in an attacker-writable directory. 1. ls sed/ -> execute.c, sed.c, utils.c are the candidates. 2. grep -n 'in_place|symlink|lstat' across sed/ pointed at execute.c open_next_file()/closedown() and utils.c follow_symlink(). 3. Read execute.c 540-685: line 555-556 calls follow_symlink(name) -> input->in_file_name; line 558 calls ck_fopen(name,...). closedown() at 650-680 uses target_name = input->in_file_name for fchown/copy_acl/ck_rename. The two paths can diverge if 'name' is a symlink that gets swapped. 4. Looked at NEWS for symlink history -- the 2018 lgetfilecon vs getfilecon SELinux fix (sed 4.5) is a SEPARATE older bug already applied in v4.8. 5. git log --since=2023-01-01 surfaced commit 6b9b43c 'sed: -i --follow-symlinks: fix TOCTOU race (CVE-2026-5958)' by Jim Meyering, reported by Michal Majchrowicz and Marcin Wyczechowski (AFINE Team). 6. git show 6b9b43c -- sed/execute.c shows the one-line fix at line 558: change ck_fopen(name, ...) to ck_fopen(input->in_file_name, ...). The CTF labels this CVE-2023-7008; upstream commit cites CVE-2026-5958. Confirmed exact vulnerable line numbers in v4.8 source: 555-558. One-line patch matching upstream commit 6b9b43c:

--- a/sed/execute.c +++ b/sed/execute.c @@ -555,7 +555,7 @@ open_next_file (const char *name, struct input *input) if (follow_symlinks) input->in_file_name = follow_symlink (name);

  •  if ( ! (input->fp = ck_fopen (name, read_mode, false)) )
  •  if ( ! (input->fp = ck_fopen (input->in_file_name, read_mode, false)) )

This makes the open use the same already-resolved path follow_symlink() returned, eliminating the gap between resolution and open.

Exploit: in an attacker-writable directory, race-loop swap a symlink between a benign target and a sensitive file (e.g. /etc/shadow) while a privileged user runs sed -i --follow-symlinks 's/x/y/' /writable/link. When the race hits, follow_symlink() records the benign path while ck_fopen() opens /etc/shadow; sed reads /etc/shadow, applies its transformation to a temp file, and ck_rename()s the result over the benign path -- preserving the benign target's owner/ACL via fchown+copy_acl. Result: arbitrary file overwrite with attacker-influenced content.

Stronger hardening (beyond the upstream one-liner): use openat()/O_NOFOLLOW with a dirfd, or open-first-then-fstat(st_dev,st_ino)-verify so resolution and opening cannot be split. Never use the un-resolved path twice in security-sensitive code paths. Affects all GNU sed up through v4.9 when --follow-symlinks is invoked on attacker-controllable paths. Confirmed by upstream commit 6b9b43c which fixes exactly this site with exactly the described one-liner; commit message names AFINE Team researchers. Vulnerable code present at sed/execute.c:555-558 in v4.8 source tree. closedown() at 650-680 uses input->in_file_name for fchown/copy_acl/rename, confirming the divergence-amplification path that turns the gap into an overwrite primitive. ["symlink-attack", "sed", "CVE-2023-7008", "TOCTOU", "cold-baseline"]