Skip to content

Predictable temp-file names enable a symlink/race attack on file rewrites #3

Description

@dolph

Summary

File.Write() writes new contents to a temp file in the same directory as the target, then atomically renames it over the target. The temp file's name comes from RandomString(20) in strings.go, which uses the global, non-cryptographic math/rand PRNG seeded once with time.Now().UnixNano().

// strings.go
b[i] = characters[rand.Intn(len(characters))]
// file_handling.go
tempName := filepath.Join(f.Dir(), RandomString(20))
if err := os.WriteFile(tempName, []byte(content), f.Mode()); err != nil { ... }

Impact (Security: High)

Anyone who can write to the same directory as a file find-replace is rewriting can predict (or just observe) the temp-file name and pre-create it as a symlink to a path the running user can write but the attacker cannot. os.WriteFile opens with O_WRONLY|O_CREATE|O_TRUNCO_CREATE does not require the file to be new, so the open follows the attacker's symlink and writes the new content (or truncates it) at the attacker-chosen location.

This is the classic insecure-tempfile-in-shared-directory pattern. Predictable RNG only makes it easier; the underlying bug is using O_CREATE instead of O_CREATE|O_EXCL together with secure naming.

The risk is highest when find-replace is run by a privileged user (e.g., during deployment scripts, in a CI runner that shares /tmp-like state, or when the tool processes a directory writable by another user).

Suggested Fix

  1. Create the temp file with os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, mode) (or use os.CreateTemp, which does this internally with a CSPRNG name).
  2. Replace math/rand with crypto/rand for any name that ends up in a path namespace.
  3. Always remove the temp file on error paths.

Files

  • file_handling.go:78-88 (Write)
  • strings.go:5-18 (RandomString)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions