mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 06:04:56 +03:00
92 lines
4.4 KiB
Python
92 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Web Shell Detection - Analyzes process and file events for web shell indicators on web servers."""
|
|
|
|
import json, csv, argparse, datetime, re
|
|
from collections import defaultdict
|
|
from pathlib import Path
|
|
|
|
WEB_SERVER_PROCESSES = {"w3wp.exe", "httpd.exe", "nginx.exe", "apache.exe", "tomcat.exe", "java.exe", "php-cgi.exe", "node.exe"}
|
|
SUSPICIOUS_CHILDREN = {"cmd.exe", "powershell.exe", "bash", "sh", "whoami.exe", "net.exe", "net1.exe", "ipconfig.exe", "systeminfo.exe", "tasklist.exe", "ping.exe", "nslookup.exe", "certutil.exe"}
|
|
WEBSHELL_EXTENSIONS = {".aspx", ".asp", ".php", ".jsp", ".jspx", ".cfm", ".ashx", ".asmx"}
|
|
WEB_DIRECTORIES = [r"\\inetpub\\", r"\\wwwroot\\", r"\\htdocs\\", r"\\webapps\\", r"\\www\\", r"\\html\\"]
|
|
|
|
def parse_logs(path):
|
|
p = Path(path)
|
|
if p.suffix == ".json":
|
|
with open(p, encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
return data if isinstance(data, list) else data.get("events", [])
|
|
elif p.suffix == ".csv":
|
|
with open(p, encoding="utf-8-sig") as f:
|
|
return [dict(r) for r in csv.DictReader(f)]
|
|
return []
|
|
|
|
def detect_webshell_process(event):
|
|
eid = str(event.get("EventCode", event.get("EventID", "")))
|
|
if eid != "1": return None
|
|
parent = (event.get("ParentImage", event.get("InitiatingProcessFileName", ""))).lower()
|
|
image = (event.get("Image", event.get("FileName", ""))).lower()
|
|
parent_name = parent.split("\\")[-1].split("/")[-1]
|
|
image_name = image.split("\\")[-1].split("/")[-1]
|
|
if parent_name not in WEB_SERVER_PROCESSES: return None
|
|
if image_name not in SUSPICIOUS_CHILDREN: return None
|
|
return {
|
|
"detection_type": "WEBSHELL_PROCESS_SPAWN",
|
|
"technique": "T1505.003",
|
|
"web_server": parent_name, "child_process": image_name,
|
|
"command_line": event.get("CommandLine", event.get("ProcessCommandLine", "")),
|
|
"hostname": event.get("Computer", event.get("DeviceName", "unknown")),
|
|
"user": event.get("User", event.get("AccountName", "unknown")),
|
|
"timestamp": event.get("UtcTime", event.get("Timestamp", "")),
|
|
"risk_score": 80, "risk_level": "CRITICAL",
|
|
"indicators": [f"Web server {parent_name} spawned {image_name}"],
|
|
}
|
|
|
|
def detect_webshell_file(event):
|
|
eid = str(event.get("EventCode", event.get("EventID", "")))
|
|
if eid != "11": return None
|
|
target = event.get("TargetFilename", event.get("FolderPath", "")).lower()
|
|
if not any(re.search(p, target, re.IGNORECASE) for p in WEB_DIRECTORIES): return None
|
|
ext = "." + target.split(".")[-1] if "." in target else ""
|
|
if ext not in WEBSHELL_EXTENSIONS: return None
|
|
return {
|
|
"detection_type": "WEBSHELL_FILE_CREATION",
|
|
"technique": "T1505.003",
|
|
"file_path": target, "extension": ext,
|
|
"creating_process": event.get("Image", event.get("InitiatingProcessFileName", "")),
|
|
"hostname": event.get("Computer", event.get("DeviceName", "unknown")),
|
|
"timestamp": event.get("UtcTime", event.get("Timestamp", "")),
|
|
"risk_score": 70, "risk_level": "HIGH",
|
|
"indicators": [f"Web shell file created: {ext} in web directory"],
|
|
}
|
|
|
|
def run_hunt(input_path, output_dir):
|
|
print(f"[*] Web Shell Hunt - {datetime.datetime.now().isoformat()}")
|
|
events = parse_logs(input_path)
|
|
findings = []
|
|
for e in events:
|
|
r = detect_webshell_process(e)
|
|
if r: findings.append(r)
|
|
r = detect_webshell_file(e)
|
|
if r: findings.append(r)
|
|
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
|
with open(Path(output_dir) / "webshell_findings.json", "w", encoding="utf-8") as f:
|
|
json.dump({"hunt_id": f"TH-WEBSHELL-{datetime.date.today()}", "findings": findings}, f, indent=2)
|
|
print(f"[+] {len(findings)} findings written to {output_dir}")
|
|
|
|
def main():
|
|
p = argparse.ArgumentParser(description="Web Shell Detection")
|
|
sp = p.add_subparsers(dest="cmd")
|
|
h = sp.add_parser("hunt"); h.add_argument("--input", "-i", required=True); h.add_argument("--output", "-o", default="./webshell_output")
|
|
sp.add_parser("queries")
|
|
args = p.parse_args()
|
|
if args.cmd == "hunt": run_hunt(args.input, args.output)
|
|
elif args.cmd == "queries":
|
|
print('''index=sysmon EventCode=1
|
|
| where match(ParentImage, "(?i)(w3wp|httpd|nginx|tomcat|java)\\.exe")
|
|
| where match(Image, "(?i)(cmd|powershell|whoami|net|ipconfig)\\.exe")
|
|
| table _time Computer ParentImage Image CommandLine''')
|
|
else: p.print_help()
|
|
|
|
if __name__ == "__main__": main()
|