Advisories list
[advisory]
id = "HSEC-2024-0003"
cwe = [150]
keywords = ["windows"]
aliases = ["CVE-2024-3566", "VU#123335"]
related = ["CVE-2024-1874", "CVE-2024-24576", "CVE-2024-22423"]

[[references]]
type = "ARTICLE"
url = "https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/"

[[references]]
type = "ADVISORY"
url = "https://kb.cert.org/vuls/id/123335"

[[references]]
type = "FIX"
url = "https://github.com/haskell/process/commit/3c419f9eeedac024c9dccce544e5a6fb587179a5"


[[affected]]
package = "process"
os = ["mingw32"]
cvss = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H"

[[affected.versions]]
introduced = "1.0.0.0"
fixed = "1.6.19.0"

process: command injection via argument list on Windows

The process library on Windows is vulnerable to a command injection vulnerability, via cmd.exe's interpretation of arguments. Programs that invoke batch files (.bat, .cmd) and pass arguments whose values are affected by program inputs may be affected.

This issue was discovered in many programming languages' Windows process execution behaviour. It was tracked by CERT/CC as VU#123335 and a coordinated disclosure was made on 2024-04-09 17:00 UTC.

A fix was released in process-1.6.19.0.

Background

Unlike POSIX systems, Windows does not have a mechanism for passing multiple arguments.Command line parsing is up to individual programs.

The process library defines the RawCommand constructor for specifying an executable and its arguments:

data CmdSpec
  = ShellCommand String
  | RawCommand FilePath [String]

On Windows, the RawCommand executable name and arguments are serialised into a single command line string, with separate arguments quoted separately. process then invokes the Windows CreateProcess routine with this command line string is given as the lpCommandLine argument.

Issue

When executing .bat or .cmd files, CreateProcess implicitly spawns cmd.exe. The System.Process command line construction does not escape characters with special meaning to cmd.exe. As a consequence, a command injection vulnerability arises when the following conditions are satisfied:

Demonstration

The following batch file, test.bat, merely prints the executable name the first two arguments (as interpreted by cmd.exe):

@ECHO OFF
ECHO 0: %0
ECHO 1: %1
ECHO 2: %2
PAUSE

The following Haskell program executes test.bat with basic string arguments. The output is as expected:

λ> readProcess "test.bat" ["a","b"] [] >>= putStrLn
0: "test.bat"
1: "a"
2: "b"

However, we can use a close quote and the & character to induce cmd.exe to execute a program named in the argument:

λ> readProcess "test.bat" ["\"&calc.exe"] [] >>= putStrLn
0: "test.bat"
1: "\"
2:

In addition to producing the above output, calc.exe is executed.

Mitigation

The lack of a general mechanism on Windows for safely conveying command line arguments to programs increases the risk of this kind of security issue. The fact that cmd.exe command line parsing is complex and poorly documented exacerbates this issue, and also heightens the risk that the fix is incomplete, or causes other issues.

If possible, avoid executing batch files where arguments include or are influenced by untrusted program inputs. If it must be done, reject arguments that include special characters including & and ".

Fix versions

process was modified to perform additional escaping and quoting when executing .bat and .cmd files on Windows (ignoring character case). The behaviour is unchanged in all other cases.

The fix was released in process-1.6.19.0. The following GHC releases were the first in their series to include a fixed version of the process library:

Such a change in semantics should normally result in a major version bump. Because we expect very few (if any) users will be impacted by the behavioural change, the GHC team made a pragmatic decision to avoid the disruption that a major version bump would cause.

Acknowledgements

Security researcher RyotaK discovered and responsibly disclosed this vulnerability, coordinating the response across the many affected langauges and ecosystems.

Ben Gamari commited and released the fix, which was based on a proposal by Fraser Tweedale. Fraser also improved the System.Process module documentation to better explain the Windows semantics.