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




一个伪装成合法企业 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, 威胁情报, 安全, 密码管理, 开发者工具, 数据泄露, 欺骗防御, 自定义脚本, 蜜罐, 证书利用, 超时处理