# Performing User Behavior Analytics

> 执行用户和实体行为分析（UEBA），通过基于 SIEM 的行为基线和统计分析， 检测异常用户活动，包括不可能旅行、异常访问模式、权限滥用和内部威胁。 适用于 SOC 团队通过偏离既定行为规范来识别被攻陷账户或内部威胁。

- Skill: `killvxk/performing-user-behavior-analytics` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add killvxk/performing-user-behavior-analytics`
- Raw SKILL.md: https://api.skillmd.com/api/skills/killvxk/performing-user-behavior-analytics/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- License: Apache-2.0
- Author: killvxk (https://skillmd.com/u/killvxk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/killvxk/performing-user-behavior-analytics

---

# 执行用户行为分析

## 适用场景

以下情况使用本技能：
- SOC 团队需要通过异常认证模式检测被攻陷账户
- 内部威胁项目需要超出基于规则检测的行为监控
- 不可能旅行或地理异常表明凭据已被入侵
- 特权账户监控需要基线偏差检测

**不适用于**作为纪律处分的唯一依据 —— UEBA 发现是需要调查的指标，而非恶意意图的证明。

## 前置条件

- 具有 30 天以上认证和访问日志历史的 SIEM，用于基线创建
- VPN、O365 和 Active Directory 认证日志规范化为 CIM
- 用于基于位置的异常检测的 GeoIP 数据库（MaxMind GeoLite2）
- 身份丰富数据（部门、角色、上级、典型工作时间）
- 具有 UBA 模块的 Splunk Enterprise Security 或等效的 UEBA 能力

## 工作流程

### 步骤 1：建立用户认证基线

从历史数据创建行为基线：

```spl
index=auth sourcetype IN ("o365:management:activity", "vpn_logs", "WinEventLog:Security")
earliest=-30d latest=-1d
| stats dc(src_ip) AS unique_ips,
        dc(src_country) AS unique_countries,
        dc(app) AS unique_apps,
        count AS total_logins,
        earliest(_time) AS first_login,
        latest(_time) AS last_login,
        values(src_country) AS countries,
        avg(eval(strftime(_time, "%H"))) AS avg_login_hour,
        stdev(eval(strftime(_time, "%H"))) AS stdev_login_hour
  by user
| eval avg_daily_logins = round(total_logins / 30, 1)
| eval login_hour_range = round(avg_login_hour, 0)." +/- ".round(stdev_login_hour, 1)." 小时"
| table user, unique_ips, unique_countries, unique_apps, avg_daily_logins,
        login_hour_range, countries
```

### 步骤 2：检测不可能旅行

识别在不可能的时间内从地理位置相距遥远的地点进行的登录：

```spl
index=auth sourcetype IN ("o365:management:activity", "vpn_logs")
action=success earliest=-24h
| iplocation src_ip
| sort user, _time
| streamstats current=f last(lat) AS prev_lat, last(lon) AS prev_lon,
              last(_time) AS prev_time, last(City) AS prev_city,
              last(Country) AS prev_country, last(src_ip) AS prev_ip
  by user
| where isnotnull(prev_lat)
| eval distance_km = round(
    6371 * acos(
      cos(pi()/180 * lat) * cos(pi()/180 * prev_lat) *
      cos(pi()/180 * (lon - prev_lon)) +
      sin(pi()/180 * lat) * sin(pi()/180 * prev_lat)
    ), 0)
| eval time_diff_hours = round((_time - prev_time) / 3600, 2)
| eval speed_kmh = if(time_diff_hours > 0, round(distance_km / time_diff_hours, 0), 0)
| where speed_kmh > 900 AND distance_km > 500
| eval alert = "不可能旅行: ".prev_city.", ".prev_country." -> ".City.", ".Country
| table _time, user, prev_city, prev_country, City, Country, distance_km,
        time_diff_hours, speed_kmh, alert
| sort - speed_kmh
```

### 步骤 3：检测异常登录时间

识别用户正常工作时间之外的登录：

```spl
index=auth action=success earliest=-7d
| eval hour = strftime(_time, "%H")
| eval day_of_week = strftime(_time, "%A")
| eval is_weekend = if(day_of_week IN ("Saturday", "Sunday"), 1, 0)
| eval is_off_hours = if(hour < 6 OR hour > 22, 1, 0)
| join user type=left [
    search index=auth action=success earliest=-60d latest=-7d
    | eval hour = strftime(_time, "%H")
    | stats avg(hour) AS baseline_avg_hour, stdev(hour) AS baseline_stdev_hour,
            perc95(hour) AS baseline_latest_hour by user
  ]
| where (is_off_hours=1 OR is_weekend=1) AND
        (hour > baseline_latest_hour + 2 OR hour < baseline_avg_hour - baseline_stdev_hour * 2)
| stats count, values(hour) AS login_hours, values(day_of_week) AS login_days,
        values(src_ip) AS source_ips
  by user, baseline_avg_hour, baseline_latest_hour
| where count > 0
| sort - count
```

### 步骤 4：检测异常数据访问模式

监控异常文件或数据库访问量：

```spl
index=file_access OR index=sharepoint earliest=-24h
| stats sum(bytes) AS total_bytes, dc(file_path) AS unique_files,
        count AS access_count by user
| join user type=left [
    search index=file_access OR index=sharepoint earliest=-30d latest=-1d
    | stats avg(eval(count)) AS baseline_avg_files,
            stdev(eval(count)) AS baseline_stdev_files,
            avg(eval(sum(bytes))) AS baseline_avg_bytes
      by user
  ]
| eval bytes_gb = round(total_bytes / 1073741824, 2)
| eval z_score_files = round((unique_files - baseline_avg_files) / baseline_stdev_files, 2)
| where z_score_files > 3 OR bytes_gb > 5
| eval anomaly_level = case(
    z_score_files > 5, "严重",
    z_score_files > 3, "高",
    bytes_gb > 10, "严重",
    bytes_gb > 5, "高",
    1=1, "中"
  )
| sort - z_score_files
| table user, unique_files, bytes_gb, baseline_avg_files, z_score_files, anomaly_level
```

### 步骤 5：检测权限滥用模式

监控特权账户使用异常：

```spl
index=wineventlog sourcetype="WinEventLog:Security"
(EventCode=4672 OR EventCode=4624 OR EventCode=4648) earliest=-24h
| eval is_privileged = if(EventCode=4672, 1, 0)
| eval is_explicit_cred = if(EventCode=4648, 1, 0)
| stats sum(is_privileged) AS priv_events,
        sum(is_explicit_cred) AS explicit_cred_events,
        dc(ComputerName) AS unique_hosts,
        values(ComputerName) AS hosts_accessed
  by TargetUserName, src_ip
| join TargetUserName type=left [
    search index=wineventlog EventCode IN (4672, 4624, 4648) earliest=-30d latest=-1d
    | stats dc(ComputerName) AS baseline_hosts,
            avg(eval(count)) AS baseline_daily_events by TargetUserName
  ]
| where unique_hosts > baseline_hosts * 2 OR priv_events > baseline_daily_events * 3
| eval risk_score = (unique_hosts / baseline_hosts * 30) + (priv_events / baseline_daily_events * 20)
| sort - risk_score
| table TargetUserName, src_ip, unique_hosts, baseline_hosts, priv_events,
        baseline_daily_events, risk_score, hosts_accessed
```

### 步骤 6：生成风险评分并优先排序调查

将所有 UEBA 信号聚合为综合风险评分：

```spl
| inputlookup ueba_impossible_travel.csv
| append [| inputlookup ueba_off_hours_access.csv]
| append [| inputlookup ueba_data_access_anomaly.csv]
| append [| inputlookup ueba_privilege_abuse.csv]
| stats sum(risk_points) AS total_risk,
        values(anomaly_type) AS anomaly_types,
        dc(anomaly_type) AS anomaly_count
  by user
| lookup identity_lookup_expanded identity AS user
  OUTPUT department, managedBy, priority AS user_priority
| eval final_risk = total_risk * case(
    user_priority="critical", 2.0,
    user_priority="high", 1.5,
    user_priority="medium", 1.0,
    1=1, 0.8
  )
| sort - final_risk
| head 20
| table user, department, managedBy, anomaly_types, anomaly_count, total_risk, final_risk
```

## 核心概念

| 术语 | 定义 |
|------|-----------|
| **UEBA** | 用户和实体行为分析（User and Entity Behavior Analytics）——针对既定基线检测异常的行为分析 |
| **不可能旅行** | 在使物理旅行不可能的时间框架内，从地理位置相距遥远的地点进行的登录事件 |
| **行为基线** | 从 30-90 天历史数据建立的用户正常活动模式的统计画像 |
| **Z 分数** | 统计量，衡量观测值偏离均值的标准差数量——值 > 3 表示异常 |
| **风险评分** | 聚合多种行为异常并按资产重要性加权的综合数值评分 |
| **同伴组分析** | 将用户行为与同部门/角色的其他人进行比较以识别离群值 |

## 工具与系统

- **Splunk UBA**：与 Splunk ES 集成的专用用户行为分析模块，提供 ML 驱动的异常检测
- **Microsoft Sentinel UEBA**：Azure Sentinel 中的内置 UEBA 功能，具有实体页面和调查图
- **Exabeam Advanced Analytics**：独立 UEBA 平台，具有会话拼接和自动时间线创建
- **Securonix**：云原生 SIEM/UEBA，具有针对内部威胁检测的预建行为模型

## 常见场景

- **账户被攻陷**：不可能旅行 + 非工作时间登录 + 异常应用访问 = 可能的凭据被攻陷
- **内部数据窃取**：员工在离职前通知期内访问正常文件量的 10 倍
- **权限提升滥用**：管理员账户从异常位置访问正常范围之外的系统
- **共享账户检测**：服务账户同时从多个地理位置登录
- **休眠账户重新激活**：90 天以上无活动的账户突然执行特权操作

## 输出格式

```
UEBA 异常报告 — 每周汇总
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
时间段：      2024-03-11 至 2024-03-17
基线用户数：  2,847
检测异常数：  23

高风险用户排名：
#  用户            部门       风险   异常
1. jsmith          财务       94.5   不可能旅行（纽约->莫斯科，2小时）、非工作时间访问、下载 15GB
2. admin_svc01     IT 运维    82.0   从 12 个新 IP 登录，访问 47 台主机（基线：8）
3. mwilson         人力资源   67.3   非工作时间文件访问（凌晨 2 点），下载量是正常的 3 倍

调查状态：
  jsmith：     已上报 Tier 2 — 可能账户被攻陷（IR-2024-0445）
  admin_svc01：审查中 — 可能是新的自动化部署（与 IT 运维确认中）
  mwilson：    等待人力资源背景信息 — 员工在通知期内，已加强监控
```

