Web Shells & Exploitation Fundamentals
A web-shell is a malicious script used by an attacker with the intent to escalate and maintain persistent access on an already compromised web application. A web-shell itself cannot attack or exploit a remote vulnerability, so it is always the second step of an attack (this stage is also referred to as post-exploitation).
An attacker can take advantage of common vulnerabilities such as SQL injection, remote file inclusion (RFI), FTP, or even use cross-site scripting (XSS) as part of a social engineering attack in order to upload the malicious script. The common functionality includes but is not limited to shell command execution, code execution, database enumeration and file management.
Why Use Web-shells?
Persistent Remote Access
A web-shell usually contains a backdoor which allows an attacker to remotely access and possibly, control a server at any time. This would save the attacker the inconvenience of having to exploit a vulnerability each time access to the compromised server is required.
An attacker might also choose to fix the vulnerability themselves, in order to ensure that no one else will exploit that vulnerability. This way the attacker can keep a low-profile and avoid any interaction with an administrator, while still obtaining the same result.
It is also worth mentioning that several popular web shells use password authentication and other techniques to ensure that only the attacker uploading the web-shell has access to it. Such techniques include locking down the script to a specific custom HTTP header, specific cookie values, specific IP addresses, or a combination of these techniques. Most web shells also contain code to identify and block search engines from listing the shell and, as a consequence, blacklisting the domain or server the web application is hosted on – in other words, stealth is key.
Unless a server is misconfigured, the web shell will be running under the web server’s user permissions, which are (or, at least, should be) limited. Using a web-shell, an attacker can attempt to perform privilege escalation attacks by exploiting local vulnerabilities on the system in order to assume root privileges, which, in Linux and other UNIX-based operating systems is the ‘super-user’.
With access to the root account, the attacker can essentially do anything on the system including installing software, changing permissions, adding and removing users, stealing passwords, reading emails and more.
Pivoting and Launching Attacks
A web-shell can be used for pivoting inside or outside a network. The attacker might want to monitor (sniff) the network traffic on the system, scan the internal network to discover live hosts, and enumerate firewalls and routers within the network.
This process can take days, even months, predominantly because an attacker typically seeks to keep a low profile and draw the least amount of attention possible. Once an attacker has persistent access, they can patiently make their moves.
The compromised system can also be used to attack or scan targets that reside outside the network. This adds an additional layer of “anonymity” to the attacker since they are using a 3rd party system to launch an attack. A step further would be to pivot (tunnel) through multiple systems to make it almost impossible to trace an attack back to its source.
Another use of web-shells is to make servers part of a botnet. A botnet is a network of compromised systems that an attacker would control, either to use themselves or to lease to other criminals. The web-shell or backdoor is connected to a command and control (C&C) server from which it can take commands on what instructions to execute.
This setup is commonly used in distributed-denial-of-service (DDoS) attacks, which require expansive amounts of bandwidth. In this case, the attacker does not have any interest in harming, or stealing anything off-of the system upon which the web shell was deployed. Instead, they will simply use its resources for whenever is needed.
Web shells exist for almost every web programming language you can think of. We chose to focus on PHP because it is the most widely-used programming language on the web.
PHP web shells do nothing more than use in-built PHP functions to execute commands. The following are some of the most common functions used to execute shell commands in PHP. Note — For the purposes of this article, we edited our host’s file and pointed the domain www.example.com to a test server.
system() function accepts the command as a parameter and it outputs the result.
The following example on a Microsoft Windows machine will run the
dir command to return a directory listing of the directory in which the PHP file is executing in.
<?php // Return the directory listing in which the file run (Windows) system("dir"); ?> --> Volume in drive C has no label. Volume Serial Number is A08E-9C63 Directory of C:\webserver\www\demo 04/27/2016 10:21 PM <DIR> . 04/27/2016 10:21 PM <DIR> .. 04/27/2016 10:19 PM 22 shell.php 1 File(s) 22 bytes 2 Dir(s) 31,977,467,904 bytes free
Similarly, executing the
ls command on a Linux machine achieves a similar result.
<?php // Return the directory listing in which the file run (Linux) system("ls -la"); ?> --> total 12 drwxrwxr-x 2 secuser secuser 4096 Apr 27 20:43 . drwxr-xr-x 6 secuser secuser 4096 Apr 27 20:40 .. -rw-rw-r-- 1 secuser secuser 26 Apr 27 20:41 shell.php
Other commands have the same effect.
<?php // Return the user the script is running under system(“whoami“); ?> --> www-data
exec() function accepts a command as a parameter but does not output the result. If the second optional parameter is specified, the result will be returned as an array. Otherwise, only the last line of the result will be shown if echoed.
<?php // Executes, but returns nothing exec("ls -la"); ?> -->
echo with the
exec() function, will only print the last line of the command’s output.
<?php // Executes, returns only last line of the output echo exec("ls -la"); ?> --> -rw-rw-r-- 1 secuser secuser 29 Apr 27 20:49 shell.php
If a second parameter is specified, the result is returned in an array.
<?php // Executes, returns the output in an array exec("ls -la",$array); print_r($array); ?> --> Array(  => total 12  => drwxrwxr-x 2 secuser secuser 4096 Apr 27 20:55 .  => drwxr-xr-x 6 secuser secuser 4096 Apr 27 20:40 ..  => -rw-rw-r-- 1 secuser secuser 49 Apr 27 20:54 shell.php )
shell_exec() function is similar to
exec(), however, instead, it outputs the entire result as a string.
<?php // Executes, returns the entire output as a string echo shell_exec(“ls -la“); ?>
--> total 12 drwxrwxr-x 2 secuser secuser 4096 Apr 28 18:24 . drwxr-xr-x 6 secuser secuser 4096 Apr 27 20:40 .. -rw-rw-r-- 1 secuser secuser 36 Apr 28 18:24 shell.php
passthru() function executes a command and returns output in raw format.
<?php // Executes, returns output in raw format passsthru(“ls -la“); ?> --> total 12 drwxrwxr-x 2 secuser secuser 4096 Apr 28 18:23 . drwxr-xr-x 6 secuser secuser 4096 Apr 27 20:40 .. -rw-rw-r-- 1 secuser secuser 29 Apr 28 18:23 shell.php
proc_open() function can be difficult to understand (you can find a detailed description of the function in the PHP docs). Put simply, by using
proc_open() we can create a handler (process) which will be used for the communication between our script and the program we want to run.
preg_replace() with the /e modifier
preg_replace() function can perform a regular expression search and replace. The /e modifier (which is deprecated), executes the replacement with
eval(). This means we can then pass the PHP code to be executed by the
<?php preg_replace('/.*/e', 'system("whoami");', ''); ?> --> www-data
Surprisingly, not many PHP developers are aware of this, however, PHP will execute the contents of backticks ( ` ) as a shell command. Note — The backtick character ( ` ), should not be confused with the single quote character ( ‘ )
<?php $output = `whoami`; echo "<pre>$output</pre>"; ?> --> www-data
Based on the above, the following is a PHP web-shell in its simplest form.
It uses the system() function to execute commands that are being passed through ‘cmd’ HTTP request GET parameter.
We have established that these functions (and a few others) can be very dangerous. What is even more dangerous, is that all these in-built PHP commands are enabled by default when PHP is installed, and the majority of system administrators do not disable them.
If you are unsure whether they are enabled on your system, the following command (PHP CLI needs to be installed) will return a list of the dangerous functions which are enabled.
php -r 'print_r(get_defined_functions());' | grep -E ' (system|exec|shell_exec|passthru|proc_open|popen|curl_exec|curl_multi_exec|parse_ini_file|show_source)'<?php print_r(get_defined_functions()); ?>
On a default installation, we can see that all of the functions mentioned above are enabled.
 => exec  => system  => passthru  => shell_exec  => proc_open  => show_source  => parse_ini_file  => popen