Web Shells: How They’re Used in Post-Exploitation, and How I Detect and Remove Them

webshells: Backdoor web shells for post-exploitation

A quick note before diving in: I’m covering web shells from the defender’s side — how they work conceptually, how they get planted, and how to find and remove them during incident response. I’m intentionally not publishing working backdoor code here. A functional web shell is, by definition, malware, and posting one that anyone could copy-paste onto a server does more harm than good. If you’re studying offensive use in an authorized lab, the safe path is a controlled, disposable environment (like a personal VM) using tools already built for that purpose in a course context (e.g., a CTF platform), never a script pulled from a blog post.

What Is a Web Shell?

A web shell is a small script — usually PHP, ASP/ASPX, JSP, or occasionally Python/Perl — that an attacker uploads to a compromised web server to maintain remote access after an initial breach. Once in place, it lets the attacker run commands, browse the filesystem, upload/download files, and often pivot deeper into the network, all through ordinary HTTP requests that can blend in with normal web traffic.

Web shells are a post-exploitation persistence mechanism. They don’t cause the initial breach — that comes from something else: an unpatched CMS plugin, an insecure file upload form, a vulnerable admin panel, or a misconfigured server. The web shell is what the attacker leaves behind afterward so they don’t have to re-exploit the original vulnerability every time they want access.

How Web Shells Get Planted

Common entry points I look for during an investigation:

What a Web Shell Lets an Attacker Do

Functionally, once deployed, a web shell typically offers:

Detecting Web Shells

This is the part that actually matters for defenders, so I’ll go deep here.

1. File Integrity and Timestamp Analysis

find /var/www -type f \( -name "*.php" -o -name "*.jsp" -o -name "*.asp*" \) -newer /var/www/known_good_file.php

Compare recently modified files against a known-clean baseline or your deployment’s version control history. Anything modified outside a deployment window is suspicious.

2. Signature and Keyword Scanning

Look for functions commonly abused in web shells:

grep -rEl "eval\(|base64_decode\(|shell_exec\(|passthru\(|system\(|assert\(|exec\(" /var/www/html --include="*.php"

This isn’t proof of a web shell by itself (legitimate code uses these functions too), but it narrows the search dramatically. Follow up by manually reviewing each hit.

3. Entropy and Obfuscation Checks

Attackers often base64-encode or otherwise obfuscate shell code to dodge simple grep signatures. Files with unusually high entropy (long strings of random-looking characters) relative to the rest of the codebase are a red flag:

for f in $(find /var/www/html -name "*.php"); do
  python3 -c "
import math, collections, sys
data = open('$f','rb').read()
if len(data) == 0: exit()
freq = collections.Counter(data)
entropy = -sum((c/len(data)) * math.log2(c/len(data)) for c in freq.values())
if entropy > 6.5:
    print(f'{entropy:.2f} $f')
"
done

4. Web Server Access Log Analysis

Web shells generate distinctive traffic patterns — repeated POST requests to an unusual file, often with short, cryptic parameter names:

awk '{print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -30

Look for .php files in unexpected directories (e.g., inside /uploads/), or files receiving disproportionately many POST requests compared to normal traffic.

5. Dedicated Web Shell Scanners

Tools purpose-built for this:

# php-malware-finder (signature-based scanner)
git clone https://github.com/nbs-system/php-malware-finder.git
cd php-malware-finder
./phpmalwarefinder /var/www/html
# Yara rules for known web shell families
yara -r webshell_rules.yar /var/www/html -w

These use curated signature databases maintained by the security community, updated as new web shell variants appear.

Incident Response Workflow

  1. Isolate — take the affected host off the network (or into a monitored quarantine VLAN) without powering it off, to preserve memory/log evidence.
  2. Image the disk for forensic preservation before making any changes.
  3. Identify the entry point — check access logs around the file’s creation timestamp to trace back to the original exploit.
  4. Identify all persistence — attackers rarely plant just one shell; search broadly using the detection methods above across the entire webroot and any writable directories.
  5. Check for lateral movement — review auth logs, cron jobs, and scheduled tasks for additional backdoors planted after initial access.
  6. Remove and patch — delete the malicious files, then patch the original vulnerability (update the CMS/plugin, fix the upload validation, rotate credentials).
  7. Rebuild from a known-clean backup where possible, rather than trying to “clean” a compromised system in place.

Prevention & Hardening

# Apache .htaccess in the uploads directory
<FilesMatch "\.(php|php5|phtml)$">
    Require all denied
</FilesMatch>

Real-World Use Cases (Defensive Side)

Common Mistakes I See

FAQ

Are all uses of eval() or base64_decode() malicious? No. Plenty of legitimate frameworks and libraries use these functions. That’s why signature scanning is a starting point for investigation, not a final verdict — always review flagged files manually.

Can antivirus software reliably catch web shells? Traditional signature-based AV catches known shells but frequently misses custom or obfuscated variants. Combining log analysis, entropy checks, and integrity monitoring is far more reliable.

How do I practice detecting web shells legally? Set up an isolated lab VM, intentionally plant a known, publicly documented test sample from a security training platform (many CTF and blue-team training sites provide these in contained environments), and practice the detection workflow above without ever exposing it to the internet.

Summary

Web shells are the “aftermath” tool of a breach, not the cause — the real fix always traces back to the vulnerability that let the file get uploaded in the first place. As a defender, the goal is layered: prevent execution in upload directories, monitor for anomalies, detect quickly when something slips through, and always look for the root cause and any additional persistence before calling an incident closed.

References

Exit mobile version