Featured image of post SOAR Playbook: Insider Threat Detection & Response

SOAR Playbook: Insider Threat Detection & Response

A SOAR playbook for detecting and responding to insider threats — combining DLP alerts, UBA anomalies, access pattern analysis, and a structured investigation workflow.

Overview

Insider threats are the hardest category to detect and respond to. The attacker already has valid credentials, legitimate access to many resources, and contextual knowledge of your environment. Traditional detection — blocking known-bad signatures — is useless. You need behavioural analysis, correlated anomalies, and a carefully designed response workflow that protects both the investigation and the employee’s rights.

This playbook covers three insider threat scenarios:

  1. Data theft — an employee mass-downloading or exfiltrating sensitive data before departure
  2. Privilege abuse — an employee using excessive access beyond their job role
  3. Sabotage — deliberate destruction or modification of systems or data

Detection Sources

Source Signal
DLP (Defender / Symantec) Mass download, USB transfer, email with sensitive attachments
SIEM / UBA Access to resources not visited in past 90 days
Active Directory Privilege escalation, unusual group membership changes
HR System (webhook) Employee in notice period, under PIP, or with recent disciplinary action
Email Security Large outbound attachments, personal email forwarding rules
File Server Audit Bulk file copy, permission change, file deletion events

Decision Tree

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Insider Threat Indicator Received
    Enrich with HR context:
    Is employee in notice period?
    Any recent disciplinary action?
        ┌─────┴───────┐
      YES             NO
     (High Risk)   (Baseline)
        │               │
        ▼               ▼
   Escalate to     Monitor + collect
   HR & Legal      evidence silently
   immediately     for 48 hours
        │               │
        └───────┬────────┘
    Correlate across sources:
    DLP + File Audit + Email + AD
       Calculate risk score
         ┌──────┴──────┐
      Score ≥ 70    Score < 70
         │               │
         ▼               ▼
   Open P2 case      Add to watchlist
   Notify HR/Legal   Continue monitoring
   Preserve evidence

Step-by-Step Logic

Step 1 — Enrich with HR Context

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def get_hr_context(employee_id, hr_api_url, hr_api_key):
    """
    Pull HR signals that elevate insider threat risk.
    This integration requires a formal agreement with HR — handle with care.
    """
    response = requests.get(
        f"{hr_api_url}/employees/{employee_id}/risk_signals",
        headers={'Authorization': f'Bearer {hr_api_key}'}
    )

    if response.status_code != 200:
        return {'available': False}

    data = response.json()
    return {
        'available': True,
        'in_notice_period': data.get('in_notice_period', False),
        'notice_end_date': data.get('notice_end_date'),
        'under_pip': data.get('performance_improvement_plan', False),
        'recent_disciplinary': data.get('disciplinary_action_last_90d', False),
        'department': data.get('department'),
        'manager': data.get('manager_email'),
        'role': data.get('job_title'),
        'access_level': data.get('access_level'),  # Standard / Elevated / Admin
    }

Step 2 — Collect Behavioural Evidence

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def collect_evidence(employee_upn, siem_client, lookback_days=30):
    """
    Pull behavioural data from multiple sources for the investigation window.
    Evidence is collected passively — no action taken at this stage.
    """
    evidence = {}

    # DLP events — mass download, USB transfers, personal email forwarding
    evidence['dlp_events'] = siem_client.search(f"""
        index=dlp user="{employee_upn}"
        | stats count by policy_name, action, dest_type
        | sort -count
    """, days_back=lookback_days)

    # File server access — unusual shares or bulk operations
    evidence['file_access'] = siem_client.search(f"""
        index=wineventlog EventCode IN (4663, 4656)
        SubjectUserName="{employee_upn.split('@')[0]}"
        | stats count, dc(ObjectName) as unique_files by ShareName
        | where unique_files > 50
        | sort -count
    """, days_back=lookback_days)

    # Email audit — large attachments sent outbound
    evidence['email_anomalies'] = siem_client.search(f"""
        index=email_audit sender="{employee_upn}"
        | where attachment_size_mb > 5
        | where NOT (recipient_domain IN ("company.ae", "company.com"))
        | table _time, recipient, subject, attachment_size_mb
    """, days_back=lookback_days)

    # AD — privilege changes, group additions
    evidence['ad_changes'] = siem_client.search(f"""
        index=wineventlog EventCode IN (4728, 4732, 4756)
        MemberName="*{employee_upn.split('@')[0]}*"
        | table _time, EventCode, GroupName, SubjectUserName
    """, days_back=lookback_days)

    # After-hours access
    evidence['after_hours'] = siem_client.search(f"""
        index=vpn OR index=identity user="{employee_upn}"
        | eval hour=strftime(_time, "%H")
        | where hour < 6 OR hour > 22
        | stats count by date_mday, hour, src_ip
    """, days_back=lookback_days)

    return evidence

Step 3 — Calculate Risk Score

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def calculate_insider_risk_score(hr_context, evidence):
    score = 0
    risk_factors = []

    # HR signals
    if hr_context.get('in_notice_period'):
        score += 30
        risk_factors.append('Employee in notice period')
    if hr_context.get('under_pip'):
        score += 20
        risk_factors.append('Employee on Performance Improvement Plan')
    if hr_context.get('recent_disciplinary'):
        score += 15
        risk_factors.append('Recent disciplinary action (last 90 days)')

    # DLP signals
    dlp_events = evidence.get('dlp_events', [])
    if len(dlp_events) > 10:
        score += 25
        risk_factors.append(f'{len(dlp_events)} DLP policy violations')
    elif len(dlp_events) > 3:
        score += 10

    # File access anomalies
    file_access = evidence.get('file_access', [])
    if any(int(r.get('unique_files', 0)) > 500 for r in file_access):
        score += 25
        risk_factors.append('Bulk file access (>500 unique files)')
    elif file_access:
        score += 10

    # Outbound email anomalies
    email_anomalies = evidence.get('email_anomalies', [])
    if len(email_anomalies) > 5:
        score += 20
        risk_factors.append(f'{len(email_anomalies)} large outbound emails to external recipients')

    # After-hours access
    after_hours = evidence.get('after_hours', [])
    if len(after_hours) > 10:
        score += 15
        risk_factors.append('Frequent after-hours access')

    return min(score, 100), risk_factors
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def initiate_legal_hold(employee_upn, case_id, legal_hold_client):
    """
    Preserve all data associated with the employee before any action
    is taken. Evidence preservation must happen before the employee
    is notified or their access is changed.
    """
    hold_items = [
        {'type': 'email',      'target': employee_upn},
        {'type': 'onedrive',   'target': employee_upn},
        {'type': 'teams_chat', 'target': employee_upn},
        {'type': 'sharepoint', 'target': employee_upn},
    ]

    hold_results = []
    for item in hold_items:
        result = legal_hold_client.place_hold(
            target=item['target'],
            hold_type=item['type'],
            case_id=case_id,
            reason='Insider Threat Investigation'
        )
        hold_results.append({
            'type': item['type'],
            'status': result.status,
            'hold_id': result.hold_id
        })

    return hold_results
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def notify_hr_legal(employee_upn, hr_context, risk_score, risk_factors, case_id):
    """
    HR and Legal must be notified BEFORE any access changes.
    The employee must NOT be notified at this stage.
    """
    message = f"""
**[CONFIDENTIAL] Insider Threat Case — {case_id}**

An automated insider threat alert has been raised for review.

**Employee:** {employee_upn}
**Department:** {hr_context.get('department', 'Unknown')}
**Manager:** {hr_context.get('manager', 'Unknown')}
**Risk Score:** {risk_score}/100

**Risk Factors Identified:**
{chr(10).join(f'- {f}' for f in risk_factors)}

**Required Actions:**
1. HR to review and advise on employment law obligations
2. Legal to confirm evidence preservation is in place
3. Security to brief line manager only when approved by HR/Legal
4. Do NOT notify the employee at this stage

Case link: [ServiceNow {case_id}]
"""

    # Send to HR manager and Legal via encrypted channel
    send_email(
        to=[HR_MANAGER_EMAIL, LEGAL_COUNSEL_EMAIL],
        subject=f"[CONFIDENTIAL] Insider Threat Alert — Action Required — {case_id}",
        body=message,
        encrypt=True
    )

Step 6 — Graduated Response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def apply_graduated_response(risk_score, employee_upn, awaiting_hr_approval=True):
    """
    Response is graduated based on risk score and HR/Legal approval.
    No action is taken automatically — all changes require explicit approval.
    """
    if awaiting_hr_approval:
        # Queue actions for HR/Legal approval — do not execute
        return {
            'status': 'PENDING_APPROVAL',
            'queued_actions': get_recommended_actions(risk_score, employee_upn)
        }

    actions_taken = []

    if risk_score >= 90:
        # Immediate suspension — pre-approved by HR/Legal
        entra_disable_account(employee_upn)
        entra_revoke_sessions(employee_upn)
        actions_taken.append('Account suspended and sessions revoked')

    elif risk_score >= 70:
        # Restrict access — remove elevated permissions
        entra_remove_group_memberships(employee_upn, privileged_only=True)
        actions_taken.append('Privileged group memberships removed')
        # Increase monitoring
        siem_add_watchlist(employee_upn, reason='Insider Threat Investigation')
        actions_taken.append('Added to enhanced monitoring watchlist')

    elif risk_score >= 50:
        # Passive monitoring only — no account changes
        siem_add_watchlist(employee_upn, reason='Insider Threat Watch')
        actions_taken.append('Added to monitoring watchlist — no account changes')

    return {'status': 'ACTIONS_APPLIED', 'actions': actions_taken}

Investigation Checklist

Once a case is opened, the analyst follows this checklist:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 Legal hold confirmed on all data sources
 HR and Legal notified (not the employee)
 Evidence package collected (DLP, email, file audit, AD logs)
 Timeline of anomalous activity documented
 Identify what data was accessed or exfiltrated
 Determine if data left the organisation (DLP + proxy logs)
 Interview line manager (only after HR/Legal approval)
 Prepare evidence summary for HR disciplinary process
 Coordinate with HR on offboarding if termination is decided
 Post-incident: review access provisioning for the role

Important Considerations

  • Privacy and employment law — Insider threat investigations are subject to local employment law. Always involve HR and Legal before taking any action or collecting evidence beyond what your existing monitoring policies permit.
  • False positives have real consequences — incorrectly accusing an employee of data theft is a serious matter. Risk scores are indicators, not verdicts.
  • Document everything — the investigation record must be thorough enough to support HR and legal proceedings if necessary.

Contact me at contact@malsayegh.ae to discuss insider threat programme design.

comments powered by Disqus
All rights Reserved for malsayegh.ae
Built with Hugo
Theme Stack designed by Jimmy