security operations

Detecting Insider Data Exfiltration via DLP

Detects insider data exfiltration by analyzing DLP policy violations, file access patterns, upload volume anomalies, and off-hours activity in endpoint and cloud logs. Uses pandas for behavioral analytics and statistical baselines. Use when investigating insider threats or building user behavior analytics for data loss prevention.

data-loss-preventiondlpexfiltration-detectioninsider-threatsecurity-operationsueba
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

  • When investigating security incidents that require detecting insider data exfiltration via dlp
  • When building detection rules or threat hunting queries for this domain
  • When SOC analysts need structured procedures for this analysis type
  • When validating security monitoring coverage for related attack techniques

Prerequisites

  • Familiarity with security operations concepts and tools
  • Access to a test or lab environment for safe execution
  • Python 3.8+ with required dependencies installed
  • Appropriate authorization for any testing activities

Instructions

Analyze endpoint activity logs, cloud storage access, and email DLP events to detect data exfiltration patterns using behavioral baselines and statistical anomaly detection.

import pandas as pd
 
df = pd.read_csv("file_activity.csv", parse_dates=["timestamp"])
# Baseline: average daily upload volume per user
baseline = df.groupby(["user", df["timestamp"].dt.date])["bytes_transferred"].sum()
user_avg = baseline.groupby("user").mean()
 
# Alert on users exceeding 3x their baseline
today = df[df["timestamp"].dt.date == pd.Timestamp.today().date()]
today_totals = today.groupby("user")["bytes_transferred"].sum()
anomalies = today_totals[today_totals > user_avg * 3]

Key indicators:

  1. Upload volume exceeding 3x daily baseline
  2. Access to files outside normal scope
  3. Bulk downloads before resignation
  4. Off-hours file access patterns
  5. USB/external device usage spikes

Examples

# Detect off-hours activity
df["hour"] = df["timestamp"].dt.hour
off_hours = df[(df["hour"] < 6) | (df["hour"] > 22)]
suspicious = off_hours.groupby("user").size().sort_values(ascending=False)
Source materials

References and resources

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

References 1

api-reference.md1.7 KB

API Reference: Detecting Insider Data Exfiltration via DLP

Pandas Behavioral Analytics

import pandas as pd
 
df = pd.read_csv("activity.csv", parse_dates=["timestamp"])
# Columns: timestamp, user, action, file_path, bytes_transferred, destination
 
# Daily volume baseline per user
daily = df.groupby(["user", df["timestamp"].dt.date])["bytes_transferred"].sum()
baseline = daily.groupby("user").agg(["mean", "std"])
 
# Off-hours detection
df["hour"] = df["timestamp"].dt.hour
off_hours = df[(df["hour"] < 6) | (df["hour"] >= 22)]
 
# Bulk download detection
df.set_index("timestamp").groupby("user").resample("1h").size()

Exfiltration Indicators

Indicator Threshold Severity
Volume > 3x baseline Per user daily avg HIGH
Volume > 5x baseline Per user daily avg CRITICAL
Off-hours events > 10 per user HIGH
Bulk downloads > 50 files/hour CRITICAL
USB transfers Any volume HIGH
Sensitive file access Pattern match HIGH

Sensitive File Patterns

patterns = [
    r"\.pem$", r"\.key$", r"\.env$",
    r"credentials", r"password", r"\.kdbx$",
    r"financial", r"payroll", r"customer.*data"
]

Microsoft Purview DLP API

import requests
headers = {"Authorization": "Bearer <token>"}
resp = requests.get(
    "https://graph.microsoft.com/v1.0/security/alerts_v2",
    headers=headers,
    params={"$filter": "category eq 'DataLossPrevention'"}
)

References

Scripts 1

agent.py7.0 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for detecting insider data exfiltration via DLP analysis."""

import json
import argparse
from datetime import datetime

import pandas as pd


def load_activity_logs(log_path):
    """Load file/cloud activity logs."""
    if log_path.endswith(".csv"):
        return pd.read_csv(log_path, parse_dates=["timestamp"])
    return pd.read_json(log_path, lines=True)


def detect_volume_anomalies(df, multiplier=3.0):
    """Detect users with data transfer volume exceeding baseline."""
    df["date"] = df["timestamp"].dt.date
    daily_volume = df.groupby(["user", "date"])["bytes_transferred"].sum().reset_index()
    user_baseline = daily_volume.groupby("user")["bytes_transferred"].agg(
        ["mean", "std"]).reset_index()
    user_baseline.columns = ["user", "avg_bytes", "std_bytes"]
    latest_date = df["date"].max()
    latest_day = daily_volume[daily_volume["date"] == latest_date]
    merged = latest_day.merge(user_baseline, on="user")
    threshold = merged["avg_bytes"] + (multiplier * merged["std_bytes"].fillna(0))
    anomalies = merged[merged["bytes_transferred"] > threshold]
    findings = []
    for _, row in anomalies.iterrows():
        findings.append({
            "user": row["user"],
            "today_bytes": int(row["bytes_transferred"]),
            "avg_bytes": int(row["avg_bytes"]),
            "multiplier": round(row["bytes_transferred"] / max(row["avg_bytes"], 1), 1),
            "severity": "CRITICAL" if row["bytes_transferred"] > row["avg_bytes"] * 5 else "HIGH",
        })
    return sorted(findings, key=lambda x: x["multiplier"], reverse=True)


def detect_off_hours_activity(df, start_hour=6, end_hour=22):
    """Detect file access during off-hours."""
    df["hour"] = df["timestamp"].dt.hour
    off_hours = df[(df["hour"] < start_hour) | (df["hour"] >= end_hour)]
    if off_hours.empty:
        return []
    user_counts = off_hours.groupby("user").agg(
        events=("timestamp", "count"),
        bytes_total=("bytes_transferred", "sum"),
        unique_files=("file_path", "nunique") if "file_path" in df.columns
        else ("filename", "nunique"),
    ).reset_index()
    findings = []
    for _, row in user_counts.iterrows():
        if row["events"] > 10:
            findings.append({
                "user": row["user"],
                "off_hours_events": int(row["events"]),
                "bytes_transferred": int(row["bytes_total"]),
                "unique_files": int(row["unique_files"]),
                "severity": "HIGH",
            })
    return sorted(findings, key=lambda x: x["off_hours_events"], reverse=True)


def detect_bulk_downloads(df, file_threshold=50, time_window="1h"):
    """Detect bulk file downloads in short time windows."""
    findings = []
    df_sorted = df.sort_values("timestamp")
    download_actions = ["download", "copy", "export"]
    action_col = "action" if "action" in df.columns else "event_type"
    downloads = df_sorted[df_sorted[action_col].str.lower().isin(download_actions)]
    if downloads.empty:
        return findings
    downloads = downloads.set_index("timestamp")
    for user, group in downloads.groupby("user"):
        rolling = group.resample(time_window).size()
        bursts = rolling[rolling >= file_threshold]
        if len(bursts) > 0:
            findings.append({
                "user": user,
                "max_downloads_per_hour": int(rolling.max()),
                "burst_periods": len(bursts),
                "total_downloads": len(group),
                "severity": "CRITICAL",
            })
    return findings


def detect_sensitive_file_access(df, sensitive_patterns=None):
    """Detect access to sensitive file types or directories."""
    if sensitive_patterns is None:
        sensitive_patterns = [
            r"\.pem$", r"\.key$", r"\.env$", r"credentials",
            r"password", r"\.kdbx$", r"\.pfx$", r"secret",
            r"financial", r"payroll", r"customer.*data",
        ]
    file_col = "file_path" if "file_path" in df.columns else "filename"
    findings = []
    import re
    for _, row in df.iterrows():
        filepath = str(row.get(file_col, ""))
        for pattern in sensitive_patterns:
            if re.search(pattern, filepath, re.IGNORECASE):
                findings.append({
                    "user": row.get("user", ""),
                    "file": filepath,
                    "pattern_matched": pattern,
                    "action": row.get("action", row.get("event_type", "")),
                    "timestamp": str(row.get("timestamp", "")),
                    "severity": "HIGH",
                })
                break
    return findings[:500]


def detect_usb_activity(df):
    """Detect USB device usage for data transfer."""
    usb_indicators = ["removable", "usb", "external"]
    dest_col = "destination" if "destination" in df.columns else "target"
    usb_events = df[df[dest_col].str.lower().str.contains(
        "|".join(usb_indicators), na=False)]
    if usb_events.empty:
        return []
    user_usb = usb_events.groupby("user").agg(
        events=("timestamp", "count"),
        bytes_total=("bytes_transferred", "sum"),
    ).reset_index()
    findings = []
    for _, row in user_usb.iterrows():
        findings.append({
            "user": row["user"],
            "usb_events": int(row["events"]),
            "bytes_to_usb": int(row["bytes_total"]),
            "severity": "HIGH",
        })
    return findings


def main():
    parser = argparse.ArgumentParser(description="Insider Data Exfiltration Detection Agent")
    parser.add_argument("--log-file", required=True, help="Activity log file")
    parser.add_argument("--output", default="dlp_exfiltration_report.json")
    parser.add_argument("--action", choices=[
        "volume", "off_hours", "bulk", "sensitive", "full_analysis"
    ], default="full_analysis")
    args = parser.parse_args()

    df = load_activity_logs(args.log_file)
    report = {"generated_at": datetime.utcnow().isoformat(), "total_events": len(df),
              "findings": {}}
    print(f"[+] Loaded {len(df)} activity events")

    if args.action in ("volume", "full_analysis"):
        findings = detect_volume_anomalies(df)
        report["findings"]["volume_anomalies"] = findings
        print(f"[+] Volume anomalies: {len(findings)}")

    if args.action in ("off_hours", "full_analysis"):
        findings = detect_off_hours_activity(df)
        report["findings"]["off_hours_activity"] = findings
        print(f"[+] Off-hours activity users: {len(findings)}")

    if args.action in ("bulk", "full_analysis"):
        findings = detect_bulk_downloads(df)
        report["findings"]["bulk_downloads"] = findings
        print(f"[+] Bulk download users: {len(findings)}")

    if args.action in ("sensitive", "full_analysis"):
        findings = detect_sensitive_file_access(df)
        report["findings"]["sensitive_access"] = findings
        print(f"[+] Sensitive file accesses: {len(findings)}")

    with open(args.output, "w") as f:
        json.dump(report, f, indent=2, default=str)
    print(f"[+] Report saved to {args.output}")


if __name__ == "__main__":
    main()
Keep exploring