A web shell can sit unused for months, which makes behavioural detection blind to it by design. Scheduled, scoped file analysis closes that gap: community YARA rules for known families, statistical heuristics for obfuscated files, and a baseline of what your web roots are supposed to contain. We recommend running all three, in that order of precision.
Signals at a Glance
| Technique | Finds | Limits |
|---|---|---|
| Maintained YARA rule sets (Neo23x0 signature-base and similar) | Known families and common stubs T1505.003 | Misses custom and heavily obfuscated shells |
| High Shannon entropy and long-line heuristics | Packed, encoded or encrypted payloads T1027 | Minified JavaScript and compressed assets also score high |
| Hash baseline against deployment manifest | Any added or modified file | Needs a clean, versioned baseline |
| Provenance checks (owner, timestamp, creating process) | Files not written by your tooling | Depends on file telemetry |
Scheduled YARA Scans
Run YARA against web roots, upload directories and temporary compilation folders on a schedule, with a maintained rule set as the base. Supplement it with your own small rules that match what you have seen. This rule flags files that decode an input parameter and execute it, a pattern shared by many shells regardless of language:
rule HUNT_WebShell_Decode_And_Execute
{
meta:
description = "Request input decoded then executed (hunt, tune/validate)"
scope = "web roots only"
strings:
$in1 = /\$_(POST|REQUEST|GET|COOKIE)\s*\[/ nocase
$in2 = "Request.Form" nocase
$in3 = "request.getParameter" nocase
$dec1 = "base64_decode" nocase
$dec2 = "FromBase64String" nocase
$dec3 = "gzinflate" nocase
$dec4 = "str_rot13" nocase
$exe1 = /\b(eval|assert|system|passthru|shell_exec|popen|proc_open)\s*\(/ nocase
$exe2 = "Runtime.getRuntime().exec" nocase
$exe3 = "System.Diagnostics.Process" nocase
condition:
filesize < 500KB and
(1 of ($in*)) and (1 of ($dec*)) and (1 of ($exe*))
}
Run it through yara -r (or your EDR’s file scan) and send matches, with file hashes, to your case queue. Exclude directories that hold only third-party libraries after reviewing them once.
Entropy, Structure and Provenance
Obfuscated shells defeat string rules, but they look unusual: long lines, few spaces and high entropy. A quick script ranks files so that an analyst reviews the strangest few first:
#!/usr/bin/env python3
# Rank script files under a web root by entropy and longest line (hunt aid, tune/validate)
import math, os, sys
from collections import Counter
EXT = {".php", ".phtml", ".jsp", ".jspx", ".aspx", ".ashx", ".asmx"}
def entropy(data: bytes) -> float:
if not data:
return 0.0
counts = Counter(data)
n = len(data)
return -sum((c / n) * math.log2(c / n) for c in counts.values())
root = sys.argv[1]
rows = []
for base, _, files in os.walk(root):
for name in files:
if os.path.splitext(name)[1].lower() in EXT:
path = os.path.join(base, name)
try:
data = open(path, "rb").read()
except OSError:
continue
longest = max((len(l) for l in data.splitlines()), default=0)
rows.append((entropy(data), longest, len(data), path))
for e, longest, size, path in sorted(rows, reverse=True)[:50]:
print(f"{e:5.2f} longest_line={longest:6d} size={size:8d} {path}")
- Combine scores: a small file with high entropy, one very long line and a recent creation time is far more suspicious than any one property.
- Hash every script in web roots after each release and store the manifest outside the server. Any file not in the manifest is a lead, and a mismatch on a known file is a stronger one.
- Compare owners and timestamps: files owned by the web server account and created outside a deployment window deserve review.
- When a file is suspicious, do not open it in a browser or run it. Copy it to an isolated analysis host, record hashes, and check access logs for who requested it and when.
- Feed every confirmed shell back into a local YARA rule, so the next instance is detected on the first scan.
Use the Neo23x0 (Florian Roth) signature-base web shell YARA rules, Elastic and SigmaHQ web shell detections, CISA advisories on web shell hunting and vendor integrity checkers. Baseline your own environment: CMS plugins, minified code and legitimate admin tools will match heuristics, so triage by provenance before you act.
Build Detections That Fire
Mjolnir Security helps blue teams turn threat intelligence into high-fidelity detections across endpoint, network, identity and cloud telemetry.
- Detection Engineering We build and tune detections mapped to ATT&CK from your own telemetry.
- Threat Hunting as a Service Hypothesis-driven hunts for the behaviours on this page.
- 24/7 Incident Response Containment when a detection fires. Call +1 833 403 5875.
Stay ahead of emerging threats. Get notified when we publish new intelligence reports and advisories.
Subscribe to Alerts