pentest-mcp
pentest-recorder · pentest-mcp
你的终端是你在一次渗透测试中最好的记录。这个工具把它变成一份结构化的记录。
评估进行到两个小时,你手里有一个域凭据、四台主机、一个可以读取的共享目录,却不知道这些是在哪个窗格里看到的。于是你开始滚动屏幕。或者重新跑一遍枚举。或者手动把所有内容粘贴到笔记文件里,然后明天再重复一遍。
pentest-recorder 监视你现有的 tmux 会话,保留终端产生的原始字节,并把它们转化为结构化的评估状态。pentest-mcp 把这份状态交给任何兼容 MCP 的智能体——这样它就能回答我是在哪里拿到 svc_backup 的密码的,并给出对应的片段、窗格、时间戳和原始字节。
你保留自己的虚拟机、tmux、VPN 和跳板、别名、字典。什么都不被包装,什么都不被替换。
tmux panes
│
▼
raw bytes ──────────────────────────────► raw/pane-000003.log
│ authoritative · never parsed
│ terminal emulation
▼
segments ◄── byte-addressable, immutable
│
│ LLM (local by default)
▼
observations ◄── append-only, every fact cites its source
│
│ deterministic rebuild
▼
entities · relationships · auth log
│
├──────────────► Obsidian vault (a projection, not the store)
│
└──────────────► pentest-mcp ────► Claude Code · Codex · any MCP agent这不是一个自主渗透测试智能体。 数据层负责保存事实和记忆;智能体负责推理。记录器从不连接你扫描过的任何目标——这一点由测试强制保证,而不是靠良好意愿。
安装
git clone git@github.com:lucianoengel/pentest-mcp.git
cd pentest-mcp
./install.sh需要 Python 3.11+、tmux 和带 FTS5 的 SQLite——最后一项安装程序会检查,因为某些发行版的 Python 没有包含它。不需要 Docker、不需要数据库服务器、不需要浏览器界面。总共只有三个第三方包:pyte、httpx、mcp。
更新
cd pentest-mcp
git pull
./install.sh重新运行安装程序就是更新。它会从检出目录重新构建,保留你现有的 config.toml,并且不动评估数据。如果某个评估是由旧版本写入的,它的存储会在下次某个命令以写入方式打开它时被迁移——pentest-recorder status 会报告 schema 版本,并在版本不一致时明确说明。
如果记录器正在运行,请先停止它(pentest-recorder stop),因为运行中的守护进程会继续使用它启动时加载的代码。
Related MCP server: mcp-ssh-interactive
使用
pentest-recorder init inlanefreight --client "ACME" --scope "172.16.119.0/24"
tmux new -s inlanefreight
pentest-recorder start然后正常开展工作。该会话中的每个窗格都会被捕获,包括你之后打开的窗格和窗口。
pentest-recorder status # what is — and is NOT — being captured
pentest-recorder search 'Summer2026!' # find the exact string, across everything
pentest-recorder pause # stop capturing, right now
pentest-recorder sync # extract, rebuild, export连接智能体
claude mcp add pentest -- ~/.local/bin/pentest-mcp --engagement inlanefreight现在可以向它提问了。
我有哪些还没验证的凭据?
// list_entities(type="credential", filter={"validated": false})
{
"items": [{
"id": "credential:INLANEFREIGHT/svc_backup:password:HolyMoly123!",
"type": "credential",
"data": {
"username": "svc_backup",
"domain": "INLANEFREIGHT",
"secret": "HolyMoly123!", // exact, never normalized
"secret_type": "password",
"status": "unvalidated"
},
"fact_type": "CONFIRMED",
"observation_count": 1
}],
"total": 1
}那个密码是从哪里来的?
// get_provenance(entity_id="credential:INLANEFREIGHT/svc_backup:...")
{
"observations": [{
"kind": "credential",
"source": "extraction",
"actor": "ollama/qwen2.5-coder:7b",
"verified": true, // appeared verbatim in the source
"segment_ids": [1]
}],
"segments": [{
"id": 1,
"terminal": "inlanefreight:2.1",
"ts_start": "2026-08-26T14:32:11+00:00",
"command": "cat /mnt/backup/scripts/backup.ini",
"cwd": "/home/kali/eng",
"raw_path": ".../raw/pane-000003.log",
"byte_start": 0,
"byte_end": 125 // the original bytes, still on disk
}]
}给我补一下进度
// get_engagement_summary()
hosts: 2 · services: 1 · identities: 1
credentials: 2 (1 unvalidated)
auth: 1 successful, 1 failed
findings: 1 candidate · open tasks: 0
hosts_with_no_service_recorded: 1 ← DC01 is under-enumerated
entities_with_unverified_fields: 0
segments: 1 (1 pending extraction)那份摘要就是智能体的索引。这就是为什么没有 list_unvalidated_credentials() 工具——计数告诉智能体哪些问题值得追问,所以十二个读取工具就覆盖了原本需要三十个工具才能覆盖的范围。
它捕获什么——以及它刻意不捕获什么
它能看见嵌套会话内部。 一个反弹 shell、ssh、evil-winrm、msfconsole、sqlplus。大量有价值的证据就在那里,而这恰恰是 shell 历史工具无法触及的:
$ nc -lvnp 4444 ← the only local command that ever runs
connect to [10.10.14.7] from (UNKNOWN) [172.16.119.30] 51422
C:\inetpub\wwwroot> type web.config
<add name="prod" connectionString="...;Password=P@ssw0rd#2026;" />
↑ captured, extracted, attributed to nothing产生它的命令只有在真正已知时才会被记录。在那个反弹 shell 内部,command 是 nc -lvnp 4444——如实记录——而 exit_code 保持为空而不是猜测。
你让它停它就停:
方式 | |
智能体窗口 | 默认排除——智能体自己的输出绝不能作为证据重新进入 |
单个窗格 |
|
单个窗口 |
|
全部,立即 |
|
你已经在管道的窗格 | 检测到、不干预、报告 |
status 会列出作用域内没有被捕获的每个窗格,并说明原因。一个被静默漏监控的窗格是这个工具可能犯的最严重的错误,所以它永远不会只是脚注:
Engagement: inlanefreight (client: ACME)
Recorder: running (pid 48213) since 2026-08-26T13:58:02+00:00
tmux server: 347338
Capturing: 3 pane(s)
%1 inlanefreight:1:recon.0 (0 B buffered)
%4 inlanefreight:2:ad.0 (2145 B buffered)
%7 inlanefreight:3:shell.0 (0 B buffered)
NOT capturing 2 pane(s) in scope:
%9 inlanefreight:5:agent.0 -- window excluded by configuration
%11 inlanefreight:4:web.0 -- already piped by another tool; left untouched
Segments: 184 total, 3 pending, 181 extracted, 0 failed
Extraction: ollama / qwen2.5-coder:7b
Local provider: no engagement data leaves this machine.你的数据存放在哪里
~/.local/share/pentest-recorder/engagements/inlanefreight/
├── engagement.db one ordinary SQLite file — the canonical store
├── raw/ exact terminal bytes, rotated and gzipped
└── evidence/目录权限 0700,文件权限 0600。原始日志是这台机器上最敏感的东西——明文域凭据和客户数据都在里面。把那个目录当作战利品来对待。
一切都可以用普通工具检查。备份就是 cp -r;审计就是一条 SQL 查询:
sqlite3 engagement.db \
"SELECT json_extract(data,'\$.username'), json_extract(data,'\$.status')
FROM entities WHERE type='credential';"不同的评估在磁盘上是分开的
每个评估一个目录、一个数据库文件——而不是带过滤器的共享表。两个使用相同 RFC1918 地址空间的客户仍然是两套不同的实体集合,在一个评估里搜索另一个的密码不会返回任何结果。
pentest-recorder init acme --client "ACME" --local-only
pentest-recorder init globex --client "Globex" --local-only每个评估绑定到同名的 tmux 会话。如果两个记录器同时指向同一个窗格,pipe-pane -o 会拒绝挤掉第一个,第二个会把该窗格报告为未监控。
评估结束时:
pentest-recorder purge -e acme --include-vault什么会到达模型
在捕获和模型之间有三层处理,每一层都可以在 [extraction] 中独立禁用。
文本能确定的事实不需要模型参与推导。 地址、URL、UNC 路径、password=X,以及可识别格式的凭据——NTLMv2、Kerberos 票据、pwdump 行、JWT、PEM 密钥——直接从文本中读取。凡是格式本身就携带账户信息的(大多数格式都是如此),归属就来自值本身:
svc_qualys::INLANEFREIGHT:1122334455667788:AB12…:0101…
└────┬────┘ └─────┬─────┘
username domain ← both are part of the value针对九种格式的语料进行测量:规则通道9/9 字节级一致恢复,9/9 归属正确;让 qwen2.5-coder:7b 转写同样的值只能做到 6/9 和 5/9。它不会做的是识别那些只因工具输出位置才有意义的值——microsoft-ds 之所以是服务,是因为 nmap 有一个 SERVICE 列,而这仍然是模型的工作。
长令牌在模型看到之前就被替换。 剩下的是一个占位符、它的长度和字符类别,以及所有周围上下文:
[SMB] NTLMv2-SSP Hash : svc_qualys::INLANEFREIGHT:1122334455667788:<Ta1b2c3d4:1>
└──────────── kept, so the model can attribute it ────┘记录器会把真实字节替换回去。转录错误从"需要检测"变成"不可能发生"。保留标识符可见很重要:隐藏整个凭据会把分类准确率降到 44%,保留它们则提高到 78%——比把原始值直接给模型看还要好。
重复内容被折叠,空片段被跳过。 相同的行合并为一个实例加一个计数,每个不同的值都被保留。一个不包含候选值、也不包含评估尚未知晓的值的片段永远不会被发送——判断依据是未知值,而不是行的形状,因为在评估进行中每种形状都很常见,否则 connected to \\SQL01\payroll 会连同新的主机和共享一起被丢弃。被跳过的片段会记录原因,并且可以重新处理。
所有这些背后的数字在 bench/BASELINE.md 中。
数据何时离开机器
提取需要一个语言模型,而且没有脱敏选项——要提取一个秘密,你就得发送那个秘密。
所以默认提供方是本地(Ollama 在 127.0.0.1 上),除非你修改配置,否则没有任何数据离开你的机器。如果你配置了远程提供方,start 会明确告诉你将要传输什么,并在你确认之前拒绝启动:
Extraction is configured to use openai (gpt-4o-mini) at
https://api.openai.com/v1
This sends captured terminal output to that service. In a penetration
test that includes, in full and unredacted:
- plaintext passwords, password hashes, tokens and API keys
- usernames, domains, internal hostnames and IP addresses
- file contents, share names and command output
- vulnerability evidence and client-identifying data
There is no redaction option: extracting a secret requires sending it.对于合同上禁止这样做的客户项目:
pentest-recorder init acme --local-only该评估会永久拒绝远程提供方,无论配置之后怎么说——并且会报告这次拒绝,同时捕获继续运行。
API 密钥从环境中读取,由配置中的 api_key_env 指定名称。密钥永远不会被写入配置文件、评估数据、日志或导出的 Markdown。
Obsidian
把 obsidian.vault_path 指向任意位置,评估就会被投影为普通 Markdown——仪表盘、主机、凭据、发现、时间线,以及每台主机一条笔记,用 wikilink 交叉链接。不需要插件,Obsidian 本身也是可选的——这些都是文本文件;cat、grep 和 git 都能正常使用。
# Credentials
| Identity | Secret | Type | Validated on | Source |
|---|---|---|---|---|
| INLANEFREIGHT\fiona | `Summer2026!` | Password | SMB FILE01 | Segment 1 |
| INLANEFREIGHT\svc_backup| `HolyMoly123!` | Password | Not yet | Segment 1 |这个 vault 是投影,不是存储:删掉它、重新导出,什么都不会丢。生成的文件带有 generated_by: pentest-recorder front matter,导出器绝不会覆盖缺少该标记的文件——它会在旁边写入并告诉你。你自己的笔记放在 Notes/ 中,那里永远不会被触碰。
include_secrets 默认是 include,因为准确的凭据跟踪正是这个工具的全部意义。如果 vault 会同步到你不希望它去的地方,就设置 redact 或 partial;这只会影响投影。
配置
~/.config/pentest-recorder/config.toml——用 pentest-recorder config --write 生成。每个设置都有文档化的默认值,无效值会按名称报告且不应用任何更改。
[data]
root = "~/.local/share/pentest-recorder"
[obsidian]
vault_path = "~/Obsidian/Pentests"
include_secrets = "include" # include | redact | partial
[tmux]
session = "@engagement" # or a glob such as "client-*"
exclude_windows = ["agent"]
[capture]
idle_flush_seconds = 3.0
max_segment_bytes = 65536
rotate_bytes = 134217728
[extraction]
rules = true # derive facts the text determines
redact = true # replace long tokens before prompting
collapse = true # collapse repeated lines
route = true # skip segments carrying nothing new
escalation_model = "" # optional stronger model for hard cases
[llm]
provider = "ollama" # ollama | openai | openai-compatible
model = "qwen2.5-coder:7b"
base_url = "http://127.0.0.1:11434"
api_key_env = "OPENAI_API_KEY" # names the variable, never holds the key
[mcp]
default_engagement = ""可选的 shell 集成
为来自你外层 shell 的片段添加确切的命令、工作目录和退出码:
source /path/to/pentest-mcp/shell/pentest-recorder.sh没有它捕获也能完整工作,嵌套会话内的活动会正确地保持未归属状态,而不是被猜测。
它是如何组织在一起的
四个存储类,每个类恰好一条规则:
类 | 表 | 规则 |
不可变 |
| 只追加;从不重写 |
只追加 |
| 被取代,从不编辑 |
派生 |
| 丢弃并从 observations 重建 |
有状态 |
| 属于你;派生逻辑从不触碰它们 |
有五点值得知道:
模型故障零成本。 tmux 把窗格输出写入普通文件;记录器从检查点字节偏移处 tail 它。捕获路径中没有任何东西依赖这个进程在运行。记录器崩溃 → 字节继续落在磁盘上,重启后会被拾取。提供方宕机 → 片段保持待处理状态。
每个提取出的字面值都会与来源核对。 没有逐字出现的值会被保留但标记出来,并通过 MCP 以 unverified 状态呈现。HolyMoly123! 悄悄变成 HolyMoly123 正是这个工具要防止的失败——而纯子串匹配无法检测到它,因为截断后的结果就是真相的一个子串。
合并是涌现的。 实体是身份键上的连通分量。周二得知 FILE01 就是 172.16.119.10,周一的两条独立记录在下一次重建时就变成一条——不需要重写,不需要墓碑。实体 ID 是内容寻址的,所以它们能经受住每一次重建。
错误的合并可以修复且可逆。 告诉智能体;它会记录一个派生逻辑会遵守的分离——包括对传递性断言的链接。
reprocess --model <better> 是安全的。 新的观察被添加,从不替换,你的 findings 和 tasks 不受影响。现在低成本地本地捕获;以后再用更好的模型改进提取。
用测量说话,而不是假设
构建过程中有三个设计问题是通过测量解决的。用 bench/ 中的脚本可以复现。
搜索索引——用 trigram,而不是调优的词法分词器。 针对真实工具输出的 20 个代表性查询:
索引 | 匹配数 |
| 5 / 20 |
| 20 / 20 |
把 tokenchars 加宽到足以容纳 172.16.119.10 的同时,也会把 INLANEFREIGHT\fiona:Summer2026! 粘成一个令牌,所以单独搜索密码什么都找不到。
捕获量——归一化消除的是噪声,而非体量。
工作负载 | 原始 | 归一化 | 缩减 |
进度行自我重写 | 247,637 | 347 | 714× |
全屏应用 | 7,731 | 209 | 37× |
大型滚动转储( | 178,022 | 173,977 | 1× |
一个 4,000 行的 find 输出全部是真实内容,会整体通过。这正是本地优先提取是承重结构而非仅仅谨慎之举的原因。
模型——qwen2.5-coder:7b。 评分标准是秘密、哈希和地址是否以字节完全相同的方式返回:
模型 | 字面召回 | 类型召回 | 认证结果错误 |
| 83% | 90% | 0 |
| 39% | 60% | 2 |
3B 模型对两个认证测试夹具均未返回任何内容,并且在成功与失败的判断上出错了两次——这是本系统绝不能悄然犯下的错误。
MCP 工具
十二个读取工具,三个写入工具。工具数量膨胀会降低智能体选择正确工具的能力,因此 get_engagement_summary 中的计数承载了本需各用一个工具才能提供的提示。
读取 | |
| 揭示下一步该问什么的计数——从这里开始 |
| 主机、服务、身份、凭据、共享、工件 |
| 查找精确字符串;读取其来源 |
| 将任何事实追溯到其分段、窗格、时间戳和字节范围 |
| 发生了什么,最新的在前 |
| 哪些地方成功了,哪些没有 |
| |
|
写入 | |
| 笔记、假设、终端之外观察到的事实、更正 |
| 确认、拒绝、驳回 |
写入始终归属于操作员或智能体——绝不归属于提取过程,调用方也无法声称其他归属。任何写入路径都不能更改分段。
开发
python -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest tests/ -q
.venv/bin/ruff check src tests benchCI 在 Python 3.11 至 3.14 上运行测试套件,安装 tmux 以便真实 tmux 验收测试真正运行而非自行跳过,执行 lint 检查,并验证 wheel 构建时许可证元数据完整。
296 个测试,包括针对真实 tmux(含嵌套反向 shell)的完整验收演练、通过 stdio 的真实 MCP 客户端、记录器与服务器之间的真实并发访问,以及一项 AST 检查,确保只有模型提供者模块可以访问网络。
tests/test_capture.py capture, segmentation, rotation, pause, restart
tests/test_normalize.py terminal emulation, both implementations
tests/test_extract.py the extraction contract and the verbatim guard
tests/test_derive.py union-find correlation, merges, corrections
tests/test_mcp.py tool surface, bounds, attribution
tests/test_acceptance.py end-to-end rehearsal in real tmux
tests/test_passivity.py proves the recorder never touches a target设计决策、规格及其背后的推理都记录在 openspec/changes/add-pentest-recorder-mcp/ 中。
范围
刻意不构建的内容:自主渗透测试、自动利用、替代终端或虚拟机、Web 仪表盘、图数据库、多用户协作、网络 MCP 传输,或每种工具一个解析器。
记录器负责捕获和组织。MCP 服务器负责暴露。智能体负责推理。三者保持分离。
许可证
MIT — © 2026 Luciano Engel.
使用它、分叉它、发布它。它不附带任何担保,这一点在这里比往常更重要:此工具会在磁盘上存储明文凭据和客户端数据。在将其用于真实项目之前,请阅读你的数据存储在哪里和数据何时离开机器。
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceAn MCP server for EMBA firmware analysis that exposes structured security findings and tools to LLMs. It enables users to programmatically query, reason over, and correlate firmware analysis results such as kernel details, SBOMs, and attack paths.6
- AlicenseNot gradedqualityDmaintenanceMCP server that enables AI agents to run fully interactive SSH sessions (via tmux) and execute commands like a human operator, with persistent sessions and multiple concurrent connections.6MIT
- AlicenseNot gradedqualityDmaintenanceMCP server that provides programmatic access to the SOLVE-IT digital forensics knowledge base, enabling LLMs to query, navigate, and search forensic techniques, weaknesses, mitigations, objectives, and citations.1MIT
- AlicenseAqualityAmaintenanceRecords your terminal sessions per command (PTY + OSC 133) into local SQLite, so AI agents can search, retrieve, and diff what commands actually printed. Secret redaction is applied by default to everything served over MCP.46MIT
Related MCP Connectors
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Agentic search over your Dewey document collections from any MCP-compatible client.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/lucianoengel/pentest-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server