Jan 28, 2026·6 min read·56 visits
Symfony forgot to quote the '=' character on Windows. If you run PHP scripts in Git Bash and use Symfony Process to handle paths with '=', MSYS2 mangles the path. In the worst case, a command meant to delete a sub-folder deletes your entire drive instead.
A critical argument injection vulnerability in the Symfony Process component allows for destructive file operations when running on Windows under MSYS2/Git Bash environments. Due to improper escaping of the equals sign (=), the MSYS2 'Magic Path Conversion' layer misinterprets file paths, potentially truncating arguments and causing commands like 'rmdir' to execute on parent directories or drive roots.
If you've spent any time in the trenches of Windows development, you know the uncomfortable truth: argv[] is a lie. Unlike Unix systems, where the kernel gratefully accepts an array of strings, Windows CreateProcess takes a single, massive string. It is then up to the child process (or the runtime invoking it) to parse that string back into arguments. It’s a game of telephone played by drunk operating systems.
Enter MSYS2 (the engine behind Git Bash), which tries to be the helpful translator between the POSIX world developers love and the Windows reality they inhabit. MSYS2 employs something called "Magic Path Conversion." It attempts to spot POSIX-style paths (like /c/users) and convert them to Windows paths (C:\Users) on the fly. Usually, this is great. But like an overly eager autocorrect, it sometimes makes catastrophic assumptions.
CVE-2026-24739 is what happens when that game of telephone goes horribly wrong. Symfony, the bedrock of modern PHP, failed to realize that the equals sign (=) is a trigger for MSYS2's magic logic. The result? A vulnerability that turns a mundane cleanup script into a digital woodchipper for your hard drive.
The vulnerability hides in Symfony\Component\Process\Process.php. This component is responsible for taking your nice, clean array of arguments and stitching them into that single command line string Windows demands. To do this safely, it needs to escape or quote characters that the shell might misinterpret. Symfony had a blacklist of dangerous characters: spaces, pipes, carets, redirects, and quotes. If an argument contained any of these, Symfony would wrap it in double quotes.
Here is where the logic failed: the developers missed the = character. Why does this matter? On standard cmd.exe, = is harmless. But in the MSYS2 ecosystem, = is a delimiter. It’s used for things like prefix=/usr/bin. When MSYS2 sees an unquoted argument with an equals sign, it thinks, "Aha! A configuration flag! Let me translate the path after the equals sign."
Because Symfony didn't quote arguments containing =, it passed them raw to the shell. If you had a path like E:/=projects/my_app, MSYS2 didn't see a file path; it saw a key-value pair. Its heuristic path translation kicked in, panicked, and in certain scenarios, mangled the path entirely. This isn't just a "bug"; it's an architectural collision between PHP's escaping logic and MSYS2's translation layer.
Let's look at the diff. It’s painfully simple, which is usually the hallmark of the most dangerous bugs. The escapeArgument method relied on a regex to decide when to quote a string.
The Vulnerable Code:
// src/Symfony/Component/Process/Process.php
if (!preg_match('/[()%!^"<>&|\s]/', $argument)) {
return $argument;
}If your argument was C:\Temp\=DeleteMe, the regex looks at it, sees no spaces, no pipes, no quotes, and says "Safe!" It returns the raw string.
The Fix (Commit ec154f6f):
// src/Symfony/Component/Process/Process.php
// Look at the regex character class expansion
if (!preg_match('/[()%!^"<>&|\s[\]=;*?\'\$]/', $argument)) {
return $argument;
}The fix doesn't just add =. The maintainers realized their blacklist was woefully incomplete for the chaotic world of Windows shells. They added square brackets [], semicolons ;, wildcards *?, single quotes ', and the dollar sign $.
By adding =, Symfony now wraps C:\Temp\=DeleteMe into "C:\Temp\=DeleteMe". When MSYS2 sees the double quotes, it backs off. It treats the string as a literal, disabling the magic path conversion that causes the corruption.
Here is the nightmare scenario. You are a developer using a CLI tool built on Symfony (like Composer or a deployment script). You are working on a Windows machine using Git Bash because you prefer the syntax.
The tool needs to clean up a directory. It runs something like this:
$process = new Process(['rmdir', '/S', '/Q', 'E:/repos/=temporary_build']);
$process->run();In a sane world, this deletes the =temporary_build folder. But here is what actually happens in the pipeline:
=. The command string becomes: rmdir /S /Q E:/repos/=temporary_build.E:/repos/=temporary_build unquoted.= or attempt to resolve the path relative to the MSYS2 root. In documented cases, this conversion can truncate the path to the nearest recognized mount point or parent.rmdir /S /Q E:/.rmdir is obedient. It doesn't ask questions. It starts recursively deleting everything on your E: drive. This isn't theoretical; this is a known side effect of argument parsing misalignment on Windows. The "Attack Complexity" is listed as High only because it requires specific path naming, but "High Complexity" is little comfort when your backup drive is being wiped.
The impact here is Integrity and Availability at their peak. We aren't talking about stealing a session cookie; we are talking about data destruction.
Consider CI/CD pipelines. Many runners execute on Windows but use Bash shells for script compatibility. If an attacker can control a directory name (e.g., via a Pull Request creating a file in a specifically named path), they could trigger a cleanup script that wipes the runner's workspace or even system directories if permissions allow.
The specific vector—MSYS2 path conversion—is insidious because it happens outside the application's logic. You can validate inputs all day in PHP, but once that string leaves the PHP process and enters the shell layer, the rules change. This vulnerability serves as a grim reminder: relying on blacklists for escaping is a game of whack-a-mole you will eventually lose.
The immediate remediation is to update Symfony. The patches (5.4.51, 6.4.33, 7.3.11, etc.) force quoting for a much broader range of characters.
However, the deeper lesson here is about environment hygiene. If you are running critical, destructive operations on Windows, stop using Git Bash wrappers for them. Use cmd.exe or PowerShell directly where possible, as they don't perform this "magic" translation layer that tries to outsmart the user.
If you absolutely must use MSYS2/Git Bash, you can set the environment variable MSYS2_ARG_CONV_EXCL to exclude specific arguments from conversion, though this is a fragile band-aid compared to patching the framework itself. If you are a library maintainer, stop blacklisting characters. Whitelist them. If it's not alphanumeric, quote it. Paranoia is a virtue.
CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Symfony Symfony | < 5.4.51 | 5.4.51 |
Symfony Symfony | >= 6.4.0, < 6.4.33 | 6.4.33 |
Symfony Symfony | >= 7.3.0, < 7.3.11 | 7.3.11 |
Symfony Symfony | >= 7.4.0, < 7.4.5 | 7.4.5 |
Symfony Symfony | >= 8.0.0, < 8.0.5 | 8.0.5 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-88 |
| CVSS v3.1 | 6.3 (Medium) |
| Attack Vector | Local (Context Dependent) |
| Impact | High Integrity / High Availability |
| Platform | Windows (MSYS2/Git Bash) |
| Bug Class | Argument Injection / Improper Escaping |
Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')
netfoil, an allowlist-based DNS proxy, failed to sanitize ALPN fields parsed from untrusted DNS-over-HTTPS (DoH) HTTPS Resource Records. This allowed attackers to inject ANSI escape sequences into log files or trigger Denial of Service (DoS) via uncontrolled memory allocations.
An issue was discovered in the tokio-postgres library for Rust prior to version 0.7.18. A trust assumption mismatch between the PostgreSQL protocol messages sent by a server and how they are parsed and indexed by the client-side library allows a rogue or compromised database server to trigger a Denial of Service (DoS) crash via an unhandled out-of-bounds slice indexing panic.
CVE-2026-14669 is a critical heap-based buffer overflow vulnerability in PostgreSQL's date/time formatting function to_char(timestamptz). The flaw arises from unsafe copying of user-controlled timezone abbreviations into a fixed-size internal buffer. An authenticated database user can trigger this issue by setting a long POSIX timezone abbreviation containing custom formatting, allowing them to overwrite adjacent heap structures and hijack execution control to achieve remote code execution (RCE) with the privileges of the 'postgres' operating system user.
An unauthenticated remote denial of service vulnerability exists in the Unleash feature management platform. By submitting a crafted JSON payload containing deeply nested structures to an OpenAPI-validated endpoint, an attacker can trigger uncontrolled recursion within the error formatting module. This leads to a call-stack exhaustion (RangeError: Maximum call stack size exceeded) inside the Node.js runtime, causing the service to crash immediately without recovery.
CVE-2026-63004 is a server-side request forgery (SSRF) vulnerability in the Unleash feature management platform. Authenticated administrators with CREATE_ADDON or UPDATE_ADDON privileges can exploit this vulnerability to initiate requests to loopback addresses, private networks, and cloud metadata endpoints, potentially leading to information disclosure and credential extraction.
Prior to version 8.0.3, Unleash's Markdown event formatter directly mutated the global template-escaping function of the shared mustache Node.js module, resulting in a process-wide security degradation where HTML/Markdown escaping was permanently disabled for the application lifetime.