mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-06 07:48:57 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
169 lines
6.1 KiB
Python
169 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Insider threat behavior detection agent using UEBA indicators.
|
|
|
|
Analyzes user activity logs to detect anomalous behaviors: off-hours access,
|
|
mass file downloads, unusual data access patterns, and privilege abuse.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
from collections import defaultdict
|
|
from datetime import datetime
|
|
|
|
RISK_INDICATORS = {
|
|
"off_hours_access": {"weight": 15, "desc": "Activity outside business hours"},
|
|
"mass_download": {"weight": 25, "desc": "Bulk file download/copy"},
|
|
"privilege_escalation": {"weight": 30, "desc": "Unauthorized privilege use"},
|
|
"unusual_destination": {"weight": 20, "desc": "Data sent to unusual destination"},
|
|
"resignation_correlated": {"weight": 35, "desc": "Activity correlated with resignation"},
|
|
"usb_mass_copy": {"weight": 30, "desc": "Mass copy to removable media"},
|
|
"cloud_upload": {"weight": 20, "desc": "Large upload to personal cloud"},
|
|
"email_to_personal": {"weight": 15, "desc": "Forwarding to personal email"},
|
|
}
|
|
|
|
BUSINESS_HOURS = (8, 18)
|
|
PERSONAL_DOMAINS = {"gmail.com", "yahoo.com", "hotmail.com", "outlook.com",
|
|
"protonmail.com", "icloud.com", "aol.com"}
|
|
CLOUD_STORAGE = {"dropbox.com", "drive.google.com", "onedrive.live.com",
|
|
"box.com", "mega.nz", "wetransfer.com"}
|
|
|
|
|
|
def parse_activity_log(filepath):
|
|
events = []
|
|
with open(filepath, "r", encoding="utf-8", errors="replace") as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
try:
|
|
evt = json.loads(line)
|
|
events.append(evt)
|
|
except json.JSONDecodeError:
|
|
parts = line.split(",")
|
|
if len(parts) >= 4:
|
|
events.append({
|
|
"timestamp": parts[0], "user": parts[1],
|
|
"action": parts[2], "detail": ",".join(parts[3:]),
|
|
})
|
|
return events
|
|
|
|
|
|
def detect_off_hours(events):
|
|
findings = []
|
|
for evt in events:
|
|
ts = evt.get("timestamp", "")
|
|
try:
|
|
dt = datetime.fromisoformat(ts.replace("Z", "+00:00"))
|
|
hour = dt.hour
|
|
if hour < BUSINESS_HOURS[0] or hour >= BUSINESS_HOURS[1]:
|
|
findings.append({
|
|
"indicator": "off_hours_access",
|
|
"user": evt.get("user", ""),
|
|
"timestamp": ts,
|
|
"hour": hour,
|
|
"action": evt.get("action", ""),
|
|
})
|
|
except (ValueError, TypeError):
|
|
continue
|
|
return findings
|
|
|
|
|
|
def detect_mass_download(events, threshold=50):
|
|
user_downloads = defaultdict(list)
|
|
for evt in events:
|
|
action = evt.get("action", "").lower()
|
|
if any(kw in action for kw in ("download", "copy", "export", "fileaccessed")):
|
|
user_downloads[evt.get("user", "")].append(evt)
|
|
|
|
findings = []
|
|
for user, downloads in user_downloads.items():
|
|
if len(downloads) >= threshold:
|
|
findings.append({
|
|
"indicator": "mass_download",
|
|
"user": user,
|
|
"file_count": len(downloads),
|
|
"time_range": f"{downloads[0].get('timestamp', '')} - {downloads[-1].get('timestamp', '')}",
|
|
"severity": "HIGH" if len(downloads) > 100 else "MEDIUM",
|
|
})
|
|
return findings
|
|
|
|
|
|
def detect_data_exfil_destinations(events):
|
|
findings = []
|
|
for evt in events:
|
|
detail = evt.get("detail", "").lower()
|
|
dest = evt.get("destination", "").lower()
|
|
target = detail + " " + dest
|
|
|
|
for domain in PERSONAL_DOMAINS:
|
|
if domain in target:
|
|
findings.append({
|
|
"indicator": "email_to_personal",
|
|
"user": evt.get("user", ""),
|
|
"destination": domain,
|
|
"timestamp": evt.get("timestamp", ""),
|
|
})
|
|
for cloud in CLOUD_STORAGE:
|
|
if cloud in target:
|
|
findings.append({
|
|
"indicator": "cloud_upload",
|
|
"user": evt.get("user", ""),
|
|
"destination": cloud,
|
|
"timestamp": evt.get("timestamp", ""),
|
|
})
|
|
if any(kw in target for kw in ("usb", "removable", "external drive", "e:")):
|
|
findings.append({
|
|
"indicator": "usb_mass_copy",
|
|
"user": evt.get("user", ""),
|
|
"timestamp": evt.get("timestamp", ""),
|
|
})
|
|
return findings
|
|
|
|
|
|
def calculate_risk_score(user_findings):
|
|
score = 0
|
|
indicators = set()
|
|
for f in user_findings:
|
|
ind = f.get("indicator", "")
|
|
if ind in RISK_INDICATORS:
|
|
score += RISK_INDICATORS[ind]["weight"]
|
|
indicators.add(ind)
|
|
risk = "CRITICAL" if score >= 80 else "HIGH" if score >= 50 else \
|
|
"MEDIUM" if score >= 25 else "LOW"
|
|
return {"score": min(score, 100), "risk_level": risk, "indicators": list(indicators)}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Insider Threat Behavior Detector")
|
|
parser.add_argument("--activity-log", required=True, help="User activity log (JSON lines or CSV)")
|
|
parser.add_argument("--download-threshold", type=int, default=50)
|
|
args = parser.parse_args()
|
|
|
|
events = parse_activity_log(args.activity_log)
|
|
all_findings = []
|
|
all_findings.extend(detect_off_hours(events))
|
|
all_findings.extend(detect_mass_download(events, args.download_threshold))
|
|
all_findings.extend(detect_data_exfil_destinations(events))
|
|
|
|
user_findings = defaultdict(list)
|
|
for f in all_findings:
|
|
user_findings[f.get("user", "unknown")].append(f)
|
|
|
|
user_risks = {}
|
|
for user, findings in user_findings.items():
|
|
user_risks[user] = calculate_risk_score(findings)
|
|
user_risks[user]["finding_count"] = len(findings)
|
|
|
|
results = {
|
|
"timestamp": datetime.utcnow().isoformat() + "Z",
|
|
"total_events": len(events),
|
|
"total_findings": len(all_findings),
|
|
"user_risk_scores": user_risks,
|
|
"findings": all_findings,
|
|
}
|
|
print(json.dumps(results, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|