Code injection, also called Remote Code Execution (RCE), occurs when an attacker exploits an input validation flaw in software to introduce and execute malicious code. Code is injected in the language of the targeted application and executed by the server-side interpreter for that language – PHP, Python, Java, Perl, Ruby, etc. Any application that directly evaluates unvalidated input is vulnerable to code injection, and web applications are a prime target for attackers. This article shows how code injection vulnerabilities arise and how you can protect your web applications from injection.
PHP Code Injection Example
Let's start with a quick example of vulnerable PHP code. The PHP
eval() function provides a quick and convenient way of executing string values as PHP code, especially in the initial phases of development or for debugging. However, when used with unknown inputs, it can leave your application vulnerable to code injection. Here’s a typical example of quick-and-dirty query string processing – just a simple echo command, like you might use for debugging parameters:
<?php eval ("echo ".$_REQUEST["user_name"].";"); ?>
The PHP interpreter will attempt to evaluate whatever is passed in the user_name parameter. As the parameter name implies, the developer expects the query string to contain a valid user name, for example:
However, an attacker might supply the following query string to exploit the vulnerable construct and inject PHP code into the application:
If successful, this injection will cause the PHP interpreter to echo admin, but then execute
phpinfo(), providing the attacker with information about the operating system, PHP version, and other configuration details.
system() function is disabled in PHP interpreter settings, a successful code injection can use this function to execute operating system commands, in effect performing command injection (see note below). Working with the vulnerable code shown above, an attacker might supply the following URL to a Linux-based server:
Again, this will echo admin and then execute code injected after the semicolon. In this example,
system('ls -l') runs the
ls -l command to list the contents of the PHP interpreter’s working directory, including permissions.
Note: Code injection is a separate concept from command injection (shell injection). An attacker exploiting a command injection vulnerability is limited to injecting commands of the underlying operating system, while a code injection vulnerability allows them to execute arbitrary code in the server-side interpreter for the web application’s language.
How Code Injection Attacks Work
Although the example above is only for PHP, the same principles apply to all other web application languages interpreted on the server. In general, an application is considered to have a code injection vulnerability when both of the following conditions occur:
- Lack of proper input validation (CWE-94)
- Dynamic evaluation of user input in a dangerous way (CWE-95)
In this case, user input is any data that is processed by the application and can be entered or manipulated by application users. This covers not just direct input, for example via form fields or file uploads, but also query string parameters, cookies and all other data sources that are beyond the developer’s control. The application usually expects specific types of input, and developers can neglect to validate and sanitize actual input data, especially if testing or debugging code makes it into the production application.
An application vulnerable to code injection takes this untrusted data and directly uses it in program code. This typically involves the use of
eval() or an equivalent function (depending on the language), but a direct concatenation of user-supplied strings also constitutes unsafe processing. An attacker can exploit such vulnerabilities by supplying malicious code in the application language. If successful, the attack can give full access to the server-side interpreter, allowing the attacker to execute arbitrary code within its process on the server. If the application has access to system calls, the attacker may be able to escalate this vulnerability to run system commands on the server, allowing command injection attacks.
How to Protect Applications from Code Injection
Regardless of language, you can avoid code injection vulnerabilities and improve web application security by following some basic security practices:
- Validate and sanitize inputs: Scan for escape characters and other special symbols for the application language and operating system, such as comment marks, line termination characters and command delimiters. If your application only expects a limited set of values, accept only those values, for example by whitelisting or conditionally switching on them.
- Avoid vulnerable evaluation constructs: If at all possible, avoid using eval() and equivalent functions on raw user inputs. Use dedicated language-specific features to safely process user-supplied arguments.
- Treat all data as untrusted: Be aware of all places where the user can provide or manipulate data for your application. Apart from obvious injection vectors such as query strings and HTML forms, code can also be injected via specially crafted data files, manually modified cookies, and other methods.
- Lock down your interpreter: If you have control over your server configuration, you may want to limit interpreter functionality to the minimum required for your application to prevent escalation to system command injection. For example, if your PHP application doesn’t use the
system()function, you can disable that function in your php.ini file by specifying it in the disable_functions directive. Commonly disabled functions for PHP include:
- Check your code: Use static code checking tools to scan for vulnerabilities related to input validation and unsafe evaluation.
- Scan your applications: Use a dynamic web vulnerability scanner to ensure your applications are safe from various types of attacks, including code injection.