UEBA 用户行为分析是一种以行为基线为核心的检测方法,它不依赖单条特征规则,而是先统计账号在正常状态下的登录时间、来源 IP、目标主机与操作频率,再用偏离度判断异常。对没有预算采购商业 SIEM 的中小企业内网来说,用 Elastic Stack 加一台采集节点就能搭起一套可用的 UEBA,把异地登录、异常时段操作、账号横向移动这几类最容易被忽略的风险捞出来。
适用场景
- 内网已经有 Wazuh、Syslog 或 ELK,但规则只有「登录失败即告警」这类静态判断,一天几百条误报,运维直接把告警关了。
- 需要检测的目标集中在四类:非常用地点或非常用时段的成功登录、针对单一账号的高频暴力破解、同一账号短时间登录多台主机的横向移动、内部人员异常提权与批量数据下载。
- 规模在 50 至 500 台主机之间,没有专职安全运营人员,希望告警数量可控且每条都能解释清楚。
前置条件
- Elasticsearch 8.x 与 Kibana 8.x(单节点即可,8 GB 内存起步)。
- 被监控主机安装 Filebeat 8.x;Windows 主机改用 Winlogbeat,采集安全事件日志 ID 4624(登录成功)、4625(登录失败)、4672(特权分配)。
- Linux 主机启用 auditd,并保留 /var/log/auth.log(Debian 系)或 /var/log/secure(RHEL 系)。
- 一个能读取审计日志的采集账号,以及可执行 curl 的管理终端。
原理说明
UEBA 的处理链条只有三步,理解这三步就能理解后面所有配置的用途。
第一步是采集。需要落地的事件只有三类:登录事件(谁、从哪个 IP、登录哪台主机、成功了没有)、进程执行事件(谁执行了什么命令、是否以 root 身份)、网络连接事件(登录后连了哪些内网端口)。登录事件回答「有没有异常账号活动」,进程与网络事件回答「异常账号做了什么」。
第二步是建立基线。把事件按「账号 × 主机 × 时间窗口」三维聚合,得到每个账号的历史分布。例如运维账号 zhangsan 过去 30 天只在 08:30 至 19:30 之间、从 10.10.20.0/24 网段、登录过 6 台主机;这就是它的正常画像。基线的价值在于它天然贴合你的业务节奏,不需要人工去猜阈值。
第三步是偏离检测。把当前事件与基线比较,超出合理区间的部分按严重度分级。关键在于告警要分层:新 IP 登录属于「提示」,非工作时段提权属于「高危」,单账号 10 分钟登录 5 台主机属于「紧急」。分级之后告警才有人看。
操作步骤
1. 采集层:auditd 落地身份与提权事件
新建审计规则文件,覆盖账号文件变更、sudo 配置变更和低权限账号的提权执行行为。
sudo tee /etc/audit/rules.d/ueba.rules > /dev/null <<'EOF'
-w /etc/passwd -p wa -k ueba_identity
-w /etc/shadow -p wa -k ueba_identity
-w /etc/sudoers -p wa -k ueba_priv
-a always,exit -F arch=b64 -S execve -F auid>=1000 -F euid=0 -k ueba_privcmd
EOF
sudo augenrules --load
sudo auditctl -s | grep -E 'enabled|rules'
第三条规则是关键:只记录普通账号(auid>=1000)通过提权执行命令的动作,服务账号自身的高频 execve 不会进入日志,能把采集量压下去一个数量级。
2. 采集层:Filebeat 发送到 Elasticsearch
sudo tee /etc/filebeat/filebeat.yml > /dev/null <<'EOF'
filebeat.inputs:
- type: filestream
id: audit-log
enabled: true
paths: [/var/log/audit/audit.log]
fields: { log_type: audit }
- type: filestream
id: auth-log
enabled: true
paths: [/var/log/auth.log]
fields: { log_type: auth }
processors:
- add_host_metadata: ~
output.elasticsearch:
hosts: ["http://127.0.0.1:9200"]
username: "elastic"
password: "${ES_PWD}"
EOF
sudo ES_PWD='你的密码' filebeat test output
sudo systemctl enable --now filebeat
3. 存储层:索引生命周期策略
登录日志保留 90 天足够做基线,热数据保留 7 天即可。先建 ILM 策略,再套到模板上。
curl -u elastic:$ES_PWD -X PUT "http://127.0.0.1:9200/_ilm/policy/ueba-90d" \
-H 'Content-Type: application/json' -d '{
"policy": {
"phases": {
"hot": { "actions": { "rollover": { "max_primary_shard_size": "20gb", "max_age": "7d" } } },
"warm": { "min_age": "7d", "actions": { "shrink": { "number_of_shards": 1 }, "forcemerge": { "max_num_segments": 1 } } },
"delete": { "min_age": "90d", "actions": { "delete": {} } }
}
}
}'
4. 基线层:用聚合查询统计账号画像
下面的 Python 脚本从 Elasticsearch 拉取最近 30 天的登录成功记录,按账号聚合出常用 IP、常用时段与常用主机三份画像,输出为 JSON 供检测环节比对。
#!/usr/bin/env python3
import json, requests
ES = "http://127.0.0.1:9200"
AUTH = ("elastic", "你的密码")
def agg_by(field):
body = {
"size": 0,
"query": {
"bool": {
"filter": [
{"range": {"@timestamp": {"gte": "now-30d"}}},
{"match_phrase": {"message": "Accepted"}}
]
}
},
"aggs": {
"user": {
"terms": {"field": "user.name", "size": 500},
"aggs": {field: {"terms": {"field": field, "size": 200}}}
}
}
}
r = requests.post(ES + "/filebeat-*/_search", auth=AUTH, json=body, timeout=60)
r.raise_for_status()
out = {}
for b in r.json()["aggregations"]["user"]["buckets"]:
out[b["key"]] = [x["key"] for x in b[field]["buckets"]]
return out
baseline = {
"src_ip": agg_by("source.ip"),
"host": agg_by("host.name"),
"generated": "auto",
}
json.dump(baseline, open("/etc/ueba/baseline.json", "w"),
ensure_ascii=False, indent=2)
print("账号数:", len(baseline["src_ip"]))
脚本建议通过 cron 每天凌晨重建一次基线:0 3 * * * /usr/local/bin/ueba_baseline.py。基线是滚动的,人员与网络变化会自动被吸收。
5. 检测层:四条覆盖核心风险的行为规则
在 Kibana 的 Stack Management 中新建检测规则,或用 ElastAlert 2 承载。以下查询可直接粘贴到 Kibana Discover 的 KQL 输入框验证。
- 暴力破解:5 分钟内同一账号登录失败超过 10 次。
message:"Failed password" and user.name:* # 聚合维度:user.name + source.ip,阈值 10,窗口 5m - 异常时段登录:本地时间 23:00 至 06:00 之间的成功登录。
message:"Accepted" and not @timestamp.hour:(8 or 9 or 10 or 11 or 12 or 13 or 14 or 15 or 16 or 17 or 18 or 19) - 横向移动:同一账号 10 分钟内成功登录 3 台以上不同主机。
message:"Accepted" # 聚合维度:user.name,cardinality 统计 host.name,阈值 3,窗口 10m - 异常提权:非白名单账号触发 ueba_privcmd 或 sudo 提权。
event.module:"auditd" and auditd.log.key:"ueba_privcmd" and not user.name:(ansible or deploy)
6. 响应层:告警分级与联动
curl -u elastic:$ES_PWD -X POST "http://127.0.0.1:9200/_watcher/watch/ueba_critical" \
-H 'Content-Type: application/json' -d '{
"trigger": { "schedule": { "interval": "5m" } },
"input": {
"search": {
"request": {
"indices": ["filebeat-*"],
"body": {
"query": { "bool": { "filter": [
{ "match_phrase": { "message": "Accepted" } },
{ "range": { "@timestamp": { "gte": "now-5m" } } }
] } }
}
}
}
},
"condition": { "compare": { "ctx.payload.hits.total": { "gt": 0 } } },
"actions": { "log": { "logging": { "text": "UEBA 命中异常登录,请核查来源 IP" } } }
}'
配置验证
按顺序执行以下四条命令,覆盖采集、索引、基线、检测四个环节。
# 1. 确认审计规则已加载
sudo auditctl -l | grep ueba
# 2. 确认索引已写入且有数据
curl -u elastic:$ES_PWD -s "http://127.0.0.1:9200/_cat/indices/filebeat-*?v&h=index,docs.count,store.size"
# 3. 确认基线文件已生成
jq 'keys | length' /etc/ueba/baseline.json
# 4. 确认检测规则能命中
curl -u elastic:$ES_PWD -s "http://127.0.0.1:9200/filebeat-*/_count" \
-H 'Content-Type: application/json' \
-d '{"query":{"match_phrase":{"message":"Failed password"}}}'
验证新 IP 检测是否生效,可以在测试机上用未出现过的源地址登录一次,观察 5 分钟内是否出现对应告警;如果没有,优先检查 source.ip 字段是否被 Filebeat 正确解析,这是 UEBA 中最常见的一处配置遗漏。
常见问题
FAQ 1:只采集 execve 就让 Elasticsearch 磁盘暴涨,怎么控制?
execve 是采集量最大的事件类型。三个措施按优先级使用:一是把 auditd 规则限定为 auid>=1000 且 euid=0,只保留提权执行;二是在 Filebeat 的 processors 中只保留 user.name、host.name、process.name、process.args、@timestamp 五个字段,其余用 drop_fields 丢弃;三是启用上面的 ILM 策略,热数据 7 天后转 warm 并 merge 成单分片。实测三个措施叠加后,500 台主机的日增索引可以控制在 15 GB 以内。
FAQ 2:基线还没有建立起来,前两周是不是没有检测能力?
冷启动期不要开偏离检测,否则告警全是噪声。建议前 14 天只部署采集与基线脚本,检测环节使用固定阈值(如登录失败 10 次、单账号 3 台主机),并且只记录告警、不做任何自动阻断。等基线文件覆盖到全部账号后再切换到偏离度判断,此时告警量通常会下降六成以上,而真实命中率反而上升。
FAQ 3:内网使用 DHCP,机器经常换 IP,新 IP 告警会被刷爆怎么办?
把比对维度从精确 IP 换成网段。在基线脚本中把 source.ip 的前三段提取为 source.subnet 字段(例如 10.10.20),以网段而非单地址作为画像维度;同时在检测规则里维护一份办公网段白名单(如 10.10.0.0/16),来自白名单网段的登录直接降级为记录而不告警。跨网段登录才值得推送到告警通道。
总结
UEBA 的门槛不在算法,而在采集面和基线质量。落到实操上有三条要点:一是采集只取登录、提权执行、网络连接三类事件,其余一律过滤,量控住在可维护区间;二是基线必须滚动重建,让组织变化自然沉淀进去,而不是一次配置长期不动;三是告警按严重度分级,把「提示」类留在看板里,只把「高危」和「紧急」推到人手上。把这套流程跑通之后,内网最容易被忽略的异地登录与账号横向移动就有了可见性,后续再叠加阻断逻辑才有意义。