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:
- Unrestricted file upload forms — a form that accepts “images” but doesn’t validate file type/extension, letting an attacker upload a
.phpfile disguised as.jpg.php. - Vulnerable CMS plugins/themes — WordPress, Joomla, and Drupal plugin vulnerabilities are a leading cause; this is exactly why tools like WPScan exist, to catch these before attackers do.
- Exposed admin/management panels with default or weak credentials.
- Remote code execution (RCE) vulnerabilities in the application itself, used to write a file directly to disk.
- Compromised FTP/SSH/CPanel credentials, allowing direct file upload without exploiting the app at all.
What a Web Shell Lets an Attacker Do
Functionally, once deployed, a web shell typically offers:
- Arbitrary command execution on the host OS
- File browsing, upload, and download
- Database connection and query execution (reusing the app’s own DB credentials)
- Sometimes a pivot point to scan the internal network from the compromised host
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
- Isolate — take the affected host off the network (or into a monitored quarantine VLAN) without powering it off, to preserve memory/log evidence.
- Image the disk for forensic preservation before making any changes.
- Identify the entry point — check access logs around the file’s creation timestamp to trace back to the original exploit.
- Identify all persistence — attackers rarely plant just one shell; search broadly using the detection methods above across the entire webroot and any writable directories.
- Check for lateral movement — review auth logs, cron jobs, and scheduled tasks for additional backdoors planted after initial access.
- Remove and patch — delete the malicious files, then patch the original vulnerability (update the CMS/plugin, fix the upload validation, rotate credentials).
- Rebuild from a known-clean backup where possible, rather than trying to “clean” a compromised system in place.
Prevention & Hardening
- Disable script execution in upload directories — configure the web server so
/uploads/(or similar) can serve files but never execute PHP/ASP/JSP:
# Apache .htaccess in the uploads directory
<FilesMatch "\.(php|php5|phtml)$">
Require all denied
</FilesMatch>
- Validate file uploads properly — check MIME type, extension, and file content (magic bytes), not just the extension.
- Keep CMS, plugins, and themes patched — the majority of real-world web shell incidents trace back to a known, unpatched vulnerability.
- Use a Web Application Firewall (WAF) to catch common web shell upload and access patterns.
- File integrity monitoring (e.g.,
AIDE,Tripwire, or even a simple cron job hashing your webroot nightly) to catch new/modified files quickly. - Least-privilege file permissions so the web server user can’t write to directories it doesn’t need to.
Real-World Use Cases (Defensive Side)
- Incident response engagements: Identifying and eradicating web shells is one of the most common tasks in a compromised-website cleanup.
- Digital forensics: Establishing a timeline of compromise by correlating web shell file timestamps with access logs and, where available, memory captures.
- Blue team exercises: Practicing detection workflows against intentionally planted (and safely contained) test files in an isolated lab, to build muscle memory before a real incident.
- Security audits: Verifying that upload directories are properly locked down before an application goes to production.
Common Mistakes I See
- Deleting the web shell file without finding the original entry point — the attacker just re-uploads it.
- Restoring from a backup that was already compromised (check backup dates against the earliest known indicator of compromise).
- Assuming one file found means the investigation is done — thorough searches routinely turn up multiple shells across a compromised site.
- Ignoring database-based persistence (some CMS platforms allow malicious code to live in the database, e.g., in a theme/widget field, not just on disk).
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
- OWASP Web Shell overview: https://owasp.org/www-community/attacks/Web_Shell
- php-malware-finder: https://github.com/nbs-system/php-malware-finder
- CISA guidance on web shell detection and mitigation: https://www.cisa.gov/news-events/cybersecurity-advisories
