pentest-mcp
pentest-recorder · pentest-mcp
あなたのターミナルは、エンゲージメントの最良の記録です。これを1つにまとめます。
アセスメント開始から2時間後、あなたはドメイン資格情報、4つのホスト、読み取り可能な共有フォルダを手に入れていますが、どのペインでそれらを見たのか分かりません。そこでスクロールするか、列挙を再実行するか、手動でノートファイルに貼り付けて、翌日また同じことを繰り返します。
pentest-recorder は既存の tmux セッションを監視し、ターミナルが生成した正確なバイトを保持し、構造化されたエンゲージメント状態に変換します。pentest-mcp はその状態を MCP 互換のエージェントに提供します。これにより、svc_backup のパスワードはどこで入手したのか という質問に対して、セグメント、ペイン、タイムスタンプ、元のバイトを答えることができます。
VM、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.shPython 3.11+、tmux、FTS5 付き SQLite が必要です。最後のものはインストーラーがチェックします。一部のディストリビューションの Python では省略されているためです。Docker もデータベースサーバーもブラウザ UI も不要です。サードパーティのパッケージは合計3つ: pyte、httpx、mcp。
更新
cd pentest-mcp
git pull
./install.shインストーラーを再実行することが更新です。チェックアウトから再構築し、既存の config.toml を保持し、エンゲージメントデータには触れません。古いバージョンで書かれたエンゲージメントがある場合、次にコマンドが書き込み用に開いたときにストアが移行されます — pentest-recorder status はスキーマバージョンを報告し、不一致があればその旨を伝えます。
実行中のレコーダーがある場合は先に停止してください (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() ツールは存在しません — カウントがエージェントにどの質問が価値があるかを伝えるため、12の読み取りツールで、そうでなければ30必要になるものをカバーしています。
キャプチャするもの — そして意図的にキャプチャしないもの
ネストされたセッションの内部も見えます。 捕獲したリバースシェル、ssh、evil-winrm、msfconsole、sqlplus。そこには多くの良い証拠があり、シェル履歴ツールが到達できないまさにその場所です:
$ 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それを生成したコマンドは、実際に既知の場合にのみ記録されます。そのリバースシェル内では、command は nc -lvnp 4444 — 正直に — であり、exit_code は推測ではなく空のままです。
指示したときに停止します:
方法 | |
エージェントウィンドウ | デフォルトで除外 — エージェント自身の出力が証拠として再入力されてはならない |
1つのペイン |
|
1つのウィンドウ |
|
すべて、即座に |
|
すでにパイプしているペイン | 検出され、そのまま放置され、報告される |
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';"別々のエンゲージメントはディスク上でも別々
エンゲージメントごとに1つのディレクトリと1つのデータベースファイル — フィルタ付きの共有テーブルではありません。同じ RFC1918 空間を使用する2つのクライアントは、2つの異なるエンティティセットのままであり、一方で他方のパスワードを検索しても何も返りません。
pentest-recorder init acme --client "ACME" --local-only
pentest-recorder init globex --client "Globex" --local-onlyそれぞれが同じ名前の tmux セッションにバインドされます。2つのレコーダーが1つのペインを対象にする場合、pipe-pane -o は最初のものを置き換えることを拒否し、2番目のものはペインを未監視として報告します。
エンゲージメントの終了時:
pentest-recorder purge -e acme --include-vaultモデルに到達するもの
キャプチャとモデルの間には3つのものがあり、それぞれ [extraction] で独立して無効化できます。
テキストが決定する事実はモデルなしで導出されます。 アドレス、URL、UNC パス、password=X、および認識された形式の資格情報 — NTLMv2、Kerberos チケット、pwdump 行、JWT、PEM キー — はテキストから直接読み取られます。形式がアカウントを運ぶ場合(ほとんどの場合)、所有者は値自体から得られます:
svc_qualys::INLANEFREIGHT:1122334455667788:AB12…:0101…
└────┬────┘ └─────┬─────┘
username domain ← both are part of the value9つの形式のコーパスで測定: ルールパスは 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% — モデルに生の値を表示するよりも優れています。
繰り返しは圧縮され、空のセグメントはスキップされます。 同一の行は1つのインスタンスとカウントになり、すべての異なる値が保持されます。候補もエンゲージメントがまだ知らない値も持たないセグメントは送信されません — 決定は 未知の値 に基づいており、行の形状ではありません。エンゲージメント中はすべての形状が馴染みがあり、connected to \\SQL01\payroll は新しいホストと共有とともにドロップされるからです。スキップされたセグメントは理由を記録し、再処理可能なままです。
これらすべての背後にある数値は bench/BASELINE.md にあります。
データがマシンを離れるとき
抽出には言語モデルが必要であり、リダクションオプションはありません — 秘密を抽出するには秘密を送信する必要があります。
したがって、デフォルトのプロバイダーはローカル (127.0.0.1 上の Ollama) であり、変更しない限り何もマシンから出ません。リモートプロバイダーを設定する場合、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 に投影されます — ダッシュボード、ホスト、資格情報、発見事項、タイムライン、ホストごとのノート、ウィキリンクで相互リンクされます。プラグインは不要で、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 |ボールトは投影であり、ストアではありません: 削除して再エクスポートしても何も失われません。生成されたファイルには generated_by: pentest-recorder フロントマターが含まれ、エクスポーターはそのマーカーがないファイルを決して上書きしません — 代わりに横に書き込み、通知します。あなた自身のノートは Notes/ にあり、触れられることはありません。
include_secrets はデフォルトで include です。正確な資格情報追跡が目的全体だからです。ボールトが同期される場所が望ましくない場合は 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 = ""オプションのシェル統合
外側のシェルからの正確なコマンド、作業ディレクトリ、終了コードをセグメントに追加します:
source /path/to/pentest-mcp/shell/pentest-recorder.shキャプチャはこれなしでも完全に機能し、ネストされたセッション内のアクティビティは推測ではなく正しく未帰属のままです。
仕組み
4つのストレージクラスがあり、それぞれに正確に1つのルールがあります:
クラス | テーブル | ルール |
不変 |
| 追記のみ; 決して書き換えない |
追記のみ |
| 置き換えられ、編集されない |
派生 |
| 観察から削除され再構築される |
状態保持 |
| あなたのもの; 派生は決して触れない |
知っておく価値のある5つの結果:
モデルの失敗はコストがかかりません。 tmux はペイン出力をプレーンファイルに書き込み、レコーダーはチェックポイントされたバイトオフセットからそれを追跡します。キャプチャパスでは、このプロセスが実行されていることに依存するものはありません。レコーダーが死んでも → バイトはディスクに書き込まれ続け、再起動時に取得されます。プロバイダーがダウンしても → セグメントは保留のままです。
抽出されたすべてのリテラルはソースに対してチェックされます。 逐語的に現れない値は保持されますがフラグが付けられ、MCP を通じて unverified として表面化されます。HolyMoly123! が静かに HolyMoly123 になるのは、このツールが防ぐために存在するまさにその失敗です — そして単純な部分文字列マッチングではそれを検出できません。切り詰めは真実の部分文字列だからです。
マージは創発的です。 エンティティは ID キー上の連結コンポーネントです。火曜日に FILE01 が 172.16.119.10 であることを学ぶと、月曜日の2つの別々のレコードは次の再構築で1つになります — 書き換えもトゥームストーンもありません。エンティティ ID はコンテンツアドレス指定されるため、すべての再構築を生き延びます。
誤ったマージは修正可能で元に戻せます。 エージェントに伝えてください。派生が尊重する分離を記録します — 推移的にアサートされたリンクも含みます。
reprocess --model <better> は安全です。 新しい観察が追加され、置き換えられることはなく、発見事項とタスクは触れられません。今は安価にローカルでキャプチャし、後で抽出を改善します。
測定された、仮定ではない
ビルド中に3つの設計上の質問が測定によって解決されました。 bench/ のスクリプトで再現できます。
検索インデックス — 調整された単語トークナイザーではなく、トライグラム。 実際のツール出力に対する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モデルは両方の認証フィクスチャに対して何も返さず、成功と失敗を2回誤った。これはこのシステムが静かに犯してはならない唯一のエラーである。
MCPツール
読み取り12、書き込み3。ツール数の肥大化はエージェントが適切なツールを選ぶ能力を低下させるため、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を行い、ライセンスメタデータがそのままの状態でホイールがビルドされることを検証する。
296のテストには、ネストされたリバースシェルを使った実際のtmuxに対する完全な受け入れリハーサル、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/ にあります。
スコープ
意図的に構築しないもの:自律型ペネトレーションテスト、自動エクスプロイト、ターミナルやVMの代替、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