If luck wasn’t on your side, you might have encountered a webshell. For the fortunate, a webshell is a file containing a high-level scripting language (usually PHP, JSP, ASP, etc.) that provides a custom web interface for an attacker when it is uploaded to a victim web server. A webshell may be implanted by several mechanisms. By far the most popular over the last decade within our observations has been via arbitrary file upload vulnerabilities, whereby a web upload function does not have sufficient restrictions and validations enforced upon incoming files.
In recent years, however, there has been a rise in leveraging remote code execution/command injection vulnerabilities to implant a webshell or at least to gain a foothold on a victim server and then implant a webshell thereafter.
Scale
Sadly for all of us, the scale of incoming webshell upload attempts from attackers is significant; the rate of attempts we see in Fortra’s Threat Intelligence IDS alerts from customer environments ranges from as low as 1,000 and as high as 50,000 unique attempts daily. Whenever a new arbitrary file upload vulnerability appears within a popular web software, that rate will jump. Our record a few years ago, after a vulnerability disclosure for a particular WordPress plugin, was greater than 300,000 attempts across two days. Webshells are a still a big problem.
Categories
We categorize webshells into three main types:
Webshell
- Most commonly observed for: Command-and-Control (C2) for Actions-on-Objective
- Common size range: 50kB – 150kB
- These are the classic, fully formed web interface, which may have rich HTML with CSS styling and potentially even mature JavaScript if the developer is so inclined. These shells offer a comprehensive set of features such as file upload, code or script execution, file editing, filesystem navigation or as a web GUI file explorer, with functions that may even allow an attacker to compromise multiple software stacks beyond web on the host (SQL or FTP server) or to use the compromised server to attack others. For the initiated, think of the old names such as C99 and its variants, WSO, b374k, etc.
These are the most common examples people will think of when hearing the word “webshell.” These days, they are by far the least common within attempts we observe against our customers, though these types of shell do still appear.
Minishell
- Most commonly observed for: Foothold/staging (with some C2)
- Common size range: 1kB – 15kB
- While these usually offer a small, simple interface, they are not as fully featured and offer only two-four functions such as file upload, directory listing/navigation or command execution; it is as common to see these with CSS or similar styling (such as below) as without.
A common use case we have observed with these over the years has been to use them as the initial foothold from which to deploy a second payload. We have even seen a minishell used to upload and deploy a full webshell on more than one occasion.
Microshell
- Most commonly observed for: Recon
- Common size range: 40B – 500B
- These files are the smallest and most common types of attempt we currently see. Microshells are usually one-liner PHP snippets that will perform one or two basic functions such as echoing a string or a file or executing a hardcoded OS command in response to a request to show successful implantation, ergo successful compromise by whichever vulnerability or attack was attempted.
In the majority of cases, these shells do not afford any interaction to the attacker and simply return an expected response, although we do still observe one-liner PHP shells which will accept and execute a command passed as a request parameter, affording C2
Management of Analysis
For example, attempting to analyze, 3,417 PHP-based webshell upload attempts (our intake for third October 2024) across the customer base would be nonsensical for humans to do without some form of automation and deduplication. The good news is that after scraping those shells out of the network streams and processing them for deduplication, those greater than 3,000 attempts were comprised of only 33 unique webshell payloads. Manual analysis of approximately 33 or more webshells a day, every day, is still a tall order. Taking it a step further, only two of those webshells were unique enough that we may have not seen them before, so this would take the daily workload to two (for PHP shells at least). Even at a greatly reduced work rate, this scenario still tasks a human with daily, manual review of webshells, which is only one of the many types of malicious files that we must care about, so we can do better.
Automation for Existing Coverage Tests
Enter ShellBase, our continuous webshell testing pipeline. For every unique enough shell we import into our analysis and storage framework, we throw the shell into an ephemeral container appropriate for the shell language. A PHP shell will cause a containerized Apache with PHP to spin up and accept the shell upload. While capturing network traffic, we send a request to the implanted shell to determine what the first response from the shell will be, which is what we then want to model within IDS detection logic to spot the same response if it is sent from a customer environment. After the request/response, the traffic capture is sent for processing with our IDS detection logic to see if our existing coverage will alert on the shell.
We are now in a position where a person needs only to spend time analyzing novel webshells not already covered although, frustratingly, many of these shells are quite old and broken. At times, manual tweaking may be required for the shell to work at all and then test for existing coverage again.
Clustering
Automation pipelines are a great help but done with a “Don’t trust, just verify” mindset when dealing with malicious file coverage, we still want to have a good understanding across everything we collect. We are back to dealing with the entire corpus which, after heavy deduplication, leaves us with approximately 11,300 webshells, which is the total for just the PHP collection.
A while ago, we discovered an excellent project (Wallace, 2014; Wallace, 2015) using similarity comparison of ssdeep hashes for clustering a collection of webshells into groups to enumerate “families” of shells. An early attempt to use the project tooling for this purpose with our webshells produces a fairly nice demonstration of groupings; although clustering for images is great for display and communication, this doesn’t afford an optimization for workflow as is.
Influenced by this prior work (Wallace, 2015), we implemented the process of using ssdeep hashes for clustering within our internal malware storage and analysis API framework, so that we can attack the groupings for coverage development rather than having to group manually or, worse, tackle shells individually. The research process now is as follows:
- Pick a bucket of malicious files (in this case, all PHP webshells) and send off an API request to cluster the bucket in bulk
- Review a few examples of the group to find file components less likely to change across the family
- Confirm that the hypothetically unchanging components do indeed exist across all members of the clustered groups
- Create the appropriate detection logic proposals incorporating the confirmed unchanging components
- Test the new detection logic, via the ShellBase traffic capture pipeline, to confirm alerts fire successfully
Show and Shell
The first thing people will notice when they cluster webshells with bulk analysis is the significant incidence of “vanity forks” — the time-honored behavior of changing a name, title, or similar component of a malicious file to indicate ownership/authorship without changing the fundamental functional code of the file. Within the clustered groups, we see this a lot:
The other discovery we want to find is a singular actor or group using the same shell but modifying the secondary or tertiary C2 IoCs. We may be able to use the future IDS data from a shell-specific signature to extract these ephemeral IoCs with a different processing pipeline and be able to
- Track the shell usage for the actor/group across campaign pushes or infrastructure iterations over time
- Produce additional, post-compromise coverage signatures which detect outbound requests from customer environments to enumerated C2 infrastructure if the ephemeral IoCs are remote IP addresses or domains
A minishell example below utilizes an email callback mechanism to alert the threat actor of successful implantation. These two shells were harvested from attack attempts over a month apart and can reasonably be used for campaign tracking based on the other “vanity” components of the shell not changing.
A common observation in clustered microshells is an ephemeral remote host being used to create a recon callback or even to deliver a secondary file payload via outbound web request. The following shell examples, attempting to masquerade as an image file to bypass common filetype upload restrictions, would send an outbound request to popular OAST (Portswigger, no date) public testing domains used for exploitation attempts where success may not generate an inline response for confirmation:
With the dual approach of continuous, automated coverage testing to unearth new samples for review and periodic bulk analysis of everything, we aim to keep on top of the problem as thoroughly and efficiently as possible whilst striving to take away as many tasks as possible that humans should no longer have to sink time into.
At Fortra’s Alert Logic, we stay ahead of evolving threats with our powerful, multi-vector approach to threat research.