Skip to content

TOCTOU race condition in grabbag__file_copy_metadata() #893

@luongtrieudai2005

Description

@luongtrieudai2005

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions