Ha! Rust Is The Answer To All Our C Programming Security Issues?
Answer: Well, No, Not Quite!!!
Programmers are being urged to update their Rust versions after the security experts working on the language addressed a critical vulnerability that could lead to malicious command injections on Windows machines. The vulnerability, which carries a perfect 10-out-of-10 CVSS severity score, is tracked as CVE-2024-24576. It …
I'm not a huge fan of Rust but come on, they're pretty clear about what security issues the language is designed to guard against and what it doesn't. They've never claimed to be a silver bullet for all security issues ever.
My first thought until I read the whole article, which explains that a Rust programming security group addressed a problem with the way a Rust standard library calls CMD.exe with escaped arguments. And it turns out that many other libraries in many other languages have the same problem - with a notable exception of the C language library which had addressed it already. The common factor amongst all is CMD.exe - so it seems like fixing CMD.exe - with security updates to older window versions - to provide a better interface would be a labor saving move going forward.
I once wrote a windows 'command.com' shell I called 'WinCMD' that did a lot of what CMD can do, specifically batch files and asynchronous windows applications from the command line. CMD in Win '95 of course made my application obsolete.
Maybe someone might write a console application for Win 7 and later that can do similar things, NOT be PowerShell, and contain improved command line security features...
The fix is not using RUST to run second-language scripts. Using multi-layered scripting like that always presents horrible attack surfaces.
Programmers do it because "it's the unix way" and they are used to it, but honestly, that's a terrible excuse.
It’s fine to call something, but passing undefined args is the issue. Use a config file, use explicit data types and allowed characters, then bug fix if a use case isn’t possible. Laziness is the issue, just going to “allow whatever”, no, allow the one command that needs to be run. Variations, specifically which variations? Iterate
Fixing CMD.EXE (or COMMAND.COM)? According to Raymond Chen it was considered, but by the time you have added enough warts to let existing .BAT files run you find that you haven't fixed anything.
That's why PowerShell exists (and was designed from scratch, with no "compatibility" for .BAT files).
Probably worth pointing out that there are people who object to linux shell scripts that only run on bash. The choices facing anyone who wants to fix a design error without breaking ecisting users have always been the same -- you can't really do it so it's just a question of how many people you want to piss off now (by making a new thing) versus how many in the future (by not making one).
Rust only counters memory access issues.
Unfortunately, for OS-level items, drivers and anywhere else that you need to use such access... it's not suitable without using "unsafe" and when you do, it's just as vulnerable as everything else.
Rust tries to solve a problem that would just be far better off written in a small dialect of any affected language that doesn't allow memory access or manipulation in certain sections. You could apply that to C retroactively with just a small tweak of a compiler.
Instead they invented another language, then threw in a keyword to take you back entirely to the same problem, and nobody properly vets that keyword entirely out of their code (because even an unsafe elsewhere can affect Rust-given guarantees in another place in your program!).
Rust without unsafe, I would be able to support.
C with an unsafe "pragma" or whatever to error out if memory manipulation is not being done in an unsafe block, I could support.
Instead we have yet-another-C with a slightly different default that's overridden for performance, or whenever you actually need to do that thing anyway.
It's 2024, who the hell is still using Batch files?
On the occasions when I am forced to use Windows, the first thing I do is install a proper shell, be it Cygwin or MinGW, WSL or a VM
It's time that CMD.exe and Command.COM are retired for good, along with the rest of MS-DOS that still persists in Windows, shirley
It's 2024, who the hell is still using Batch files
Everybody. They might not do so themselves but loads of programs will run a batch file in the background because it's easy and it works.
Do you sometimes see command windows popping up and disappearing just as quickly? Well that was probably a batch file.
"It's 2024, who the hell is still using Batch files?"
[meekly raises hand]
Using it for quite a few things; for example, on this workstation, using it after a monthly, automated full clone of the hard drive to a standby drive to then disconnect the drive after the successful clone. The clone software has no other ability to handle this, so I pressed the system event log and a Powershell batch file into use to get 'r done.
I see files emailed to me with names like "Urgent_Purchase_Order.bat" frequently, maybe this Rust update will result in some new file names?
That would be helpful but I've got the mail-server blocking a lot of suspicious attachments so I'm not specifically concerned, but anything like this problem is always added to the mail-server block list.
"On the occasions when I am forced to use Windows, the first thing I do is install a proper shell, be it Cygwin or MinGW, WSL or a VM"
Back in the day I used them for lots of things. One of my more (in)famous batch files at $workplace was called "Megamake" (it built everything and produced a distribution as a zip file). Windows nmake is pathetic so I used a batch file to coordinate the build. Worked well enough on a Win '98 or NT 4 system. Obvious Spaceballs reference, heh.
It's not just BAT files, it's all those job management systems that store the BAT code and then call up the command shell to run it on the fly, some of that stuff can be 15 years or more old. I was recently working on a system and found binaries still in active use that were compiled in 1997 and still in use, that's just the tip of a very old and very huge iceberg of technical debt and legacy baggage.
I prefer PowerShell myself on Windows, it's way more flexible and easier to get stuff done but I still have to dig out my old DOS batch notes sometimes when the need arises, especially on some of the more obscure things like dynamic variables in DOS batch FOR loops, yes it can do stuff like that despite how simple DOS batch seems.
+1 for dynamic variables in FOR loops! Also delayed expansion can be useful. The syntax is awful, but in most corporates you are allowed batch files but not powershell so what are you going to do… try and get approval for a random python runtime or just bash out a batch of batch files you’re translating from bash..
Everyone. We tried moving to powershell but that got locked down because it’s too powerful, and they can’t lock batch files down because everybody uses them… so when you need to trigger something on a schedule, or run a preconfigured but composable process… yep… batch file baby
It’s self sustaining at this point
Well, plenty of those around it would seem! Perhaps it’s the default setting for some flavors of Windows Server, I don’t know, I don’t care I just want to call this ten line script every day, which is going to call some “enterprise” software ( you know, other batch files wrapping Java… or an exe an intern wrote 30 years ago that only has stdin and no config file )
It's a misquote from South Park, originally referencing Windows 98.
The point is that even if Rust is hugely safer than C/C++ it's not foolproof. Anyone can write bad and insecure code in any language and my attempt at a joke here was really only aimed at the minority who go too far with evangelising its merits and make claims that may imply that it's some kind of magic bullet.
Yeah, I'm well aware that most people think my 'dad jokes' are rubbish! I agree.
You forgot to add:
Addressing massive security flaws exposed by Java's implementation "is left as an exercise for the reader" much like basic GUI widgets and UI elements, and about a million other useful things.
Truth be told, their internal team doesn't even have time to fix the memory bugs in the JVM, so maybe they are just being cynically realistic about their workhour/open bugs ratio problem and lot just lazy or incompetent.
And also to be fair, the root issue is that no one ever though through the encapsulation problems that the CMD shell and it's text processing created, and M$ will never touch anything that deep in the OS. It's a classic study in technical debt and some mistakes being almost impossible to fix past a certain point.
I'm confused (again?). This headline, advistory, etc, reads as a Rust problem, but "Erlang, Go, Python, and Ruby are also affected and have updated their respective documentation pages to raise awareness of the issue.". Do those language implementations on Windows use Rust or just made the same mistake, or ?
Right. It's more accurate to say that C implementations on Windows pick an approach, and then at least it's consistent for programs using that implementation; and because (as you say) C has been around on Windows for a long time, those implementations are more or less the de facto standard for argument splitting for programs running under cmd.exe, and they've been fixed for at least some of the corner cases. So other implementations, in whatever language, need to conform to what the major C implementations do for consistency and avoiding surprise, and they need to try to address those corner cases.
To answer OP's question: Calling out Rust in the headline was clickbait, pure and simple. It's just one among many. But since it's both popular and controversial right now, it'll attract more readers than, say, "Erlang fixes critical command injection bug on Windows". (JFTR, many security claims have been made about Erlang, too, and with some justification.)
It sounds like the editors decided that putting Rust in the headline would garner the most attention. The article indicates that the real issue stems from the interaction between code written in the mentioned languages and the cmd.exe shell's approach to passing arguments between programs. At a guess, misinformation about how to code shell escape prevention for cmd.exe made it into some common source of truth, and the developers for all those languages used the same source, whether directly or indirectly.
This post has been deleted by its author
It is also worth pointing out that the 10/10 rating in the headline is a worst case scenario that is quite unlikely to happen and as RyotaK points out in his report linked in the article shouldn't directly be applied to rust applications.
To be affected an application would have to:
- call external commands
-by way of cmd.exe (either directly or indirectly by trying to execute a .bat or .cmd file [either on purpose or being tricked into it when the programmer doesn't supply the extension of the command])
-using unsanitized user supplied input as the argument for that command call
Another mitigating factor is that most of the application that do all of the above will have the input supplied by the local logged in user that usually has the rights to execute any command in cmd.exe at will and thus doesn't need to exploit a rust program.
It should be exceedingly rare to find a rust program that can be tricked into calling a batch file with an argument supplied by some unauthorized remote attacker.
-using unsanitized any user supplied input as the argument for that command call.
The problem is with the sanitisation (escaping) that Rust (and a few others) provides.
It didn't work, so anything relying on it is in trouble.
Different platforms spawn processes in different ways. Not better or worse, different. It sounds like someone relied on a summary - or perhaps simply copied something from Stack Overflow - instead of looking up the full documentation.
This is really another demonstration of why reimplementing from scratch is risky.
I said an application is only at risk if it uses unsanitized user supplied input as the (up to now insufficient) sanitation provided by Rust won't matter if the application does its own sanitisation.
Currently i can't think of a valid use case where i'd call a batch file with some user supplied argument but if i did i'd probably attempt to check if the input matches the format i expect and display an error message if not before calling the batch file.
Rust is probably in the headline because it was probably found in Rust first, and then the person discovering it tried other languages (such as Go, Erlang and Python) to see if they also were vulnerable to it. The CVE certainly implies that (it's registered against Rust).
I don't see Perl mentioned (which *also* runs on Windows), but it is safe to assume that Perl would have similar issues.
And yes, CMD and the way the batch interpreter within it do parameter parsing is... interesting. This is what happens when you take POSIX-esque things and graft them onto things that come from DOS, which in turn borrowed things from other OSes, including other POSIX-compliant and -non-compliant ones.
Perl's documentation flagged it as a problem at least four years ago (earliest record in archive.org).
The Register is a red top, it picked the headline most likely to poke the commentards....
The issue is the Win32 API CreateProcess function. This does 2 things:
1. it takes a single string for the programs arguments (rather than an array). The program is called with this single string as it's arguments.
2. It will spawn CMD if you ask it to run a .bat file (either explicitly or because of extension matching with %PATHEXT%).
Because programs need to parse a single argument string the arguments need to be escaped. eg, if you have 2 arguments "Hello world" and "Goodbye", your final string might end up being "Hello\ World Goodbye"
The rules CMD uses to parse arguments are different from those used by the C library when you're calling an EXE. Most languages take an array of arguments (so need to do the escaping on Windows) and only account for the EXE case, so if you pass user supplied input as an argument and can persuade the program to run a .bat file (therefore spawning CMD) you can 'do nasty things'.
Windows (and msdos) delegated command line processing to each executable compared with Unix which delegated most of that processing to the command interpreter (shell.) Having all the wild card expansion, environment variable expansion, quoting etc performed by the shell imposes the conventions of the shell. Each Windows application can mangle its command line anyway it likes - A.EXE * might mean *.* or not, or just \*
Brings back memories of a very long time ago.
I recall MKS Unix utilities (programs) eg vi.exe for msdos when invoked from command.com eg vi *.c the program vi.exe would pass the command line viz *.c to a spawned subprocess "glob.exe" of vi.exe with an address of a chunk of memory belonging to vi.exe where the sh style expansion of *.c was returned and passed back to vi.exe's main(). [Joys of real mode memory non-protection.]
I wrote my own version for my own C apps for consistent argument expansion when those apps were run from command.com. (This could also be implemented as a TSR since glob.exe was really just a primitive shared library.)
The only virtue of msdos and windows is that it make one rather devious.*
The execv(2) syscall has the advantage of simplicity - what goes (argv[]) into execve (path, argv, envp) emerges "as is" in main (argc, argv, envp) - nil buggery. Posix_spawn(3) treats command line arguments the same way being effectively a fork(2) followed by an execve(2) with the added complications of not separating the two calls.
*Apropos of nothing other than one of favourite Terry Pratchett's Vetinari quotes:
"The female mind is certainly a devious one, my lord."
Vetinari looked at his secretary in surprise. "Well, of course it is. It has to deal with the male one."
It's worse. CreateProcess actually lets you feed two strings, one for the launched app and one for the arguments, but for various reasons I (and possibly others) have long considered that one should leave the first string empty and put the executable as the first "argument".
This is far from obvious, but certainly plays more nicely if the program being launched expects argv[0] to be the program name, which many command-line programs do.
Remember, on a POSIX system, when you spawn a program in a new process, you don’t pass it a command line, you pass it an array of command words. There is no need to go through a shell, and therefore no need to handle escaping of shell-special characters: whatever strings you pass in the argument array just go straight through to the spawned program, without any special interpretation.
However, on Windows, you must pass a single “command line” string, and it seems you always have to go through a shell. That is where stupid bugs like this can arise.
However, on Windows, you must pass a single “command line” string, and it seems you always have to go through a shell.
You don't always go through a shell, only if running a batch file. However, Windows may infer the extension if you don't provide one so running a batch file may not be what is intended.
The rules for escaping are different between the c runtime and CMD.exe, so if you expect to be running an exe you may end up passing an incorrectly escaped string to CMD.
It's not really a bug, (although it's a poor design choice) it's just an example of subtle differences between complex systems.
And to think I complained about command line parsing in UNIX:
How Command Line Parameters Are Parsed
There is a CommandLineToArgvW() function but it seems even different bits in Windows roll their own version depending on the file type.