193 lines
13 KiB
Markdown
193 lines
13 KiB
Markdown
# Архитектура burterm
|
||
|
||
English version: [ARCHITECTURE.md](ARCHITECTURE.md).
|
||
История решений, найденные баги и оговорки по надёжности — в [NOTES.md](NOTES.md).
|
||
|
||
## Обзор
|
||
|
||
burterm — терминальный инструмент для веб-пентеста, bug bounty и CTF: один
|
||
статический Go-бинарник, 13 вкладок TUI и headless CLI-режим на том же
|
||
движке. Сборка без cgo.
|
||
|
||
```
|
||
cmd/burterm/ точка входа: CLI-флаги или запуск TUI
|
||
internal/
|
||
engine/ ядро Intruder: маркеры, атаки, фильтры
|
||
payload/ генераторы payload'ов
|
||
cli/ разбор флагов и payload-спецификаций
|
||
proxy/ MITM-прокси
|
||
spider/ краулер
|
||
sitemap/ scope-правила, дерево сайта
|
||
scanner/ активные/пассивные проверки уязвимостей
|
||
fuzzer/ blackbox-фаззер бинарников
|
||
project/ хранилище сессии (SQLite+FTS5)
|
||
decoder/ comparer/ sequencer/ утилиты
|
||
tui/ интерфейс (bubbletea)
|
||
```
|
||
|
||
## Общие принципы
|
||
|
||
- **Асинхронный I/O через `tea.Cmd`/`tea.Msg`.** TUI построен на архитектуре
|
||
Elm: `Update(msg) (Model, Cmd)`. Любой побочный эффект (сеть, диск,
|
||
таймер) — это `tea.Cmd`, функция без аргументов, возвращающая `tea.Msg`,
|
||
выполняемая фреймворком в отдельной горутине. `Update` никогда не
|
||
блокируется на I/O.
|
||
- **Потоковые операции — через каналы.** Intruder-атаки, Scanner, Spider,
|
||
Fuzzer стримят результаты через `<-chan T`. Общий паттерн: горутина-продюсер
|
||
пишет в канал через `select` с `<-ctx.Done()`; TUI читает через
|
||
`waitForXxx(ch) tea.Cmd`, переиздающий себя после каждого сообщения.
|
||
- **Значения снимаются с модели до ухода `tea.Cmd` в фон** — замыкание не
|
||
держит ссылку на модель, которая может измениться до того, как команда
|
||
выполнится.
|
||
|
||
## internal/engine — ядро Intruder
|
||
|
||
- `§маркеры§` в сыром HTTP-запросе отмечают точки вставки payload'а.
|
||
`ParseMarkers` вычленяет их в `[]InsertionPoint{Start, End}` (байтовые
|
||
смещения в очищенной строке) и возвращает текст без маркеров.
|
||
`InsertBeforeMarker` вставляет произвольный текст перед N-м маркером —
|
||
используется рекурсией (см. ниже).
|
||
- `BuildRequest` подставляет payload'ы и парсит результат в `*http.Request`
|
||
через `http.ReadRequest`.
|
||
- Четыре режима атаки: `RunSniper`, `RunBattering`, `RunPitchfork`,
|
||
`RunClusterBomb` — общий пул воркеров, читающих задания из канала.
|
||
Pitchfork/ClusterBomb используют `drainGenerator`, вычитывающий генератор
|
||
целиком в память перед стартом.
|
||
- `EstimateTotal` считает ожидаемое число запросов по `Len()` генераторов до
|
||
старта атаки — для индикации прогресса в TUI.
|
||
- `ParseStatusFilter`/`ParseLengthFilter`/`ParseWordFilter`/`ParseLineFilter`
|
||
— общий синтаксис (`>N`, `<N`, `N-M`, точное число) через один внутренний
|
||
парсер `parseIntRangeFilter`.
|
||
- `New(cfg)` настраивает `http.Transport` с `MaxIdleConnsPerHost:
|
||
cfg.Concurrency` для переиспользования соединений при конкурентных
|
||
запросах к одному хосту.
|
||
|
||
## internal/payload — генераторы payload'ов
|
||
|
||
Интерфейс `Generator` (`Next`/`Reset`/`Len`). Реализации:
|
||
`WordlistGenerator`, `NumericRangeGenerator`, `BruteForceGenerator`
|
||
(перебор алфавита заданной длины), `ExtensionGenerator` (оборачивает любой
|
||
генератор, добавляя расширения к каждому слову — `spec|ext:.php,.html`).
|
||
`presets.go` — набор готовых заготовок (числа, буквы, частые
|
||
имена/пароли, SQLi/XSS/path-traversal пробники). `smart.go` — эвристика
|
||
подсказки заготовки по имени параметра рядом с маркером.
|
||
|
||
## internal/cli — CLI-флаги
|
||
|
||
`ParsePayloadSpec` разбирает строки вида `wordlist:<путь>` /
|
||
`range:<from>:<to>:<step>` / `preset:<имя>` / `brute:<алфавит>:<min>:<max>`,
|
||
с необязательным суффиксом `|ext:...`. Используется и headless-режимом
|
||
(`cmd/burterm`), и TUI — единая грамматика.
|
||
|
||
## internal/proxy — MITM-прокси
|
||
|
||
- `Server` — форвардный прокси с MITM через собственный CA (`ca.go`,
|
||
генерируется в `~/.burterm` при первом запуске), `CONNECT`-туннель с
|
||
`tls.Server` и динамическим выпуском листовых сертификатов по SNI.
|
||
- Каждая транзакция — `Entry{Raw, StatusCode, Length, Headers,
|
||
ResponseBody, ...}` в потокобезопасном `Store` (`sync.Mutex`,
|
||
подписка на новые записи через канал).
|
||
- `Server.SetScope(*sitemap.Scope)` под `sync.RWMutex` — если задан,
|
||
`capture`/`captureErr` пропускают запись в `Store` для запросов вне
|
||
scope. Сам трафик прокси обслуживает независимо от scope.
|
||
- `session.go`: `ExtractSessionHeaders(entries, host)` вытаскивает
|
||
`Cookie`/`Authorization` из истории для конкретного хоста (парсит их из
|
||
`Entry.Raw`).
|
||
|
||
## internal/spider — краулер
|
||
|
||
`Spider.Crawl` — BFS-обход в границах `scope *url.URL` и `maxDepth`.
|
||
`extractLinksAndForms` парсит HTML, извлекает ссылки и описания форм.
|
||
GET-формы (`Method=="GET"`) автоматически конвертируются в URL с
|
||
заглушками `test` на каждое поле и добавляются в очередь обхода
|
||
(`buildGetFormURL`); POST-формы пропускаются. `SetHeaders` задаёт
|
||
заголовки для каждого запроса обхода — используется для передачи
|
||
сессионных Cookie/Authorization из Proxy.
|
||
|
||
## internal/sitemap — scope и дерево сайта
|
||
|
||
`Scope` — упорядоченный список include/exclude regex-правил
|
||
(`Add`/`RemoveAt`/`Matches`/`Rules`). `Tree`/`Node`/`Endpoint` — дерево
|
||
путей сайта, строится из потока URL через `Build`, экспортируется в JSON.
|
||
|
||
## internal/scanner — проверки уязвимостей
|
||
|
||
- `Check` — интерфейс активной проверки (`Name`/`Probes`/`Analyze`),
|
||
подставляет payload'ы в одну точку вставки через ту же
|
||
`engine.BuildRequest`, что и Intruder. Реализации: SQLi, XSS (reflected,
|
||
с уникальной канарейкой), SSTI, LFI, RCE (time-based), SSRF, XXE.
|
||
- `Scanner.Run` возвращает канал `Event{Kind: EventProgress|EventFinding}`
|
||
— прогресс и находки в одном потоке.
|
||
- `CheckCORS` — одноразовая проверка через заголовок `Origin` (не
|
||
вписывается в интерфейс `Check`, так как не работает через маркер).
|
||
- `DecodeJWT`/`AlgNoneVariant` — разбор JWT и генерация варианта без
|
||
подписи для проверки обхода.
|
||
|
||
## internal/fuzzer — фаззер бинарников
|
||
|
||
Blackbox-мутационный фаззер произвольного исполняемого файла (CTF pwn).
|
||
`Config{Target, Args, UseStdin, Timeout, Workers, CrashDir}`. Мутации
|
||
(`mutate.go`): bit-flip, byte-flip, arithmetic, interesting values, block
|
||
delete/duplicate, splice, `havoc` (комбинация нескольких мутаций за
|
||
проход). Краш определяется по сигналу завершения процесса
|
||
(`SIGSEGV`/`SIGABRT`/`SIGFPE`/`SIGILL`/`SIGBUS`) через
|
||
`syscall.WaitStatus`. `Run` возвращает канал `Event{Kind:
|
||
EventProgress|EventCrash}`, каждый воркер — независимая горутина со своим
|
||
`rand.Source`.
|
||
|
||
## internal/project — хранилище сессии
|
||
|
||
SQLite (`modernc.org/sqlite`, без cgo) с FTS5-индексом в режиме external
|
||
content (текст хранится один раз в `requests`, `requests_fts` держит
|
||
только инвертированный индекс), синхронизация через триггеры
|
||
`INSERT`/`UPDATE`/`DELETE`. `Store.Open` ставит `db.SetMaxOpenConns(1)`.
|
||
`ParseQuery` — мини-язык поиска (`host:… method:… status:… color:… текст`)
|
||
— известные `key:value`-токены уходят в структурированный SQL, остальное
|
||
— в FTS5 `MATCH`. `SaveMeta`/`LoadMeta` — произвольные key-value записи
|
||
(используется для scope-правил Target). `SaveLastPath`/`LoadLastPath` —
|
||
путь последнего открытого проекта хранится вне самой БД
|
||
(`~/.burterm/last-project`).
|
||
|
||
## internal/decoder, comparer, sequencer
|
||
|
||
`decoder` — кодеки URL/Base64/HTML/Hex/MD5 и цепочка автоопределения.
|
||
`comparer` — токенный LCS-diff. `sequencer` — статистические тесты FIPS
|
||
140-2 (monobit/poker/runs/long-run) на 20000-битных блоках.
|
||
|
||
## internal/tui — интерфейс
|
||
|
||
- `Model` (`model.go`) — корневая модель, поле `active view` определяет
|
||
видимую вкладку. `Update` делегирует необработанные сообщения активной
|
||
вкладке.
|
||
- Каждая вкладка — своя `xxxModel` в отдельном файле
|
||
(`requestbuilder.go`, `results.go`, `proxyview.go`, `repeater.go`,
|
||
`spiderview.go`, `targetview.go`, `decoderview.go`, `comparerview.go`,
|
||
`sequencerview.go`, `dashboardview.go`, `scannerview.go`,
|
||
`projectview.go`, `fuzzerview.go`), с методами
|
||
`Update(tea.Msg) (xxxModel, tea.Cmd)` и `View() string`.
|
||
- `theme.go` — палитра, `panel()` (рамка с заголовком через
|
||
`lipgloss.RoundedBorder`), `renderTabs()` (таб-бар с заливкой активной
|
||
вкладки), `renderLogo()`.
|
||
- `results.go` — кэширует скомпилированные предикаты фильтров
|
||
(`recompileFilters`), копит отфильтрованный вывод в `strings.Builder`
|
||
инкрементально (`AddResult`), рендерит во `viewport` дросселированно по
|
||
таймеру `resultsTickCmd` (раз в 300мс), только для видимой атаки.
|
||
- `attacks []*attackRun` — список независимых параллельных атак, каждая
|
||
со своим `id`/каналом/`cancel`/`resultsModel`.
|
||
- `recursionMeta` — параметры авто-рекурсии Intruder в найденные
|
||
директории: шаблон запроса, точка вставки, спецификации payload'ов,
|
||
глубина, предикат статусов.
|
||
- Сообщения именуются по паттерну `xxxStartedMsg`/`xxxResultMsg`/
|
||
`xxxDoneMsg`/`xxxErrMsg` для каждой длительной операции.
|
||
|
||
## Хранение на диске
|
||
|
||
Всё под `~/.burterm/`: `ca.pem`/`ca-key.pem` (MITM CA), `last-project`
|
||
(путь последнего проекта), `fuzz-crashes/<unix>/` (крашащие вводы
|
||
фаззера), `sitemap-<unix>.json` (экспорт карты сайта).
|
||
|
||
## Режимы запуска
|
||
|
||
TUI (без аргументов) и headless CLI (`-request`/`-payload`/`-mode` и
|
||
т.д., см. `internal/cli/flags.go`) используют один и тот же
|
||
`internal/engine` — сценарии CI/скриптов не дублируют логику атаки.
|