A while ago there was a security advisory published for Rust that the standard library's Command API was not sufficiently handling arguments passed to cmd.exe and .bat files such that malicious arguments could invoke arbitrary shell code. This was fixed in Rust 1.77.2.
Reading through the advisory as well as the documentation on Command::arg and the Windows argument splitting section of std::process, I can see the core issue originates from Windows passing arguments via a flat string and the parsing of that string can differ based on the executable, but I have not seen an explanation of what inputs are a concern for cmd.exe nor how Command::arg mitigates them.
For example, if I have a batch file and I provide a potentially malicious argument:
Command::new("my_script.bat")
.arg(&untrusted_user_input)
.spawn();
- Is this safe with the 1.77.2 mitigations or is it still vulnerable?
- What kind of escape logic is applied to the argument?
- What problems can occur if I were to use
.raw_arginstead? - What input could be "inescapable" and thus return an
InvalidInputerror?
I've found it hard to find good resources on cmd.exe parsing. There's How does the Windows Command Interpreter (CMD.EXE) parse scripts? which may have the answers, but its incredibly dense and not clear which parts are relevant (whole script vs just arguments). There's also Does the Windows "cmd.exe" parse arguments differently? which shows that /C can behave in unintuitive ways, but its not clear if/how that related to calling a batch script.
The Rust documentation telling me to "use caution with untrusted inputs" and "validate untrusted input so that only a safe subset is allowed" does not provide me with any actionable guidance if I do need to handle untrusted inputs.
make_bat_command_lineinstd::sys::args::windows(called from here as part ofspawn), and the mitigation PR that changed it is #123681.