Skip to main content
Glama

pentest-recorder · pentest-mcp

あなたのターミナルは、エンゲージメントの最良の記録です。これを1つにまとめます。

CI Python MCP local-first licence: MIT

アセスメント開始から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.sh

Python 3.11+、tmux、FTS5 付き SQLite が必要です。最後のものはインストーラーがチェックします。一部のディストリビューションの Python では省略されているためです。Docker もデータベースサーバーもブラウザ UI も不要です。サードパーティのパッケージは合計3つ: pytehttpxmcp

更新

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必要になるものをカバーしています。


キャプチャするもの — そして意図的にキャプチャしないもの

ネストされたセッションの内部も見えます。 捕獲したリバースシェル、sshevil-winrmmsfconsolesqlplus。そこには多くの良い証拠があり、シェル履歴ツールが到達できないまさにその場所です:

$ 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

それを生成したコマンドは、実際に既知の場合にのみ記録されます。そのリバースシェル内では、commandnc -lvnp 4444 — 正直に — であり、exit_code は推測ではなく空のままです。

指示したときに停止します:

方法

エージェントウィンドウ

デフォルトで除外 — エージェント自身の出力が証拠として再入力されてはならない

1つのペイン

tmux set -p @pentest-record off

1つのウィンドウ

tmux set -w @pentest-record off

すべて、即座に

pentest-recorder pause

すでにパイプしているペイン

検出され、そのまま放置され、報告される

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 value

9つの形式のコーパスで測定: ルールパスは 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 自体もオプションです — これらはテキストファイルであり、catgrepgit で問題なく動作します。

# 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.tomlpentest-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つのルールがあります:

クラス

テーブル

ルール

不変

segments, pane_instances

追記のみ; 決して書き換えない

追記のみ

observations

置き換えられ、編集されない

派生

entities, relationships, auth_attempts

観察から削除され再構築される

状態保持

findings, tasks, notes

あなたのもの; 派生は決して触れない

知っておく価値のある5つの結果:

モデルの失敗はコストがかかりません。 tmux はペイン出力をプレーンファイルに書き込み、レコーダーはチェックポイントされたバイトオフセットからそれを追跡します。キャプチャパスでは、このプロセスが実行されていることに依存するものはありません。レコーダーが死んでも → バイトはディスクに書き込まれ続け、再起動時に取得されます。プロバイダーがダウンしても → セグメントは保留のままです。

抽出されたすべてのリテラルはソースに対してチェックされます。 逐語的に現れない値は保持されますがフラグが付けられ、MCP を通じて unverified として表面化されます。HolyMoly123! が静かに HolyMoly123 になるのは、このツールが防ぐために存在するまさにその失敗です — そして単純な部分文字列マッチングではそれを検出できません。切り詰めは真実の部分文字列だからです。

マージは創発的です。 エンティティは ID キー上の連結コンポーネントです。火曜日に FILE01172.16.119.10 であることを学ぶと、月曜日の2つの別々のレコードは次の再構築で1つになります — 書き換えもトゥームストーンもありません。エンティティ ID はコンテンツアドレス指定されるため、すべての再構築を生き延びます。

誤ったマージは修正可能で元に戻せます。 エージェントに伝えてください。派生が尊重する分離を記録します — 推移的にアサートされたリンクも含みます。

reprocess --model <better> は安全です。 新しい観察が追加され、置き換えられることはなく、発見事項とタスクは触れられません。今は安価にローカルでキャプチャし、後で抽出を改善します。


測定された、仮定ではない

ビルド中に3つの設計上の質問が測定によって解決されました。 bench/ のスクリプトで再現できます。

検索インデックス — 調整された単語トークナイザーではなく、トライグラム。 実際のツール出力に対する20の代表的なクエリ:

インデックス

一致

unicode61 + pentest tokenchars

5 / 20

trigram

20 / 20

tokenchars172.16.119.10 をまとめて保持するのに十分広げると、INLANEFREIGHT\fiona:Summer2026! も単一のトークンに接着されるため、パスワードだけを検索しても何も見つかりません。

キャプチャ量 — 正規化はノイズを除去するが、量は減らさない。

ワークロード

正規化

削減率

進行行の自己書き換え

247,637

347

714×

全画面アプリケーション

7,731

209

37×

大規模スクロールダンプ(find

178,022

173,977

4,000行のfindはすべて実コンテンツであり、全体をそのまま通過する。だからこそ、ローカルファーストの抽出は単なる慎重さではなく、重要な役割を担っている。

モデル — qwen2.5-coder:7b シークレット、ハッシュ、アドレスがバイト単位で同一に返るかどうかで評価:

モデル

リテラル再現率

種類再現率

認証結果エラー

qwen2.5-coder:7b

83%

90%

0

llama3.2:3b

39%

60%

2

3Bモデルは両方の認証フィクスチャに対して何も返さず、成功と失敗を2回誤った。これはこのシステムが静かに犯してはならない唯一のエラーである。


MCPツール

読み取り12、書き込み3。ツール数の肥大化はエージェントが適切なツールを選ぶ能力を低下させるため、get_engagement_summaryのカウントは、それぞれにツールが必要となるヒントを担っている。

読み取り

get_engagement_summary

次に何を尋ねるかを示すカウント — ここから始める

list_entities · get_entity

ホスト、サービス、アイデンティティ、資格情報、共有、アーティファクト

search · get_segment

正確な文字列を検索し、その出典を読む

get_provenance

あらゆる事実をセグメント、ペイン、タイムスタンプ、バイト範囲に追跡

get_recent_activity

何が起こったか、新しい順

list_auth_attempts

どこで何が成功し、何が失敗したか

list_findings · get_finding · list_tasks

list_engagements

書き込み

record_observation

メモ、仮説、ターミナル外で見た事実、修正

update_finding · update_task

確認、拒否、却下

書き込みは常にオペレーターまたはエージェントに帰属し、抽出に帰属することは決してない。呼び出し側がそう主張することはできない。書き込みパスがセグメントを変更することはできない。


開発

python -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest tests/ -q
.venv/bin/ruff check src tests bench

CIは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.

使用し、フォークし、出荷してください。保証はありませんが、ここではそれが通常以上に重要です。このツールは平文の資格情報とクライアントデータをディスクに保存します。実際のエンゲージメントに使用する前に、データの保存場所データがマシンを離れるとき をお読みください。

Install Server
A
license - permissive license
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

  • F
    license
    Not graded
    quality
    C
    maintenance
    An 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
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP 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.
    6
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP 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.
    1
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Records 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.
    4
    6
    MIT

View all related MCP servers

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.

View all MCP Connectors

Latest Blog Posts

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