When To Use — To Avoid Command Injection Vulnerabilities

The Dangers of Unescaped User Input in Shell Commands

Shell commands can be vulnerable to injection attacks if untrusted user input is inserted without proper escaping. Attackers can craft malicious input strings to inject extra commands that get executed by the shell. This can lead to unauthorized access, data loss, denial of service, and other threats depending on the context.

For example, consider a web application that passes user-supplied input directly to a shell command for execution:

# Vulnerable code  
hostname=$USER_INPUT
ping -c 3 $hostname

An attacker could supply input like ; rm -rf / to delete system files. The injected command gets executed because the input was not handled safely:

ping -c 3 ; rm -rf /

Shell command injection can also be leveraged to open reverse shells for remote code execution, exefiltrate data, or perform other malicious actions. Supplying unsafe user input to shell commands is extremely risky and a common vulnerability.

When User Input Needs Escaping

Any user-supplied input that gets expanded into a shell command requires escaping to prevent injection attacks. Common high-risk situations include:

  • Web application form fields used in shell commands
  • Command line arguments injected into scripts
  • Reading input from files or databases to construct commands
  • Allowing users to specify hostnames, filenames, or other arguments

Proper escaping causes the shell to treat the input as literal data instead of executable code. This prevents embedded commands or other potentially dangerous input from executing with privileges.

Escaping User Input with —

The — characters can be used in bash and other shells to escape user input before injecting into a command. Everything after — is treated as a single literal string. For example:

hostname=$USER_INPUT 
ping -c 3 -- $hostname

This causes $hostname to be passed as a single argument instead of interpreted for expansions or splitting. The — escapes the variable, preventing injection risks.

Alternatively, you can quote the user input using single quotes which also avoids expansions:

  
hostname='$USER_INPUT'
ping -c 3 $hostname

In general, escaping with — or quoting with single quotes are preferred ways to incorporate untrusted input into shell commands safely.

Safe Ways to Use User Input in Shell Scripts

Besides escaping, there are other secure ways to handle user input in shell scripts:

  • Use curly brace expansion ${parameter} instead of just $parameter to avoid split injections or other risks
  • Pass user input as function arguments rather than expanding inline
  • Validate or whitelist inputs instead of blacklisting bad values
  • Use principle of least privilege when executing commands based on input
  • Always sanitize when incorporating input into sensitive command options

Care should be taken any time external data gets inserted into a shell command string. Consider whether the input needs executing directly or if an isolation layer like arguments or a function interface can be used instead.

Real World Examples

Consider a script that pings hosts supplied in a web form:

#!/bin/bash
  
hostname=$USER_INPUT 
ping -c 3 $hostname

This allows command injection into the ping command. An attacker can submit ;shutdown -h now to shut down the server. The secure way is to escape the input:

  
#!/bin/bash

hostname=$USER_INPUT
ping -c 3 -- "$hostname" 

Here is another example vulnerable to injection from unsafe $@ expansion:

#!/bin/bash

user="$1"
lookup "$user"

The fix is to use proper quoting around $@:

  
#!/bin/bash
  
user="$1"
lookup "$@"

These examples illustrate why careful escaping and quoting is essential when incorporating external input into shell scripts.

How to Test for Vulnerabilities

Several techniques can help test shell scripts for command injection flaws:

  • Fuzz test by supplying randomized unexpected input
  • Attempt common attack patterns like semicolon insertion
  • Use linters like shellcheck to catch issues
  • Examine scripts line by line for unsafe expansions
  • Validate assumptions about validity of inputs

For high-risk scripts, code review, static analysis, dynamic scanning tools, and manual testing can help avoid overlooking flaws.

Testing inputs by intentionally trying to break validation rules can reveal overlooked attack surface in input handling. Assume all user input requires escaping.

Conclusion

Preventing command injection requires diligent escaping of untrusted inputs using — or single quotes in shell scripts. Validate that inputs match expected formats. Be extremely cautious about expanding unvalidated data into sensitive command options.

Safer alternatives include passing user data as arguments, using whitelists, or avoiding invoking commands based on input when possible. Test scripts vigilantly to detect command injection flaws before deploying. Make injection-proofing untrusted input a routine secure coding practice.

Leave a Reply

Your email address will not be published. Required fields are marked *