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 и SQLite с FTS5 — последнее установщик проверяет, потому что в некоторых дистрибутивных Python его нет. Никакого Docker, сервера баз данных или браузерного интерфейса. Всего три сторонних пакета: pyte, httpx, mcp.
Обновление
cd pentest-mcp
git pull
./install.shПовторный запуск установщика и есть обновление. Он пересобирает из checkout, сохраняет ваш существующий 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() — счётчики подсказывают агенту, какие вопросы стоит задавать, так что двенадцать инструментов чтения покрывают то, на что иначе потребовалось бы тридцать.
Что он захватывает — и чего намеренно не делает
Он видит внутри вложенных сессий. Пойманный reverse shell, 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Команда, которая породила вывод, записывается только тогда, когда она действительно известна. Внутри этого reverse 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: дашборд, хосты, учётные данные, находки, таймлайн и заметка на каждый хост, перекрёстно связанные вики-ссылками. Никаких плагинов, и сам 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 |Хранилище — это проекция, а не хранилище: удалите его, переэкспортируйте, ничего не потеряете. Сгенерированные файлы несут front matter 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Захват полностью работает и без неё, а активность внутри вложенной сессии остаётся корректно неприписанной, а не угаданной.
Как это держится вместе
Четыре класса хранения, у каждого ровно одно правило:
класс | таблицы | правило |
неизменяемый |
| только добавление; никогда не переписывается |
только добавление |
| заменяется, но не редактируется |
производный |
| удаляется и пересобирается из observations |
с сохранением состояния |
| ваши; деривация их никогда не трогает |
Пять следствий, о которых стоит знать:
Сбой модели ничего не стоит. tmux пишет вывод панели в обычный файл; рекордер читает его с контрольной точки в байтах. Ничто в пути захвата не зависит от того, работает ли этот процесс. Рекордер умирает → байты продолжают ложиться на диск и подхватываются при перезапуске. Провайдер недоступен → сегменты остаются в ожидании.
Каждый извлечённый литерал проверяется по своему источнику. Значение, которое не встречается дословно, сохраняется, но помечается и отображается как unverified через MCP. HolyMoly123! тихо превращающееся в HolyMoly123 — это именно тот сбой, для предотвращения которого существует этот инструмент — и простое сопоставление подстрок не может его обнаружить, поскольку усечение является подстрокой истины.
Слияние возникает само. Сущности — это связные компоненты по ключам идентичности. Узнайте во вторник, что FILE01 — это 172.16.119.10, и две отдельные записи понедельника станут одной при следующей пересборке — без переписывания, без надгробий. Идентификаторы сущностей адресуются по содержимому, поэтому они переживают любую пересборку.
Неверное слияние исправимо и обратимо. Скажите агенту; он запишет разделение, которое деривация учтёт — включая связи, утверждённые транзитивно.
reprocess --model <better> безопасен. Новые наблюдения добавляются, а не заменяются, и ваши находки и задачи не трогаются. Захватывайте дёшево и локально сейчас; улучшайте извлечение позже.
Измерено, а не предположено
Три проектных вопроса были решены измерениями во время разработки. Воспроизведите с помощью скриптов в 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× |
find на 4000 строк — это весь реальный контент, и он проходит целиком. Именно поэтому локальное извлечение — это несущая конструкция, а не просто благоразумное решение.
Модель — 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 действительно запускались, а не пропускали себя, выполняет линтинг и проверяет, что wheel собирается с сохранением метаданных лицензии.
296 тестов, включая полную приемочную репетицию с реальным tmux с вложенной обратной оболочкой, реальным MCP-клиентом через stdio, реальным конкурентным доступом между рекордером и сервером, и 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/.
Область применения
Сознательно не реализовано: автономное пентестирование, автоматическая эксплуатация, замена терминала или виртуальной машины, веб-панель, графовая база данных, многопользовательское сотрудничество, сетевой 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