gweber/mcp-decoy

GitHub: gweber/mcp-decoy

一个伪装成企业 MCP 集成平台的欺骗式蜜罐服务,通过返回虚假数据并记录完整的攻击者交互取证信息,帮助安全团队检测和分析针对 AI 工具基础设施的入侵行为。

Stars: 0 | Forks: 1

# MCP 诱饵服务器 ![Node.js 24+](https://img.shields.io/badge/Node.js-24%2B-339933?logo=node.js&logoColor=white) ![MCP 2024-11-05](https://img.shields.io/badge/MCP-2024--11--05-6B46C1) ![测试 141 通过](https://img.shields.io/badge/tests-141%20passing-brightgreen) ![许可证 MIT](https://img.shields.io/badge/license-MIT-blue) 一个伪装成合法企业 MCP (Model Context Protocol) 集成平台的 Express.js 服务器。每一次交互都会被进行详细的取证记录,并可选择通过 RFC 5424 syslog 转发至 SIEM。专为针对具有 AI 能力的攻击者的欺骗式威胁检测而设计。 ## 概述 企业 AI 工具已成为高价值的攻击目标。威胁行为者通过调用将 LLM 客户端连接到内部服务的工具,入侵 MCP 服务器以窃取凭证、源代码和业务数据。 该服务器将自身呈现为 `enterprise-integrations` —— 一个面向开发工具的、看似合理的 MCP 中心 —— 并对每次工具调用返回令人信服的虚假数据。同时,它会记录源 IP、请求的工具、参数和完整的请求上下文,并将每个事件转发到您的 SIEM。 它内置了 10 个看似合理的企业集成类别,总共包含 38 个工具,其原型通常是通过 MCP 式内部工具暴露出来的各类系统。 **应对的威胁模型:** 攻击者获取了 MCP endpoint URL(例如,通过凭证窃取、供应链入侵或内部侦察),并连接 LLM 客户端以枚举可用工具并窃取数据。 ## 架构 ``` ┌─────────────────────────────────────────────────────────┐ │ MCP Client / LLM Agent │ └────────────┬────────────────────────┬───────────────────┘ │ POST /mcp │ GET /sse │ (Streamable HTTP) │ POST /messages ▼ ▼ (SSE transport) ┌─────────────────────────────────────────────────────────┐ │ index.js │ │ Express 5 · JSON-RPC 2.0 · MCP 2024-11-05 │ │ │ │ handleRpc() ──► tools.js (38 tool dispatchers) │ │ │ └── fake data generators │ │ │ │ │ ▼ │ │ store.js (LogStore, circular buffer, EventEmitter) │ │ │ │ │ ├──► syslog.js (RFC 5424, UDP / TCP) │ │ └──► /api/events (SSE to dashboard) │ └──────────────────┬──────────────────────────────────────┘ │ GET /api/* ▼ ┌─────────────────────────────────────────────────────────┐ │ dashboard/ (Vue 3 + Vite) │ │ Pinia store · Chart.js · Real-time SSE feed │ └─────────────────────────────────────────────────────────┘ ``` **组件:** - `index.js` — Express 服务器、MCP 协议处理(支持两种传输方式)、仪表盘 API 以及访问日志中间件。 - `tools.js` — 全部 38 个工具的定义(MCP `inputSchema`)及其虚假数据响应生成器。 - `store.js` — 内存循环日志缓冲区(10,000 条记录)。Singleton `EventEmitter` 会将每条新记录推送给仪表盘 SSE 订阅者。 - `syslog.js` — RFC 5424 syslog 转发器。支持 UDP(发后即忘)和 TCP(持久连接与重连缓冲)。 - `dashboard/` — Vue 3 SPA,使用 Pinia 进行状态管理,Chart.js 用于时间线图表,并通过 `/api/events` 提供 SSE 实时数据流。 ## 快速开始 ``` git clone https://github.com/gweber/mcp-decoy.git cd mcp-decoy npm install npm start ``` 服务器默认监听 3110 端口。验证其是否正在运行: ``` curl http://localhost:3110/health # {"status":"ok","server":"enterprise-integrations","version":"1.2.0"} ``` 用于开发并实现自动重启: ``` npm run dev ``` ## 配置 所有配置均通过环境变量进行。服务器使用安全的默认值运行,不需要配置文件。 | 变量 | 默认值 | 描述 | |---|---|---| | `PORT` | `3110` | Express 服务器绑定的 TCP 端口 | | `SERVER_NAME` | `enterprise-integrations` | 握手期间发送给客户端的 MCP `serverInfo.name` | | `STORE_BACKEND` | `sqlite` | 日志存储后端。在非持久化实验室运行时使用 `memory` | | `SQLITE_PATH` | `./data/mcp-decoy.db` | 当 `STORE_BACKEND=sqlite` 时的 SQLite 数据库路径 | | `LOG_RETENTION_DAYS` | `90` | SQLite 保留窗口(以天为单位)。较旧的记录在启动时会被清理,也可以通过编程方式清理 | | `LOG_MAX_SIZE` | `10000` | 保留的最大日志记录数。对于 SQLite,这会在每次插入后限制记录数;对于内存,这会限制内存环形缓冲区 | | `DASHBOARD_TOKEN` | _(未设置)_ | 用于 `/api/*` 和仪表盘数据访问的可选 bearer token。MCP 诱饵 endpoint 保持未认证状态 | | `SYSLOG_HOST` | _(未设置)_ | Syslog 目标主机名或 IP。未设置时 syslog 转发为 **禁用** 状态 | | `SYSLOG_PORT` | `514` | Syslog 目标端口 | | `SYSLOG_PROTOCOL` | `udp` | 传输方式:`udp` 或 `tcp` | | `SYSLOG_FACILITY` | `16` | RFC 5424 facility 代码(16 = local0) | | `SYSLOG_SEVERITY` | `5` | 原始访问事件的 RFC 5424 severity 代码(5 = notice) | | `SYSLOG_DETECTIONS` | `true` | 当设置了 `SYSLOG_HOST` 时,将生成的检测作为单独的 RFC 5424 syslog 事件转发。设置为 `false` 可仅转发原始日志 | | `SYSLOG_APP_NAME` | `mcp-decoy` | syslog 消息中的 APP-NAME 字段 | 示例 — 启用到本地收集器的 syslog 转发: ``` PORT=8080 \ SERVER_NAME=enterprise-integrations \ SYSLOG_HOST=10.0.1.5 \ SYSLOG_PORT=514 \ SYSLOG_PROTOCOL=udp \ node index.js ``` ## MCP 协议支持 该服务器通过两种传输方式实现了 MCP 规范 `2024-11-05`。 ### 可流式传输的 HTTP 传输 (`POST /mcp`) 基于 HTTP 的标准 JSON-RPC 2.0。发送 `Accept: text/event-stream` 的客户端会收到 SSE 封装的响应;其他客户端则收到普通的 JSON 响应。 **握手:** ``` # 初始化 curl -s -X POST http://localhost:3110/mcp \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","clientInfo":{"name":"test","version":"1.0"},"capabilities":{}}}' # 列出工具 curl -s -X POST http://localhost:3110/mcp \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' # 调用工具 curl -s -X POST http://localhost:3110/mcp \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"confluence_search","arguments":{"cql":"type=page AND space=ENG"}}}' ``` ### SSE 传输 (`GET /sse` + `POST /messages`) 适用于需要持久 SSE 连接的客户端(例如旧版 MCP SDK)。 ``` # 1. 打开 SSE 连接 — 注意响应中的 session endpoint curl -N http://localhost:3110/sse # event: endpoint # data: /messages?sessionId= # 2. 通过 session 发送 RPC(在单独的终端中) curl -s -X POST "http://localhost:3110/messages?sessionId=" \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' ``` ### 服务器发现 ``` curl http://localhost:3110/.well-known/mcp ``` ## 支持的工具 ### Bitbucket (3 个工具) | 工具 | 描述 | |---|---| | `bitbucket_search_repositories` | 按名称/描述/元数据搜索工作区 | | `bitbucket_search_code` | 跨代码库的全文本代码搜索 | | `bitbucket_search_artifacts` | 搜索并检索 pipeline 构建产物 | ### Cassandra (3 个工具) | 工具 | 描述 | |---|---| | `cassandra_list_keyspaces` | 列出带有复制配置的 keyspaces | | `cassandra_execute_select_query` | 执行 CQL SELECT 查询 | | `cassandra_server_info` | 集群名称、版本、数据中心、节点 | ### Elasticsearch (3 个工具) | 工具 | 描述 | |---|---| | `elasticsearch_list_indices` | 列出索引的健康状况、文档数量和大小 | | `elasticsearch_search_logs` | 使用查询字符串搜索日志索引 | | `elasticsearch_cluster_info` | 集群名称、状态、节点数、版本 | ### PostgreSQL (3 个工具) | 工具 | 描述 | |---|---| | `postgresql_list_databases` | 列出数据库的所有者和大小 | | `postgresql_execute_select_query` | 执行 SQL SELECT 查询 | | `postgresql_server_info` | 服务器版本、当前 DB、设置快照 | ### Confluence (2 个工具) | 工具 | 描述 | |---|---| | `confluence_get_page` | 按标题检索页面(返回 body HTML) | | `confluence_search` | 返回页面标题和摘要的 CQL 查询 | ### GitHub (4 个工具) | 工具 | 描述 | |---|---| | `github_search_repositories` | 根据主题、可见性和 star 搜索代码库 | | `github_search_code` | 包含文件路径和文本匹配的代码搜索 | | `github_list_commits` | 列出所有者/代码库/分支的提交 | | `github_get_pull_request_comments` | 包含文件/行引用的 PR 审查评论 | ### GitLab (4 个工具) | 工具 | 描述 | |---|---| | `gitlab_search_repositories` | 包含 Web URL 和可见性的项目搜索 | | `gitlab_search_code` | 限定在特定项目内的代码搜索 | | `gitlab_list_commits` | 针对项目 ID 和 ref 的提交列表 | | `gitlab_get_pull_request_comments` | 包含作者和线程类型的合并请求评论 | ### Google Workspace (5 个工具) | 工具 | 描述 | |---|---| | `google_search_drive_files` | 跨 Drive 文件的全文本搜索 | | `google_sheets_read` | 按文件名读取电子表格单元格的值 | | `google_docs_read` | 按文件名读取文档正文 | | `google_chat_search_message` | 跨空间搜索 Chat 消息 | | `google_slides_get_presentation` | 检索演示文稿的幻灯片和元素 | ### Jenkins (2 个工具) | 工具 | 描述 | |---|---| | `jenkins_searchbuildlog` | 按作业名称和模式搜索构建日志 | | `jenkins_getjobscm` | SCM 配置:repo URL、凭证 ID、分支规格 | ### Jira (2 个工具) | 工具 | 描述 | |---|---| | `jira_search_issues` | 返回带有字段和分页问题的 JQL 查询 | | `jira_get_issue` | 按 key(如 `SEC-412`)获取完整的问题详情 | ### Slack (3 个工具) | 工具 | 描述 | |---|---| | `slack_get_user_info` | 通过 Slack ID 或用户名获取用户个人资料 | | `slack_conversations_search_messages` | 跨频道的消息搜索 | | `slack_channels_list` | 列出频道的成员数和隐私标志 | ### Salesforce (4 个工具) | 工具 | 描述 | |---|---| | `salesforce_query_soql` | 针对标准对象执行 SOQL 查询 | | `salesforce_list_reports` | 列出报表库及其文件夹和最后运行日期 | | `salesforce_get_report` | 按名称获取完整的报表数据 | | `salesforce_get_account` | 包含联系人、商机、案例的客户详情 | ## 仪表盘 取证仪表盘是由 `dashboard/` 提供的 Vue 3 SPA。 **开发模式**(热重载,将 API 代理到 3110 端口): ``` cd dashboard npm install npm run dev # Vite 启动于 http://localhost:5173 ``` **生产构建**(由 Express 服务器在 `/` 提供): ``` cd dashboard npm run build # 输出写入到 dashboard/dist/ # 然后只需:node index.js(将 dist/ 作为静态文件提供服务) ``` **仪表盘显示内容:** - 总请求数、唯一 IP、过去一小时内的请求数 — 通过 `/api/events` 实时更新 - 时间线图表:过去 60 分钟内每分钟的请求数 - 调用次数最多的工具(柱状图) - 最常见的源 IP(柱状图) - MCP 方法细分(initialize / tools/list / tools/call) - 最近的检测面板,包含严重程度、规则 ID、源 IP、置信度和摘要 - 当 `DASHBOARD_TOKEN` 保护仪表盘/API 时,可选的 token 提示 - 分页且可过滤的访问日志表 — 按 IP、工具、MCP 方法或时间范围过滤 ## 安全与部署说明 MCP 诱饵被专门设计为欺骗性 endpoint。请将其视为暴露的传感器,而不是受信任的生产集成。 - **不要**使用真实的凭证进行配置,也不要将其连接到生产数据存储。所有工具响应应保持为虚假/诱饵数据。 - 除非您有意让诱饵可从其他网段访问,否则请将其绑定到 localhost。对于 Docker,在本地运行时推荐使用 `-p 127.0.0.1:3110:3110`。 - 在将仪表盘/API 暴露到 localhost 之外之前,请设置 `DASHBOARD_TOKEN`。这会通过 bearer token `Authorization` 标头保护 `/api/*` 数据访问;MCP 诱饵 endpoint(`/mcp`、`/sse`、`/messages`、`/.well-known/mcp`)保持未认证状态,以便客户端仍可与传感器交互。 - 对于 Internet 或共享网络暴露,仍请将服务置于受信任的反向代理、VPN、防火墙规则或实验室网络边界之后。`DASHBOARD_TOKEN` 是一个轻量级的访问门控,而不是企业 SSO。 - `X-Forwarded-For` 用于源 IP 归因。仅当服务位于您控制的代理之后时,才应信任该字段。 - 日志默认存储在 SQLite 中,并具有可配置的保留期。如果需要集中取证,请转发到 syslog/SIEM。 - 在共享或客户环境中部署欺骗系统之前,请查阅当地法律、内部政策和同意要求。 ## SQLite 持久化 默认情况下,MCP 诱饵将日志保存在本地 SQLite 数据库中,保留期为 90 天。要进行显式的持久化本地设置: ``` STORE_BACKEND=sqlite \ SQLITE_PATH=./data/mcp-decoy.db \ LOG_RETENTION_DAYS=90 \ npm start ``` SQLite 模式会自动创建数据库目录,存储完整的事件 JSON,并为时间、IP、工具和 MCP 方法查询保留索引。它还会将安全检测持久化到 `detections` 表中,并为时间、规则 ID、严重程度和源 IP 建立索引。保留期默认为 **90 天**,并在启动时应用;`LOG_MAX` 仍会在每次插入后限制保留的最大日志行数。 ## 检测规则 MCP 诱饵会将选定的 MCP 活动转化为去重的安全发现。检测存储在 SQLite 中,包含在 `/api/stats` 中,由 `/api/detections` 返回,并作为 `detection` 事件通过 `/api/events` 流式传输到仪表盘。 当前的确定性规则: | 规则 ID | 严重程度 | 置信度 | 触发条件 | |---|---:|---:|---| | `MCP_TOOL_ENUMERATION` | medium | high | 客户端调用 `tools/list` | | `MCP_MULTI_TOOL_RECON` | high | high | 同一源 IP 在 5 分钟内调用 3 个或以上不同的工具 | | `MCP_UNKNOWN_TOOL_PROBE` | medium | medium | 客户端调用了诱饵未导出的工具名称 | | `MCP_SECRET_HUNTING_ARGS` | high | medium/high | 工具参数包含敏感狩猎词汇,例如 `.env`、`password`、`secret`、`token`、`api_key` 或 `credential` | | `MCP_DATASTORE_RECON` | high | high | 客户端调用 PostgreSQL、Cassandra 或 Elasticsearch 诱饵工具 | | `MCP_SOURCE_CODE_RECON` | medium | high | 客户端调用 GitHub、GitLab、Bitbucket 或 Jenkins 源代码/DevOps 诱饵工具 | | `MCP_IDENTITY_RECON` | medium | high | 客户端调用 Slack 身份/协作诱饵工具 | 检测会根据规则、源 IP、主题工具/方法和 5 分钟的时间桶进行去重,以减少告警垃圾信息。请将检测视为分诊信号:在做出事件响应决策之前,请结合 EDR、代理、身份提供者和 SIEM 日志对源主机/用户进行关联分析。 ## Syslog 集成 设置了 `SYSLOG_HOST` 后,每个记录的访问事件都会作为 RFC 5424 消息转发,其中包含带有 `id`、`ip`、`mcp_method` 和 `tool` 的结构化数据元素。默认情况下,生成的检测会作为单独的 RFC 5424 消息转发;设置 `SYSLOG_DETECTIONS=false` 可在保留原始访问日志的同时禁止转发检测。 **原始访问消息格式:** ``` <133>1 2026-04-22T14:30:00.000Z hostname mcp-decoy 1234 tools/call [id="" ip="10.0.1.42" mcp_method="tools/call" tool="confluence_search"] MCP tool call: confluence_search from 10.0.1.42 ``` PRI 值 `133` = facility 16 (local0) × 8 + severity 5 (notice)。 **检测消息格式:** ``` <131>1 2026-04-22T14:30:01.000Z hostname mcp-decoy 1234 detection [mcp-detection detection_id="" rule_id="MCP_DATASTORE_RECON" severity="high" confidence="high" source_ip="10.0.1.42" tool="postgresql_list_databases" mcp_method="tools/call" evidence_count="1"] MCP detection: MCP_DATASTORE_RECON high from 10.0.1.42 ``` 检测的 syslog severity 是根据检测的严重程度映射的,而不是 `SYSLOG_SEVERITY`: - `critical` → RFC severity 2 / critical - `high` → RFC severity 3 / error - `medium` → RFC severity 4 / warning - `low` → RFC severity 5 / notice 使用默认的 local0 facility,高严重程度的检测使用 PRI `131` = 16 × 8 + 3。 ### Splunk (Universal Forwarder 或 HEC) **通过 UDP syslog 输入:** ``` SYSLOG_HOST=splunk-indexer.corp.internal \ SYSLOG_PORT=514 \ SYSLOG_PROTOCOL=udp \ node index.js ``` 在 Splunk 中配置端口 514 上的 UDP 输入(`Settings → Data Inputs → UDP`),sourcetype 为 `syslog`。 **推荐的原始活动搜索:** ``` index=main sourcetype=syslog app="mcp-decoy" NOT msgid="detection" | rex field=_raw "\[id=\"(?P[^\"]+)\" ip=\"(?P[^\"]+)\" mcp_method=\"(?P[^\"]+)\" tool=\"(?P[^\"]+)\"\]" | stats count by src_ip, tool | sort -count ``` **推荐的检测搜索:** ``` index=main sourcetype=syslog app="mcp-decoy" " mcp-decoy " " detection " | rex field=_raw "rule_id=\"(?P[^\"]+)\" severity=\"(?P[^\"]+)\" confidence=\"(?P[^\"]+)\" source_ip=\"(?P[^\"]+)\" tool=\"(?P[^\"]+)\".*evidence_count=\"(?P[^\"]+)\"" | stats count by severity, rule_id, confidence, src_ip, tool | sort -count ``` ### QRadar 通过 UDP syslog 转发到配置为 `Syslog` 类型的 QRadar 日志源。结构化数据字段将出现在原始事件中。针对原始活动字段(`tool`、`ip`)和检测字段(`rule_id`、`severity`、`confidence`、`source_ip`、`detection_id`、`evidence_count`)创建自定义 DSM 属性提取。 ``` SYSLOG_HOST=qradar.corp.internal \ SYSLOG_PORT=514 \ SYSLOG_PROTOCOL=udp \ node index.js ``` ### syslog-ng ``` source s_mcp_decoy { network( ip("0.0.0.0") port(514) transport("udp") ); }; destination d_mcp_decoy { file("/var/log/mcp-decoy/access.log" template("${ISODATE} ${HOST} ${MSG}\n") ); }; filter f_mcp_decoy { program("mcp-decoy"); }; log { source(s_mcp_decoy); filter(f_mcp_decoy); destination(d_mcp_decoy); }; ``` ### Graylog 在端口 514 上创建 UDP GELF 或 Syslog 输入。在 message 字段上配置提取器以解析结构化数据的键值对: ``` Raw activity Grok: \[id="%{DATA:mcp_id}" ip="%{IP:src_ip}" mcp_method="%{DATA:mcp_method}" tool="%{DATA:tool}"\] Detection Grok: \[mcp-detection detection_id="%{DATA:detection_id}" rule_id="%{DATA:rule_id}" severity="%{DATA:severity}" confidence="%{DATA:confidence}" source_ip="%{IP:src_ip}" tool="%{DATA:tool}" mcp_method="%{DATA:mcp_method}" evidence_count="%{NUMBER:evidence_count}"\] ``` **TCP 模式**(用于可靠交付给 Graylog): ``` SYSLOG_HOST=graylog.corp.internal \ SYSLOG_PORT=514 \ SYSLOG_PROTOCOL=tcp \ node index.js ``` TCP 传输维护持久的连接,并在重连期间缓冲消息。 ## 测试 ``` # 运行所有测试(141 个测试) npm test # Watch 模式 npm run test:watch # 覆盖率报告(V8 provider) npm run test:coverage ``` 测试位于 `test/` 目录中,使用 Vitest 4 和 Supertest: | 文件 | 范围 | 数量 | |---|---|---| | `test/tools.test.js` | 单元测试 — 所有 38 个工具分发器、schema 验证、虚假数据形态 | ~70 | | `test/server.test.js` | 集成测试 — HTTP endpoint、MCP 协议握手、两种传输方式、可选的仪表盘/API 认证、检测转发 | ~55 | | `test/syslog.test.js` | 单元测试 — RFC 5424 原始/检测消息格式化、severity 映射、检测转发配置 | 5 | | `test/detections.test.js` | 单元测试 — 确定性检测规则、敏感狩猎词汇、多工具侦察 | 9 | | `test/store.test.js` | 单元测试 — LogStore 后端、查询过滤器、统计数据、时间线、检测持久化 | ~30 | ## 部署 ### 已发布的容器镜像 发布镜像已发布到 GitHub Container Registry: ``` ghcr.io/gweber/mcp-decoy:1.2.0 ghcr.io/gweber/mcp-decoy:latest ``` 使用 SQLite 持久化和仪表盘/API token 认证运行发布镜像: ``` DASHBOARD_TOKEN=$(openssl rand -hex 32) docker run --rm \ -p 127.0.0.1:3110:3110 \ -e DASHBOARD_TOKEN="$DASHBOARD_TOKEN" \ -e STORE_BACKEND=sqlite \ -e SQLITE_PATH=/data/mcp-decoy.db \ -v mcp-decoy-data:/data \ ghcr.io/gweber/mcp-decoy:1.2.0 ``` Compose 镜像示例: ``` services: mcp-decoy: image: ghcr.io/gweber/mcp-decoy:1.2.0 ports: - "127.0.0.1:3110:3110" environment: DASHBOARD_TOKEN: "${DASHBOARD_TOKEN:-}" STORE_BACKEND: sqlite SQLITE_PATH: /data/mcp-decoy.db volumes: - mcp-decoy-data:/data volumes: mcp-decoy-data: ``` ### Docker Compose 该代码库包含一个面向生产的 `Dockerfile` 和 `compose.yaml`。Docker 镜像构建 Vue 仪表盘并由 Express 服务器提供静态仪表盘服务;无需单独的仪表盘容器。对于本地运行,请将发布的端口绑定到回环地址。 ``` DASHBOARD_TOKEN=$(openssl rand -hex 32) DASHBOARD_TOKEN="$DASHBOARD_TOKEN" docker compose up --build -d curl http://localhost:3110/health ``` 直接使用 Docker 的示例: ``` DASHBOARD_TOKEN=$(openssl rand -hex 32) docker run --rm \ -p 127.0.0.1:3110:3110 \ -e DASHBOARD_TOKEN="$DASHBOARD_TOKEN" \ -e STORE_BACKEND=sqlite \ -e SQLITE_PATH=/data/mcp-decoy.db \ -v mcp-decoy-data:/data \ ghcr.io/gweber/mcp-decoy:1.2.0 ``` 可以通过 shell 或 `.env` 文件提供有用的环境变量: ``` DASHBOARD_TOKEN=$(openssl rand -hex 32) \ SYSLOG_HOST=splunk-indexer.corp.internal \ SYSLOG_PORT=514 \ SYSLOG_PROTOCOL=udp \ SYSLOG_DETECTIONS=true \ docker compose up --build -d ``` ## 取证用途 ### 日志结构 存储在日志中的每个访问事件都包含以下字段: | 字段 | 描述 | |---|---| | `id` | UUID — 事件的唯一标识符,也用作 syslog MSGID | | `time` | ISO 8601 时间戳 | | `ip` | 源 IP(对于代理部署,会遵循 `X-Forwarded-For`) | | `method` | HTTP 方法 | | `path` | HTTP 路径 | | `ua` | `User-Agent` 标头 | | `mcp_method` | MCP JSON-RPC 方法(如 `initialize`、`tools/list`、`tools/call` 等) | | `tool` | 工具名称 — 仅在 `tools/call` 事件中出现 | | `args` | 客户端提供的工具参数 — 仅在 `tools/call` 中出现 | | `client` | 来自 `initialize` 握手的 MCP `clientInfo` 对象 | ### 查询 API 如果设置了 `DASHBOARD_TOKEN`,请在 API 调用中包含 bearer token: ``` curl -H "Authorization: Bearer YOUR_DASHBOARD_TOKEN" 'http://localhost:3110/api/stats' ``` 下面的未认证示例假设未设置 `DASHBOARD_TOKEN`。 ``` # 所有日志,分页显示 curl 'http://localhost:3110/api/logs?limit=50&offset=0' # 按 source IP 筛选 curl 'http://localhost:3110/api/logs?ip=10.0.1.42' # 按工具筛选 curl 'http://localhost:3110/api/logs?tool=confluence_search' # 按 MCP method 筛选 curl 'http://localhost:3110/api/logs?mcp_method=tools/call' # 按时间范围(ISO 8601)筛选 curl 'http://localhost:3110/api/logs?from=2026-04-22T00:00:00Z&to=2026-04-22T23:59:59Z' # 聚合统计 curl 'http://localhost:3110/api/stats' # 检测,分页显示 curl 'http://localhost:3110/api/detections?limit=50&offset=0' # 筛选检测 curl 'http://localhost:3110/api/detections?severity=high&rule_id=MCP_DATASTORE_RECON' # 时间线(每分钟请求数,最近 60 分钟) curl 'http://localhost:3110/api/timeline?minutes=60' ``` ### 解析攻击者行为 **阶段 1 — 侦察** 攻击者通常会从 `initialize` 开始,紧接着进行 `tools/list`。这是枚举服务器暴露内容最廉价的方式。单个 IP 仅调用一次 `tools/list` 而没有其他操作,对于扫描器来说是正常的;如果同一个 IP 继续进行 `tools/call`,则表明正在进行主动利用。 **高信号工具调用** 以下工具调用表明是定向的数据窃取尝试,而不是随意的侦察: - `confluence_search` 或 `confluence_get_page`,其查询包含 `credentials`、`password`、`secret`、`api_key` 或 `runbook` - `github_search_code` / `gitlab_search_code` / `bitbucket_search_code`,其查询包含环境变量名、token 或 `.env` - `jenkins_getjobscm` — 检索用于 pipeline SCM 配置的凭证 ID - `postgresql_execute_select_query` 或 `cassandra_execute_select_query`,带有 `SELECT *` 或针对用户/会话表的查询 - `slack_get_user_info` 或 `slack_conversations_search_messages` — 通常用于建立联系人映射或查找聊天中共享的凭证 - `salesforce_get_account`,带有已知的客户名称 — 表明 CRM 数据窃取 **需要关联的行为模式** | 模式 | 解释 | |---|---| | 单个 IP,仅 `tools/list` | 自动化扫描器/探测 | | 单个 IP,跨服务进行顺序工具调用(Jira → GitHub → Confluence) | 有条理的人类攻击者或正在进行横向侦察的 agent | | 多个 IP,相同的工具,在短时间窗口内具有相似的参数 | 协同攻击或共享工具 | | 在 PostgreSQL 查询中定向使用 `mfa_enabled: false` | 攻击者利用返回的虚假数据来指导下一步操作 | | `clientInfo` 命名为真实的 MCP 客户端软件(例如 `claude-desktop`、`cursor`) | 证实了被劫持或路由错误的 LLM 客户端会话 | **与 syslog 关联** `id` 字段在内存日志和 syslog MSGID 之间共享。使用它在您的 SIEM 和仪表盘之间关联事件。内存日志中的 `args` 字段(未转发到 syslog)包含完整的工具参数 — 可用于准确了解攻击者正在寻找什么数据。 ## 许可证 MIT
标签:BOF, Express, GNU通用公共许可证, MCP, MITM代理, Node.js, StruQ, 威胁情报, 安全, 密码管理, 开发者工具, 数据泄露, 欺骗防御, 自定义脚本, 蜜罐, 证书利用, 超时处理