Вы открываете веб-сайт, а он начинает ходить в Google, AWS, какие-то трекеры и неизвестные хосты, скрипты которые загружают другие скрипты… Хотите узнать — что именно происходит? Это можно выяснить за 5 минут, с помощью PowerShell и Chrome DevTools.
Вот об этом мы и поговорим в этой заметке.
Как это использовать?
Начнем с конца. Посмотрим, как пользоваться функционалом pscdp и PSGraph.
Предположим, мы хотим посмотреть, как устроен тот или иной веб-сайт, вернее куда он ходит при загрузке. Для этого нужно, сначала, запустить Chrome или Chromium в headless режиме, и включить ему все необходимые опции, чтобы он стал ожидать подключений по CDP:
Про CDP мы говорили в прошлый раз
chromium --headless --disable-gpu --remote-debugging-port=9222 --remote-allow-origins=ws://127.0.0.1:92225
Теперь можно загружать модули pscdp и PSGraph и начинать эксперименты.
Соберем данные, для чего запустим наш мини-краулер:
import-module "PSQuickGraph"
import-module "pscdp"
$res = Start-Crawling -Url "https://azazello.darkcity.dev" -MaxDegreeOfParallelism 10
Опция MaxDegreeOfParallelism ограничивает количество вкладок в броузере, то есть, одновременно обрабатываемых страниц.
Команда подключается к браузеру на порту 9222, открывает страницу, извлекает ссылки с этой страницы и обходит эти ссылки - каждую в своей вкладке. Она ожидает загрузки страницы некоторое время, затем обрабатывает, закрывает и процесс повторяется. При этом, для каждой открываемой страницы она сохраняет ссылки, которые на ней нашла, а так же записывает все сетевые события, о которых браузер сообщает нам по CDP по мере загрузки и исполнения скриптов на странице. В результате такого обхода мы получаем все страницы сайта, до которых смогли добраться следуя по ссылкам, и сетевые события, сгенерированные браузером.
Кроме этого в объекте, который вернется в результате исполнения команды, содержится граф, подходящий для визуализации связей между страницами.
PS /tmp> $res | fl
Graph : PSGraph.Model.PsBidirectionalGraph
CapturedEvents : {ProcessedUrlData { page = CrawlTarget { Url = https://azazello.darkcity.dev/azure-networking-d3js, Depth = 0 }, events = System.Collections.Generic.List`1[BaristaLabs.ChromeDevTools.Runtime.IEvent], links =
System.Collections.Generic.List`1[System.String] }, ProcessedUrlData { page = CrawlTarget { Url = https://azazello.darkcity.dev/graphs-windows-firewall, Depth = 0 }, events =
System.Collections.Generic.List`1[BaristaLabs.ChromeDevTools.Runtime.IEvent], links = System.Collections.Generic.List`1[System.String] }, ProcessedUrlData { page = CrawlTarget { Url = https://azazello.darkcity.dev/nmap-smtp-user-enum, Depth
= 0 }, events = System.Collections.Generic.List`1[BaristaLabs.ChromeDevTools.Runtime.IEvent], links = System.Collections.Generic.List`1[System.String] }, ProcessedUrlData { page = CrawlTarget { Url =
https://azazello.darkcity.dev/powershell-graph-sysmon, Depth = 0 }, events = System.Collections.Generic.List`1[BaristaLabs.ChromeDevTools.Runtime.IEvent], links = System.Collections.Generic.List`1[System.String] }…}
Мы можем визуализировать этот граф при помощи PSGraph. При этом генерируется html файл с force-directed визуализацией, основанной на Vega.
Export-Graph -Graph $res.Graph -Format Vega_ForceDirected -Path "/tmp/force.html"
Выглядит результат примерно вот так
Серенькие вершины - события, cиние - вершины нулевого уровня, сам веб-сайт, ну и оранжевые - ссылки, которые мы нашли на страницах, но еще не ходили по ним. Цвета нод меняются, в зависимости от глубины, на которую вы хотите погрузиться при обходе ссылок. По-умолчанию команда пытается обойти весь сайт внутри корневого домена. Не стоит задавать слишком большую глубину, вершин будет просто огромное количество. Дальше первого уровня я никогда не ходил ;).
События показывают, какие сетевые запросы генерирует веб-сайт. Как видим на графе, сайт содержит не так уж и много внешних ссылок. Событий же значительно больше. Это загружаемые JavaScript модули, картинки и другие внешние зависимости.
Все это можно так же увидеть и в табличном виде. Например такая команда вернет список посещенных страниц
$res.CapturedEvents.Page
а вот такая, даст количество событий и ссылок по каждой странице. Тут мы используем синтаксис с “вычисляемыми полями”
$res.CapturedEvents | Select-Object @{l="pageUrl"; e = {$_.Page.url} }, @{l="eventsCount"; e = {$_.events.count}}, @{l="linksCount"; e={$_.links.count}}
PowerShell как инструмент анализа данных
Давайте теперь сделаем еще одну хитрую штуку. Предыдущая команда считает все ссылки на странице, но они могут вести как внутрь сайта, так и наружу. Внутренние ссылки нам не очень интересны. Давайте воспользуемся силой PowerShell и посчитаем только те, что ведут наружу. Исключительно ради спортивного интереса. Например вот так
$res.CapturedEvents |
Select-Object @{l="pageUrl"; e = {$_.Page.url} },
@{l="eventsCount"; e = {$_.events.count}},
@{l="externalLinks"; e={ ($_.links | ? { $_ -notlike "https://azazello.darkcity.dev/*" }).count }},
@{l="totalLinks"; e={ ($_.links).count }}
Тут мы добавляем вычисляемую колонку, которая при рассчете отфильтровывает ссылки, начинающиеся с https://azazello.darkcity.dev/, с нашего корневого домена. Ну и потом возвращает их количество. Получается примерно вот так
Куда же сайт еще ходит? Это можно легко увидеть следующей командой. После небольшой обработки и удаления дубликатов получается примерно вот так
$res.CapturedEvents.events.Request
Как видим, внешних ссылок тоже не много. В основном все внутренние.
Таким образом мы получили список доменов, к которым ходит сайт. Осталось только найти их адреса, и добавить в firewall. :)
Хочется большего? Добавьте geo-IP разрешение и узнайте, в какие страны уходит ваш трафик. Или скормите список доменов whois-анализатору и найдите неожиданных владельцев. Но про это - в следующий раз.