threat hunting

Detecting Insider Threat Behaviors

Detect insider threat behavioral indicators including unusual data access, off-hours activity, mass file downloads, privilege abuse, and resignation-correlated data theft.

data-theftinsider-threatmitre-attackproactive-detectionthreat-huntingueba
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

  • When proactively hunting for indicators of detecting insider threat behaviors in the environment
  • After threat intelligence indicates active campaigns using these techniques
  • During incident response to scope compromise related to these techniques
  • When EDR or SIEM alerts trigger on related indicators
  • During periodic security assessments and purple team exercises

Prerequisites

  • EDR platform with process and network telemetry (CrowdStrike, MDE, SentinelOne)
  • SIEM with relevant log data ingested (Splunk, Elastic, Sentinel)
  • Sysmon deployed with comprehensive configuration
  • Windows Security Event Log forwarding enabled
  • Threat intelligence feeds for IOC correlation

Workflow

  1. Formulate Hypothesis: Define a testable hypothesis based on threat intelligence or ATT&CK gap analysis.
  2. Identify Data Sources: Determine which logs and telemetry are needed to validate or refute the hypothesis.
  3. Execute Queries: Run detection queries against SIEM and EDR platforms to collect relevant events.
  4. Analyze Results: Examine query results for anomalies, correlating across multiple data sources.
  5. Validate Findings: Distinguish true positives from false positives through contextual analysis.
  6. Correlate Activity: Link findings to broader attack chains and threat actor TTPs.
  7. Document and Report: Record findings, update detection rules, and recommend response actions.

Key Concepts

Concept Description
T1078 Valid Accounts
T1530 Data from Cloud Storage Object
T1567 Exfiltration Over Web Service

Tools & Systems

Tool Purpose
CrowdStrike Falcon EDR telemetry and threat detection
Microsoft Defender for Endpoint Advanced hunting with KQL
Splunk Enterprise SIEM log analysis with SPL queries
Elastic Security Detection rules and investigation timeline
Sysmon Detailed Windows event monitoring
Velociraptor Endpoint artifact collection and hunting
Sigma Rules Cross-platform detection rule format

Common Scenarios

  1. Scenario 1: Employee downloading bulk files before resignation
  2. Scenario 2: IT admin accessing HR data outside job function
  3. Scenario 3: Service account used for unauthorized data queries
  4. Scenario 4: Contractor copying source code to personal cloud storage

Output Format

Hunt ID: TH-DETECT-[DATE]-[SEQ]
Technique: T1078
Host: [Hostname]
User: [Account context]
Evidence: [Log entries, process trees, network data]
Risk Level: [Critical/High/Medium/Low]
Confidence: [High/Medium/Low]
Recommended Action: [Containment, investigation, monitoring]
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md1.9 KB

API Reference: Detecting Insider Threat Behaviors

Risk Indicator Weights

Indicator Weight Description
resignation_correlated 35 Activity after resignation notice
privilege_escalation 30 Unauthorized privilege use
usb_mass_copy 30 Mass copy to removable media
mass_download 25 Bulk file download/copy (>50 files)
unusual_destination 20 Data sent to unusual destination
cloud_upload 20 Upload to personal cloud storage
off_hours_access 15 Activity outside 8am-6pm
email_to_personal 15 Forwarding to personal email

UEBA Data Sources

Source Indicators
DLP logs File downloads, USB copies, email attachments
Proxy logs Cloud storage uploads, personal email
VPN logs Off-hours access, unusual locations
AD logs Privilege changes, group modifications
Endpoint logs Application usage, screen captures

Splunk SPL - Mass Download Detection

index=dlp action IN ("download", "copy", "export")
| bin _time span=1h
| stats count by user, _time
| where count > 50
| sort -count

Microsoft Sentinel - Off-Hours Access

SigninLogs
| where TimeGenerated between (datetime(22:00)..datetime(06:00))
| where ResultType == 0
| summarize count() by UserPrincipalName, bin(TimeGenerated, 1h)
| where count_ > 5

Personal Cloud Domains

CLOUD_STORAGE = {
    "dropbox.com", "drive.google.com",
    "onedrive.live.com", "box.com",
    "mega.nz", "wetransfer.com"
}

Risk Score Calculation

score = sum(RISK_INDICATORS[ind]["weight"] for ind in detected_indicators)
risk = "CRITICAL" if score >= 80 else "HIGH" if score >= 50 else "MEDIUM"

CLI Usage

python agent.py --activity-log user_activity.jsonl
python agent.py --activity-log events.csv --download-threshold 100
standards.md1.5 KB

Standards and References - Detecting Insider Threat Behaviors

MITRE ATT&CK Mappings

Technique Name Description
T1078 Valid Accounts See attack.mitre.org/techniques/T1078
T1530 Data from Cloud Storage Object See attack.mitre.org/techniques/T1530
T1567 Exfiltration Over Web Service See attack.mitre.org/techniques/T1567

Detection Data Sources

Source Event ID Purpose
Sysmon 1 Process creation with command line
Sysmon 3 Network connection initiated
Sysmon 7 Image loaded (DLL)
Sysmon 10 Process access (LSASS)
Sysmon 11 File creation
Sysmon 12/13 Registry create/set
Sysmon 22 DNS query
Sysmon 25 Process tampering
Windows Security 4624 Successful logon
Windows Security 4625 Failed logon
Windows Security 4648 Explicit credential logon
Windows Security 4672 Special privileges assigned
Windows Security 4688 Process creation
Windows Security 4697 Service installed
Windows Security 4698 Scheduled task created
Windows Security 4769 Kerberos TGS requested
Windows Security 5140 Network share accessed

References

workflows.md2.9 KB

Detailed Hunting Workflow - Detecting Insider Threat Behaviors

Phase 1: Data Collection and Querying

Splunk SPL Query

index=wineventlog EventCode=5145
| where match(Share_Name, "(?i)(finance|hr|legal|confidential|executive)")
| stats count dc(Relative_Target_Name) as unique_files by Account_Name Source_Address
| where unique_files > 100
| sort -unique_files

KQL Query (Microsoft Defender for Endpoint)

CloudAppEvents
| where ActionType in ("FileDownloaded","FileCopied","FileShared")
| summarize FileCount=count(), UniqueFiles=dcount(ObjectName) by AccountObjectId, IPAddress
| where FileCount > 100

Phase 2: Baseline and Anomaly Detection

Step 2.1 - Establish Normal Behavior Baseline

  • Collect 30 days of historical data for the targeted technique
  • Document expected patterns, frequencies, and legitimate use cases
  • Identify known false positive sources and document exceptions
  • Build statistical baseline (mean, standard deviation) for key metrics

Step 2.2 - Identify Anomalies

  • Compare current activity against the 30-day baseline
  • Flag events exceeding 3 standard deviations from normal
  • Prioritize anomalies by risk score and potential business impact
  • Cross-reference with threat intelligence for known IOCs

Phase 3: Investigation and Correlation

Step 3.1 - Deep Dive Analysis

  • For each anomaly, collect full process tree context
  • Correlate with network activity, file operations, and authentication events
  • Check binary signatures, file hashes, and certificate validity
  • Review user account context and access patterns

Step 3.2 - Attack Chain Reconstruction

  • Map findings to MITRE ATT&CK kill chain stages
  • Identify initial access vector if applicable
  • Trace lateral movement and privilege escalation paths
  • Determine data access and potential exfiltration

Phase 4: Validation and Response

Step 4.1 - True/False Positive Determination

  • Verify findings with system owners and IT operations
  • Check change management records for authorized activities
  • Validate user context (authorized actions vs. compromised account)
  • Document determination rationale for each finding

Step 4.2 - Response Actions

  • For confirmed threats: initiate incident response procedures
  • For detection gaps: create or update detection rules
  • For false positives: tune existing rules and update exclusions
  • Update threat hunting playbook with lessons learned

Phase 5: Documentation and Reporting

Step 5.1 - Hunt Report

  • Summarize hypothesis, methodology, and findings
  • Include all queries executed and their results
  • Document IOCs discovered and detection rules created
  • Provide recommendations for security improvements

Step 5.2 - Knowledge Base Update

  • Add findings to threat intelligence platform
  • Update MITRE ATT&CK coverage heatmap
  • Share detection rules via Sigma format
  • Schedule follow-up hunts for related techniques

Scripts 2

agent.py6.1 KB
Display-only source. This catalog never executes bundled scripts.
#!/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()
process.py3.5 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Insider Threat Detection - Analyzes logs for T1078 indicators."""

import json, csv, argparse, datetime, re
from collections import defaultdict
from pathlib import Path

DETECTION_PATTERNS = [
    r'bulk.*download',
    r'mass.*copy',
    r'sensitive.*access',
]

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 analyze_event(event):
    cmd = event.get("CommandLine", event.get("command_line", event.get("ProcessCommandLine", "")))
    content = event.get("Task_Content", event.get("Parameters", event.get("RawEventData", "")))
    search_text = f"{cmd} {content}"
    risk = 0
    indicators = []
    for pattern in DETECTION_PATTERNS:
        if re.search(pattern, search_text, re.IGNORECASE):
            risk += 25
            indicators.append(f"Pattern match: {pattern}")
    if not indicators:
        return None
    risk = min(risk, 100)
    return {
        "technique": "T1078",
        "command_line": cmd[:500] if cmd else content[:500],
        "hostname": event.get("Computer", event.get("DeviceName", event.get("hostname", "unknown"))),
        "user": event.get("User", event.get("AccountName", event.get("UserId", "unknown"))),
        "timestamp": event.get("_time", event.get("timestamp", event.get("UtcTime", event.get("Timestamp", "")))),
        "risk_score": risk,
        "risk_level": "CRITICAL" if risk >= 75 else "HIGH" if risk >= 50 else "MEDIUM" if risk >= 25 else "LOW",
        "indicators": indicators,
    }

def run_hunt(input_path, output_dir):
    print(f"[*] Insider Threat Hunt - {datetime.datetime.now().isoformat()}")
    events = parse_logs(input_path)
    findings = [f for f in (analyze_event(e) for e in events) if f]
    Path(output_dir).mkdir(parents=True, exist_ok=True)
    slug = "detecting_insider_th"
    with open(Path(output_dir) / f"{slug}_findings.json", "w", encoding="utf-8") as f:
        json.dump({"hunt_id": f"TH-{datetime.date.today()}", "total_events": len(events), "findings": findings}, f, indent=2)
    with open(Path(output_dir) / "hunt_report.md", "w", encoding="utf-8") as f:
        f.write(f"# Insider Threat Hunt Report\n\n")
        f.write(f"**Date**: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"**Findings**: {len(findings)}\n\n")
        for finding in sorted(findings, key=lambda x: x["risk_score"], reverse=True)[:20]:
            f.write(f"### [{finding['risk_level']}] {finding['technique']}\n")
            f.write(f"- **Host**: {finding['hostname']}\n")
            f.write(f"- **Indicators**: {', '.join(finding['indicators'])}\n\n")
    print(f"[+] {len(findings)} findings written to {output_dir}")

def main():
    p = argparse.ArgumentParser(description="Insider Threat 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="./detecting_insid_output")
    sp.add_parser("queries")
    args = p.parse_args()
    if args.cmd == "hunt": run_hunt(args.input, args.output)
    elif args.cmd == "queries":
        print("=== Detection Queries ===")
        print("See references/workflows.md for platform-specific queries")
    else: p.print_help()

if __name__ == "__main__": main()

Assets 1

template.mdtext/markdown · 2.6 KB
Keep exploring