xiph / xiph/flac

TOCTOU race condition in grabbag__file_copy_metadata()

Open
#893 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C
Stars
2.4k
Forks
363
PR merge metrics
No merged PRs in 30d

Description

poc_toctou_flac.sh

TOCTOU race condition in grabbag__file_copy_metadata()

Environment

  • flac version: 1.3.3
  • OS: Ubuntu 22.04 (WSL)
  • Tool: strace 5.16

Summary

grabbag__file_copy_metadata() in src/share/grabbag/file.c:116 calls
flac_stat(filename) and flac_chmod(filename) on the same path string.
Between the two calls, a local attacker with write access to the output
directory can atomically replace the file with a symlink pointing to an
arbitrary target. The subsequent chmod() follows the symlink and changes
permissions on that target.

Vulnerable pattern

// src/share/grabbag/file.c
flac_stat(filename, &stats);           // CHECK: read mode by path
flac_chmod(filename, stats.st_mode);   // USE:   apply mode by path

Verification

strace confirms chmod() is called by path, not fchmod() by descriptor:

chmod("/tmp/.../dst.flac", 0100644) = 0   // by path — vulnerable
// fchmod() not observed

Race confirmed with a PoC script (written with AI assistance):

  • symlink swap succeeded on attempt 4 out of 50,000
  • victim file permissions changed from 000 to 644
  • tested on flac 1.3.3, Ubuntu/WSL

Attack scenario

Most relevant when flac is invoked by a service or automated pipeline
with elevated privileges, where the output directory is also writable by
a lower-privileged user (e.g. media processing server, NAS, CI/CD pipeline).

Suggested fix

Replace path-based calls with file-descriptor-based equivalents:

// BEFORE
flac_stat(filename, &stats);
flac_chmod(filename, stats.st_mode);

// AFTER
int fd = open(filename, O_RDONLY);
if (fd >= 0) {
    fstat(fd, &stats);
    fchmod(fd, stats.st_mode);
    close(fd);
}

Note

PoC script available on request.

Reported by: Luong Trieu Dai

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/share/grabbag/file.c:116 and trace grabbag__file_copy_metadata(), focusing on the flac_stat() and flac_chmod() calls. Review the supplied PoC and strace evidence, then verify the metadata copy no longer permits a path replacement between checking and changing permissions. Done when the TOCTOU permission-change scenario is prevented and the relevant behavior remains correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.