Rust Command Injection:
Examples and Prevention

stackhawk

StackHawk|August 27, 2021

This article discusses Rust command injection explained by way of examples/vulnerabilities and their fixes. Discover how to secure your apps before they deploy.

The Rust programming language has been praised for having so many security features built right into the code. Be that as it may, it's by no means a pass for Rust developers to relax when making new apps. Hackers can still take advantage of existing Rust-Lang code to carry out command injection attacks. 

This post explores the extent to which command injection can be perpetrated in the context of Rust apps. We'll show a few examples to make the threat real, followed by best practice patches thereof. 

Let's start with a brief overview of how a command injection exploit would unfold and why it's possible.

Rust Command Injection: Examples and Prevention image

How a Command Injection Attack Works

Let's start off by explaining what is actually happening when a command injection attack executes. Many often mistake the command injection with a remote code execution attack. The latter requires extra code passed toward the server on which an application is hosted. However, command injections target the operating system on which an app is hosted. Much like how a SQL injection attack targets the underlying database. 

The logic is simple; even if an app can be exploited by other means, taking over the entire hosting machine is so much better. This way, you gain control over the app and any others sharing the host machine. To achieve this, the attacker passes a few simple commands that execute on the operating system through the terminal. Ergo, command injection attacks are also known as shell attacks. 

Simple commands that prompt the operating system to respond to the attacker under the guise of the app's access level can provide so much information about the app and even the host machine itself. This will make more sense when you get to the example section of the post. Keep going!

The Rust Context of Command Injection

When taken in the context of Rust-Lang applications, command injections disregard any perceptions of robustness attributable to the language itself and focus on the underlying stack. Your code can still give the attacker the leeway to perpetrate an attack. 

There are certain aspects of your code that an attacker can take advantage of. The following section discusses these for web apps and native platform systems made with Rust-Lang. 

Rust Command Injection Examples

Let's say, for example, your Rust application queries information from the server through a request like this:

pub fn new_job(url: String) {
thread::spawn(move || {
let _child = job::Command::new("cmd.exe")
.arg("/C")
.arg("ping")
.arg(&url)
.arg("-t")
.spawn()
.expect("Couldn't run &arg");
});
}

Already this piece of Rust code poses a threat as it prompts the creation of two processes that directly allow user args to run on the OS. The cmd.exe executable initiates the command prompt. Then it passes the arguments provided in succession, ending in an open exploitable path for any hacker to run extra arguments of their own. All this, under the rights of an approved user (usually with admin privileges). 

Several commands can be sent to the OS as part and parcel of the .args as follows. 

For Linux-hosted apps, these are the arguments: 

  1. {whoami}: prints the name of the user on which commands will be executed (the same command for Windows)

  2. {uname -a}: prints details about the operating system in the same command-line tab

  3. {ifconfig}: prints out network configuration information

  4. {netstat -an}: reveals network connections discoverable on the server machine (works on Windows OS too)

  5. {ps -ef}: this is a roll call of all processes running

The same arguments have Windows versions. It makes so much sense to find out the OS type first. 

  1. {ver}: shows the attacker what OS you're running

  2. {ipconfig /all}: extracts network configuration details

  3. {tasklist}: self-explanatory

These easy-to-learn commands are just the tip of the iceberg when exploiting Rust applications. In fact, a hacker could go as far as downloading files (or entire apps) from your root directory. All it takes on their end is to learn enough about the host machine. From this attack, they could develop other crafty methods of getting paid for their effort. As is the case with most corporate ransom-motivated hacks.

Rust-Lang Web Application Vulnerabilities

You may have resorted to using Rust as the back-end language for your web applications, which is possible with the use of Rust frameworks. This simply means the browser can send queries through the navigation bar to your host machine. From a hacker's perspective, this is a possible entry point for commands that your OS will parse. Let's look at how this could happen. 

Consider this snippet of code from a Rust web application's main function.

Rust Command Injection: Examples and Prevention image

Rust-Lang command injection examples.

While it will execute and deliver the expected outcome, it leaves so much room for an attack through command injection. Once the code runs, the browser can be used to put breaks (like &, |, -, ....) in between requests to prompt an alternative command execution by the target OS. The code even lets the shell run if the cmd command fails. 

One such attack could look like this: 

Normal URL: www.domainname.com/resourcelocation/queryorcommand 

The URL above is clean and will produce predictable results. However, the one below, which has an injected command in between the ampersand signs, will execute the hacker's wishes on the machine. 

Injected URL: www.domainname.com/resourcelocation/& command &queryorcommand 

How to Patch Your Applications Against Command Injection

Now that we've gone over two instances to demonstrate the possibility and severity of command injections in Rust-Lang, let's look at the fixes. 

1. Use Kill Methods to End Open Command Prompt Sessions

In reference to the first code example, the _child process would loop on any attacker's whim. To seal this vulnerability, we can implement the kill method on each process while telling it what we expect.

Here's a syntactic implementation of the kill method.

We first need a child process implementation to later terminate:

impl _Process

And then the kill command itself:

pub fn kill(&mut self) -> Result<()>

This is just one of the few practices you must enforce across your dev team for better application security overall. Where users have such access to the system, you must also put safety nets in place to hold off hackers.

Rust Command Injection: Examples and Prevention image

2. Block Arbitrary Commands From Being Parsed

Now, let's look at the second example. Sanitizing that main() function by explicitly listing a range of values as a forbidden range will help. Simply declare a forbidden array comprising the pause and directive commands that your system reads as instructions. 

The implementation would look like this:

let blocked = ['|', '&', etc]

Etc. Here is a long list of such characters. 

This array is followed by an iteration that checks the input or final argument processed using a common Rust loop control flow.

Ready to Test Your App

Testing Rust-Lang Apps for Vulnerabilities

Knowing that your code is weak is a more actionable situation than figuring out when an attack is active. At least then you can implement one of the methods above to stop the event of an attack. The Rust language has a very rich resource of cargos where you'll find safety-proven implementations against many of the possible attacks. 

Some of the packages you'll find in cargo spaces will protect you from not only command injection, but XSS and SQL injection as well. The more threats a package covers, the better it is for you to pick.

Deliberately following coding best practices, such as developing from a security standpoint, will improve your odds against command injection attacks.

However, you're better off including a testing process alongside your continuous integration workflow to make sure any new code doesn't expose you to risks. StackHawk tools allow such intuitive detection of loopholes before they go into production. 

This post was written by Taurai Mutimutema. Taurai is a systems analyst with a knack for writing, which was probably sparked by the need to document technical processes during code and implementation sessions. He enjoys learning new technology and talks about tech even more than he writes.


StackHawk  |  August 27, 2021