// Package scanner реализует активные и пассивные проверки на типовые // классы уязвимостей (аналог Scanner в Burp): SQLi, XSS, SSTI, LFI, // RCE (командная инъекция), SSRF, XXE, CORS-мисконфигурация, JWT. // // Активные проверки (Check) переиспользуют ту же инфраструктуру // маркеров §...§ и engine.BuildRequest, что и Intruder — по сути, // Scanner это Intruder с фиксированным набором payload'ов на класс // уязвимости и анализом ответа вместо простого прогона. // // Честная оговорка про качество детекции, важная для трактовки // результатов: // - SQLi/LFI/SSTI/XXE — детект по сигнатурам в теле ответа плюс // time-based эвристика для слепых случаев. Ложноположительные // срабатывания возможны, особенно на time-based (просто медленный // сервер даст такой же эффект) — результаты Scanner это наводки // для ручной проверки, не готовый вердикт. // - SSRF — без out-of-band листенера (как Burp Collaborator) реальный // SSRF в общем случае не подтвердить; здесь только best-effort // проверка через облачные метадата-эндпоинты и file://, которые // иногда отражаются прямо в ответе. // - CORS/JWT — не задействуют Probes()/Analyze(), это отдельные // одноразовые проверки (CheckCORS, DecodeJWT) с другой формой // входных данных, поэтому не реализуют интерфейс Check. package scanner import ( "net/http" "time" ) // Severity — грубая оценка серьёзности находки. Не заменяет CVSS и // подобное — это просто сортировка "на что смотреть в первую очередь". type Severity int const ( SeverityInfo Severity = iota SeverityLow SeverityMedium SeverityHigh ) func (s Severity) String() string { switch s { case SeverityInfo: return "INFO" case SeverityLow: return "LOW" case SeverityMedium: return "MEDIUM" case SeverityHigh: return "HIGH" default: return "?" } } // Finding — одна находка проверки. type Finding struct { Check string Severity Severity Detail string Payload string // что было отправлено (пусто для CORS/JWT — там нет единого "payload") Evidence string // фрагмент ответа/заголовка, подтверждающий находку } // ProbeResult — минимальный набор данных об ответе, нужный проверкам. // Отделён от engine.Result по тому же принципу, что sitemap.Record // отделён от proxy.Entry — scanner не должен тянуть за собой знание // о внутреннем устройстве Intruder-движка сверх того, что реально нужно. type ProbeResult struct { StatusCode int Body string Headers http.Header Duration time.Duration Err error } // Check — активная проверка одного класса уязвимости через подстановку // payload'ов в одну точку вставки (маркер). type Check interface { // Name — человекочитаемое имя, показывается в TUI и в Finding.Check. Name() string // Probes — payload'ы, которые по очереди подставляются в маркер. Probes() []string // Analyze решает, есть ли находка, сравнивая ответ на probe с // baseline (ответ на нейтральное/исходное значение той же точки). // Возвращает nil, если находки нет. Analyze(probe string, resp, baseline ProbeResult) *Finding } // EventKind различает два вида сообщений в потоке Scanner.Run: тик // прогресса (ещё один пробник отправлен) и собственно находку. type EventKind int const ( EventProgress EventKind = iota EventFinding ) // Event — одно сообщение из канала Scanner.Run. Kind говорит, какое из // полей заполнено: Finding (при EventFinding) или Done/Total (при // EventProgress). Объединение прогресса и находок в один канал/тип — // осознанное решение: TUI всё равно должен читать их последовательно // из одного потока, заводить два отдельных канала ради этого не давало // бы ничего, кроме сложности синхронизации между ними. type Event struct { Kind EventKind Finding Finding // валидно при Kind == EventFinding Done int // валидно при Kind == EventProgress — сколько пробников уже отправлено Total int // общее число пробников в этом прогоне (включая baseline) }