Як хакери проводять розвідку перед атакою
Коли я запропонував колегам зробити recon власного сайту, щоб взяти скріни для цієї статті — вони розвели руками. Що там можна знайти, ми його тільки недавно зробили.
За 15 хвилин я знайшов річ, яку потрібно було фіксити в той же вечір — внутрішня docker-адреса нашого бекенду, яка просочилася у мінімізований клієнтський JS. І додатково — піддомен сервісу, який не світиться в публічних DNS-реєстрах, але katana все одно його витягла з нашого ж власного коду. Це не витік, але нагадування: будь-яке посилання у твоєму фронтенді — публічна інформація.
Жодна з цих знахідок не була результатом атаки. Жодної спроби «увійти». Усе — публічно. Усе — з відкритих джерел. Усе — за п’ятнадцять хвилин.
У цій статті я покажу всі команди. Щоб ви могли їх повторити на власному домені й побачити, що видно у вас.
Що таке recon і чим він відрізняється від атаки
Recon (розвідка) — це картографування цілі перед будь-якою атакою. Сам recon нічого не ламає. Він збирає інформацію: які піддомени існують, які з них живі, який стек, які API-endpoint’и, які залежності, які люди в команді.
Цей етап є невід’ємною частиною, оскільки спроба взлому без карти — це дії наосліп. Знаючи, що сайт на застарілому Nuxt, атакуючий йде у CVE-базу. Іноді recon показує щось, що навіть не потребує PoC, наприклад, API-ключ від якогось сервісу.
Розвідка ділиться на категорії:
🟢 Passive recon — запит іде до третьої сторони, не до тебе. crt.sh, subfinder, GitHub-пошук, Wayback Machine — усі вони опитують публічні бази, які про твою компанію вже знають. У твоїх логах не з’явиться жодного байта. Ти ніколи не дізнаєшся, що тебе таким чином дослідили.
🟡 Active recon — запит іде до твого сервера, але виглядає як один з тисячі звичайних відвідувачів. httpx-toolkit робить один GET /. katana завантажує JavaScript-файли так само, як це робить браузер кожного юзера. У логах це не відрізнити від легітимного трафіку.
Я провів цей recon на власну інфраструктуру. Покажу всі п’ять етапів і всі шість інструментів.

Інструмент № 1: crt.sh — публічний реєстр сертифікатів
Кожен раз, коли твій сайт отримує SSL-сертифікат, інформація про це потрапляє у публічні Certificate Transparency логи. Це обов’язково за стандартом — інакше браузер відмовиться довіряти сертифікату. Це гарна штука, бо ти можеш бачити, якщо хтось випустить серт на твій домен (атака). І це водночас гарна штука для атакуючого, бо він бачить всі сертифікати, які колись існували для твоєї компанії.
https://crt.sh/?q=%25.vapan.org
%25 — це URL-encoded %, wildcard. Всі сертифікати для будь-якого піддомена .vapan.org.

Те ж саме через термінал:
curl -s "https://crt.sh/?q=%25.vapan.org&output=json" | jq -r '.[].name_value' | sed 's/*.//g' | tr ',' '\n' | sort -u

Ключове: сертифікати з CT-логів видалити неможливо. Старі логи лежать там назавжди. Якщо у тебе колись був legacy-admin.yourcompany.com з сертифікатом — він і досі видимий, навіть якщо DNS-запис ти видалив п’ять років тому.
Інструмент № 2: subfinder + bbot
crt.sh — це одне джерело. Серйозний recon агрегує з 20+ джерел паралельно. Найшвидший спосіб — subfinder:
subfinder -d vapan.org -all -silent
Цей запит опитує: crt.sh, AlienVault OTX, VirusTotal, GitHub indexed, Shodan, Censys, Wayback, і ще близько 15 джерел. За 5 секунд.
Для контрасту — те ж саме на більших доменах:

Малий сайт — 8 субдоменів, легко переглянути. Великий бренд — двадцять п’ять тисяч. Масштаб різний, принцип один: це публічна інформація, до якої кожен має доступ.
Більш просунутий інструмент — bbot. Це фреймворк з понад 50 модулями: subdomain enumeration, port scan через Shodan, JS-аналіз, email-харвестинг, GitHub-індексація. Замість шести окремих утиліт — одна команда:
bbot -t vapan.org -p subdomain-enum -y


Інструмент № 3: httpx — який з знайдених піддоменів живий
З 8 піддоменів, які знайшов subfinder, реально працюючий — один. Як це перевірити? Інструмент httpx-toolkit:
cat all-subs.txt | httpx-toolkit -silent -title -tech-detect -status-code -follow-redirects -timeout 5

Що видає httpx крім «живий чи ні»:
- Title сторінки
- Tech stack (за заголовками, скриптами, реалізаціями)
- Status code і ланцюжок редіректів
- TLS-інформацію (якщо додати
-tls-grab)
На моєму запиті для vapan.org httpx-toolkit повернув:
- Live, статуси 301 → 200
- Title: «Web Design & Development Services. Build Your Website Today»
- Tech: Cloudflare, Cloudflare Browser Insights, HTTP/3, Node.js, Nuxt.js, Vue.js
Це не криптично, не приховано — це в HTTP-заголовках і у завантажених ресурсах. Будь-хто, хто відкриває твій сайт у браузері з відкритим DevTools, бачить це теж.
Що з цим робить атакуючий: Він тепер знає, які CVE дивитися. Спрямований пошук «Nuxt.js CVE 2025», «Vue.js prototype pollution», «Node 22 vulnerabilities». Він точно знає, що шукати.
Інструмент № 4: katana + LinkFinder — JS-аудит, де я знайшов витоки
Сучасні веб-додатки — це single-page applications (Nuxt, Next, React, Vue, Angular). Це означає: коли ти відкриваєш сайт, твій браузер скачує великий пакет JavaScript-файлів. У цих файлах — вся логіка фронтенду, включно з:
- адресами всіх API-endpoint’ів, на які ходить додаток
- параметрами цих endpoint’ів
- внутрішніми feature flags
- умовами авторизації (UI-рівень)
- іноді — конфіг для зовнішніх сервісів
katana — web crawler від ProjectDiscovery, який краулить сайт як браузер, виконує JavaScript, і тягне ВСІ URL — у тому числі JS-файли:
katana -u https://vapan.org -d 2 -jc -silent | grep -E ".js$" | sort -u > js-files.txt wc -l js-files.txt

65 JavaScript-файлів. Це для невеликого корпоративного сайту. У великих SPA — сотні. Кожен — публічно завантажується кожним відвідувачем.
65 JavaScript-файлів. Це для невеликого корпоративного сайту. У великих SPA — сотні. Кожен — публічно завантажується кожним відвідувачем.
Далі — найцікавіше. Беремо ці файли і просто грепаємо по них патерни:
for js in $(cat js-files.txt); do curl -s --max-time 5 "$js" | grep -oE '"/[a-zA-Z0-9/_-]+"' | sort -u done | sort -u | grep -E '^"/(api|v[0-9]|admin|auth|user|account)"'

І ось — карта мого власного API:
/api/articles /api/articles/ /api/auth/login /api/auth/me /api/circle/stats /api/form/submitFeedback /api/news /api/_nuxt_icon /users/me
Відновлена з фронтенду, без авторизації, без жодного запиту на нестандартний URL. Усі ці endpoint’и працюють, я можу їх вручну спробувати. Не всі з них потребують повного login — і ось де атакуючий починає копати.
Що я знайшов на vapan.org за 15 хвилин
Два конкретні артефакти, які видно зі сторони — без жодного скана:
Знахідка № 1: внутрішня docker-адреса бекенду в клієнтському JS
У мінімізованих JS-файлах фронтенду я знайшов рядок виду backend:8080. Це внутрішнє ім’я Docker-контейнера, по якому фронтенд звертається до бекенду всередині нашої compose-мережі.
Чому це проблема:
- Це підтверджує атакуючому, що сайт стоїть за Docker Compose або Kubernetes з іменованими сервісами. Стек одразу звужується.
- Це підказує структуру внутрішньої мережі: є щось з ім’ям
backend, воно слухає:8080. Якщо знайти SSRF чи незахищений preview-енд — є куди стукати. - Це помилка білда: внутрішнє ім’я не повинно потрапляти у клієнтський бандл. Має бути публічний proxy-шлях (
/api), і збірка повинна обрізати все, що схоже наservice-name:port.
Фікс: замінити прямі звернення на relative-URL (/api/...) і додати lint-правило, яке падає білд при знаходженні патерну [a-z]+:\d{2,5} у бандлі.
Знахідка № 2: піддомен, якого немає в публічних DNS-записах
katana витягла з JS посилання на ***.vapan.org — піддомен, який не з’являється у crt.sh, не індексується пошуком, не світиться у subfinder. На ньому стоїть Telegram WebApp, про який я знаю — це робочий сервіс.
Це не витік. Але це повчально:
- Можна подумати, що про цей піддомен знають лише наша команда і Telegram. Виявилось, що достатньо одного
<a href>у фронтенді — і будь-якийkatana/gau/waybackurlsйого витягне. - Принцип «security through obscurity» (захистились тим, що нічого нікому не казали) не працює. Якщо посилання є у твоєму HTML/JS — піддомен публічний, крапка.
Висновок: для приватних інтерфейсів (admin-панелі, internal-dashboard, staging) обов’язкова аутентифікація на рівні застосунку, а не сподівання, що ніхто не дізнається URL. Можна додатково ховати на рівні Cloudflare Access / IP allowlist — але це повинно бути додатковим шаром, а не єдиним.
Що з цим робити власнику сайту — 5-хвилинний чеклист
- Перевір crt.sh на свій домен. Відкрий
https://crt.sh/?q=%25.вашдомен. Подивись, які піддомени там є. Якщо побачиш забуті dev/staging-домени — переконайся, що вони або взагалі видалені з DNS, або захищені (auth/IP allowlist) і не торкаються продакшен-даних. - GitHub dork по своєму домену. На github.com у пошуку:
"your-domain.com" filename:.env,"your-domain.com" filename:config.js. Якщо знаходить — швидко змінюй усі секрети, які могли там бути. - Перевір власні JS у проді через
katana— і потімgrepпо них наapi/,localhost,:8080,_PRIVATE,secret,token. Зайве 95% знаходиться саме так. - Налаштуй моніторинг нових сертифікатів. crt.sh має RSS-фід для домену — підпишись. Як тільки хтось випустить серт на твій бренд (фішинг або підман-у-середині), ти дізнаєшся першим.
Висновок
Recon — це не магія і не хакерство. Це рутинна процедура картографування, доступна будь-кому з ноутбуком. Шість утиліт, п’ятнадцять хвилин — і ти маєш повну карту: піддомени, стек, API, забуті сертифікати, витоки в JS.
Я зробив це на vapan.org і знайшов дві речі. Ти можеш зробити це на своєму домені просто зараз, тими ж командами з цієї статті, і побачити, що видно у тебе.
Якщо нічого — добре. Якщо щось — добре, що ти знайшов це першим, а не хтось інший.
4 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарівдякую
дяк!
Чудова стаття, дякую!
Дякую!