TCP SYN — передача пакета по сети с подробным разбором
Что представляет собой 3 way handshake:
- Устройство, которое желает установить связь отправляет SYN пакет (если рассматривать запрос к странице сайта, то соединение инициируется клиентом)
- В ответ сервер отправляет SYN/ACK, что означает готовность к соединению
- Клиент получает SYN/ACK и отвечает ACK
Таким образом, происходит установка соединения. Далее возможна передача данных.
Рассмотрим как работают сетевые устройства, при на первом шаге. С любым пакетом процесс аналогичен и будет повторяться. Предполагается, что отправитель и получатель находятся в разных сетях на удалении друг от друга.
Первая задача клиентского устройства — сформировать пакет SYN: порт отправителя(случайный), порт назначения, IP адрес назначения. Это TCP/IP и третий уровень модели OSI. В дальнейшем пакет не меняется, к нему только добавляются дополнительные заголовки.
Поскольку адрес назначения не из локальной сети (так и будет если сервер находится в интернете) — пакеты должны уходить на роутер, куда их отправит коммутатор, установленный в офисной сети или у провайдера.
Компьютер-иницатор соединения делает ARP запрос и узнает MAC роутера. ARP — широковещательный запрос, который коммутатор в локальной сети рассылает на все порты кроме того с которого пришел запрос. Отвечает только роутер поскольку в своей таблице маршрутизации видит сеть назначения.
Роутер за коммутатором
MAC во фрейме роутер ставит в качестве получателя, свой MAC в качестве отправителя. Получается дополнительный заголовок второго уровня OSI.
Формируется CRS (или frame check sequence FCS) — хэш пакета md5, добаляет хэш в начало пакета.
Роутер в сети
Первый роутер на пути проверит CRS и сравнит хэш до отправки и полученный им при приеме. Обработка продолжится только при совпадении значений. Так реализована защита от ошибок и перехвата трафика.
Затем распаковывает пакет и видит свой MAC как получателя, отбрасывает заголовок второго уровня.
Роутер проверяет IP адрес назначения, смотрит таблицу роутинга и отправляет пакет на следующий хоп (если нужно предварительно делает ARP, формирует новый заголовок 2 уровня со своим MAC и MAC следующего хопа). Вновь формируется и добавляется CRS.
Следующий хоп повторяет то же, так пока IP адрес получателя не совпадет с тем, который находится в «зоне ответственности» роутера.
Конечный хост также проверяет CRS, только он распаковывает пакет 3 уровня, который сформирован изначально и выясняет на какой порт требуется доставить и кто является отправителем.
Формируется SYN-ACK и отправляется обратно таким же образом. По этому принципу передаются по TCP/IP сети общего доступа любые данные.
Про разграничение сетей на локальные и сети общего доступа — приватные и публичные адреса.
Особенности получения пакетов через raw socket в Linux
Linux (в отличии, к примеру, от FreeBSD) позволяет использовать сырые сокеты не только для отправки, но и для получения данных. В этом месте существуют интересные грабли, на которые я наступил. Теперь спешу показать их тем, кто еще на знает, чтобы каждый, используя свой любимый язык программирования, будь то C++ или Python, мог опробовать их в деле.
Суть граблей изображена на рисунке, чтобы те, кто уже в курсе, не тратили свое время.
Я буду писать примеры на С, но вы можете перенести их и на другие языки, предоставляющие возможность низкоуровневой работы со стеком TCP\IP в Linux.
Некоторые понятия
Напомню, что для инициализации сырого сокета мы передаем параметр, обозначающий тип протокола. Например UDP:
socket(AF_INET, SOCK_RAW, IPPROTO_UDP)
Этот протокол я буду называть уровнем на котором работает сырой сокет. В примере мы создали сырой сокет на уровне UDP.
Уровень сырого сокета не ограничивает вас в формировании пакета на отправку, вы можете самостоятельно сформировать как UDP, так и IP заголовок. А вот при получении данных начинается самое интересное…
Грабли
Допустим мы создали 2 сырых сокета на уровне UDP и воспользовались одним из них для отправки UDP пакета на UDP эхо сервер. Эхо вернет нам UDP payload обратно. Так вот, Стек TCP\IP скопирует полученный пакет на все сырые сокеты того уровня, который указан в поле Protocol IP заголовка пришедшего пакета. Еще раз повторюсь — на ВСЕ, даже те, которые открыты в других приложениях (по этой причине приложение, оперирующее сырыми сокетами может быть запущено только с правами суперпользователя). Так как UDP эхо сервер отвечает UDP пакетом, то все сырые сокеты UDP уровня его получат.
Отметим еще одну важную особенность. Не зависимо от уровня сырого сокета, ему доставляется полный пакет, включающий IP заголовки.
Таким образом, каждый сырой сокет в Linux является сниффером на том уровне, на котором он был создан. Следует помнить об этом при разработке приложений.
Пример
Не стал нагружать заметку кодом. Для тех, кому интересно попробовать, я
выложил свой пример на github. Там cmake проект, который собирает простенький UDP эхо-сервер и приложение, создающее 2 сырых сокета уровня UDP, один из которых посылает данные, но оба отправляются в epoll в ожидании ответа. Для чистоты эксперимента эхо-сервер и пример желательно пустить на разных машинах (не забудьте поправить код в соответствии вашим IP-шникам). Для интереса можно запустить несколько экземпляров примера.
Для внешкольного чтения есть хорошая статья.
Packet crafting как он есть
Создание пакетов или packet crafting — это техника, которая позволяет сетевым инженерам или пентестерам исследовать сети, проверять правила фаерволлов и находить уязвимые места.
Делается это обычно вручную, отправляя пакеты на различные устройства в сети.
В качестве цели может быть брандмауэр, системы обнаружения вторжений (IDS), маршрутизаторы и любые другие участники сети.
Создание пакетов вручную не означает, что нужно писать код на каком-либо высокоуровневом языке программирования, можно воспользоваться готовым инструментом, например, Scapy.
Scapy — это один из лучших, если не самый лучший, инструмент для создания пакетов вручную.
Утилита написана с использованием языка Python, автором является Philippe Biondi.
Возможности утилиты практически безграничны — это и сборка пакетов с последующей отправкой их в сеть, и захват пакетов, и чтение их из сохраненного ранее дампа, и исследование сети, и многое другое.
Всё это можно делать как в интерактивном режиме, так и создавая скрипты.
С помощью Scapy можно проводить сканирование, трассировку, исследования, атаки и обнаружение хостов в сети.
Scapy предоставляет среду или даже фреймворк, чем-то похожий на Wireshark, только без красивой графической оболочки.
Утилита разрабатывается под UNIX-подобные операционные системы, но тем не менее, некоторым удается запустить ее и в среде Windows.
Эта утилита так же может взаимодействовать и с другими программами: для наглядного декодирования пакетов можно подключать тот же Wireshark, для рисования графиков — GnuPlot и Vpython.
Для работы потребуется права суперпользователя (root, UID 0), так как это достаточно низкоуровневая утилита и работает напрямую с сетевой картой.
И что важно, для работы с этой утилитой не потребуются глубокие знания программирования на Python.
Приступаем
Официальный сайт проекта — www.secdev.org/projects/scapy
Установку можно провести разными способами, например apt-get install python-scapy, в случае дистрибутивов на основе Debian.
Так же можно просто скачать свежую версию с сайта разработчиков:
# cd /tmp
# wget scapy.net
# unzip scapy-latest.zip
# cd scapy-2.*
# sudo python setup.py install
После этого запуск происходит непосредственно командой scapy.
На экране отобразится примерно так:
Мы видим стандартное приглашение для ввода, все действия будут выполняться в интерактивном режиме.
Выход происходит комбинацией Ctrl+D, либо набрав функцию exit().
Изучаем инструмент
На самом деле Scapy сильно отличается от привычных утилит. Он работает в текстовом режиме, но любое взаимодействие осуществляется не через привычные ключи и параметры командной строки, а через интерпретатор Python’а.
Такой подход вначале может показаться несколько неудобным и непривычным, но со временем и с практикой приходит понимание того, что это было правильным решением, и что это действительно удобно.
Вначале посмотрим на поддерживаемые протоколы, для этого вызовем функцию ls().
Вывалится более 300 разнообразных протоколов, с которыми можно работать, включая прикладные вроде HTTP, транспортные TCP и UDP, сетевого уровня IPv4 и IPv6 и канального уровня Ether (Ethernet).
Важно обращать внимание на регистр: большинство протоколов пишутся в Scapy с заглавными буквами.
Для того чтобы подробно рассмотреть поля определенного протокола, можно вызвать функцую ls() с указанием протокола: ls(TCP)
В результате будут выведены все поля, которые можно модифицировать в процессе создания пакетов. В скобках показаны значения, которые используются по умолчанию, можно заметить, что порт отправителя 20 (это ftp-data) и порт получателя – 80 (это естественно HTTP), так же установлен флаг SYN (flags = 2).
К примеру, если рассмотреть канальный уровень (Ethernet), то тут возможностей будет поменьше:
В дополнение к функции ls(), есть полезная функция lsc(), которая выведет практически весь основной функционал Scapy:
Для того чтобы получить более подробную информацию о каждой функции, можно использовать help(имя_функции), например:
Видим нечто похожее на MAN страницу в Unix системах.
Для выхода можно использовать опять же привычную в Linux клавишу Q.
Мы посмотрели на протоколы и функции, теперь можно перейти к делу — к созданию пакетов.
Крафтим
Можно создавать сразу пакеты высоких уровней (сетевого и прикладного), и Scapy автоматически дополнит низлежащие уровни, а можно вручную собирать, начиная с канального уровня.
Разделяются уровни модели OSI символом прямого слэша (/).
Нужно обратить внимание на то, что Scapy читает пакет от нижнего уровня слева, до более высокого справа. Поначалу это может немного сбивать с толку, но после небольшой практики всё станет вполне привычно.
К слову, в терминологии Scapy сетевой пакет разделяется на слои, и каждый слой представляется как экземпляр объекта.
Собранный пакет в упрощенном виде может выглядеть как:
Ether()/IP()/TCP()/”App Data”
В большинстве случаев используется только уровень L3, предоставляя Scapy возможность самостоятельно заполнять канальный уровень, на основе информации из ОС.
Меняя значения полей каждого протокола мы меняем стандартные значения (их выводит функция ls()).
Теперь создадим какой-нибудь простой пакет.
Всё очень просто: мы указали адрес назначения, порт и вобщем-то нагрузку в виде слова «TEST».
Сам пакет был незамысловато назван packet, мы увидим очень подробно и развернуто наш свежесозданный пакет:
И теперь, выполнив знакомую уже функцию ls(packet):
Уровни в нем разделяются символами «—«.
Вместо того, чтобы создавать пакет за один раз можно создавать его частями:
В этом примере мы создали переменные под каждый уровень модели OSI.
В качестве имен переменных можно использовать и буквы и цифры, при этом, не забывая о регистре.
И теперь собираем всё в один пакет:
Видно, что результат получится аналогичный.
Углубляемся в пакеты
Мы уже смотрели на вывод функции ls(), но не всегда нужна такая подробная информация о пакете.
Достаточно просто набрать имя переменной и сразу увидеть краткую сводку:
Так же можно использовать метод summary():
Если же нужно чуть больше информации, то есть метод show():
Кроме того, можно просмотреть любое поле, просто указав его:
Разумеется, это работает только в том случае, если такие поля уникальны в пределах пакета.
Если, например, взять поле flags, которое присутствует как в TCP, так и в IP, тут уже нужно конкретизировать, что мы хотим увидеть. В противном случае Scapy выведет значение первого найденного поля (IP flags в нашем примере).
Конкретизация происходит путем указания протокола в квадратных скобках:
К слову, по умолчанию установленные флаги выводятся в цифровом представлении.
Если все управляющие биты будут включены (установлены в 1), то получим значение равное 255. В нашем случае значение 2 говорит о том, что установлен SYN бит.
Но существует возможность отобразить управляющие биты и в символьном отображении:
Как уже говорилось, в любой момент можно достаточно просто поменять значение любого поля:
А в случае, если поле не является уникальным, то нужно указать протокол:
Вторым способом является использование конструкции payload, которая позволяет перепрыгнуть через один уровень (через L3 в нашем случае):
Здесь мы вначале просматриваем вывод слоев над L3, затем просматриваем значение TCP флагов и устанавливаем для них новое значение.
Кстати, можно даже несколько раз вызвать payload, поднимаясь при этом выше и выше:
Можно еще посмотреть на содержимое пакета в шестнадцатеричном виде, для этого есть функция hexdump():
Разбираемся с адресацией
Scapy и в деле указания адреса получателя так же проявляет большую гибкость.
Масса вариантов — здесь и привычная десятичная форма, и доменное имя и CIDR нотация:
В последнем случае пакет будет отправлен на каждый адрес в подсети.
Множество адресов можно задать, просто разделяя их запятой, не забыв про квадратные скобки:
На этом этапе может возникнуть мысль: «А что если нужно задать множество портов?».
И тут Scapy предоставляет широкие возможности, можно указать как диапазон, так и просто перечислить множество:
Обращаю внимание на различие в скобках, в случае диапазона они круглые, а в случае множества – квадратные.
И завершая разговор про указание целей, рассмотрим ситуацию, когда нужно отправить множество пакетов на множество портов.
Для того, чтобы увидеть какие пакеты будут отправлены придется задействовать цикл for, не забываем, что язык программирования у нас Python.
На самом деле ничего сложного, всё очень логично:
Вначале мы уже привычно создаем пакет, в котором указываем подсеть и диапазон портов.
Затем, используя цикл, создаем список, где переменной «а» присваивается каждый элемент структуры пакета. В Python’е отсутствуют массивы в привычном понимании. Вместо них для хранения объектов используются списки.
Мы используем цикл for, для того чтобы «распаковать» всю структуру и отобразить ее в таком наглядном виде.
Отправляем пакеты в путь
С таким же размахом и широтой происходит и отправка пакетов:
- функция send() – отправляет пакеты, используя сетевой (L3) уровень, никак не обрабатывая ответы. Используется принцип — отправили и забыли;
- функция sendp() – отправляет пакеты, используя канальный (L2) уровень, учитываются указанные параметры и заголовки Ethernet кадров. Ответы всё так же не ожидаются и не обрабатываются;
- функция sr() – является аналогичной send(), исключение составляет то, что она уже ожидает ответные пакеты;
- функция srp() – отправляет и принимает пакеты, уровень L2
- функция sr1() – отправляет пакет третьего уровня и забирает только первый ответ, множество ответов не предусматривается;
- функция srp1() – аналогично sr1(), только уже канальный уровень.
Каждую из этих функций можно вызвать и без дополнительных параметров, просто указывая имя переменной, содержащей пакет.
Но вместе с тем существует много дополнительных опций, которые могут быть иногда полезны.
Например, timeout – укажет, сколько времени (в секундах) нужно ждать до получения ответного пакета, retry – сколько раз нужно повторно слать пакет, если ответ не был получен и одна из самых полезных опций – это filter, синтаксис которого очень похож на tcpdump.
В качестве наглядного примера отправим пакет в сеть:
Здесь мы используем функцию, которая после отправки ожидает ответ, устанавливаем таймаут 0.1 секунды и фильтруем ответы, которые подпадают под указанное правило.
Как поступать с ответными пакетами?
Можно взять и назначить переменную, которая и будет содержать ответ:
А смотреть уже привычным способом, просто вызывая переменную response.
Видно, что ответ сохранился в двух вариантах – Results и Unanswered, результаты и без ответа, соответственно.
Указывая смещение, можно вывести только необходимую часть ответа:
Или подробную информацию:
Если же пакет был отправлен в сеть без указания переменной (например, просто функцией sr()), то по умолчанию пакет будет числиться за переменной «_» (символ подчеркивания).
Чтобы достать оттуда эти пакеты, можно использовать конструкцию:
При этом разные результаты сохранятся в двух разных переменных (res и unans).
Более подробный вывод достигается опять же путем указания смещения:
Ловим ответные пакеты
Теперь рассмотрим ситуацию, если пакетов в ответ приходит много.
То, что мы увидели, было по сути, самое что ни есть сканирование портов.
Открытые порты будут с флагами SA (SYN/ACK), например:
Мы смотрим именно на пакет по номеру, счет традиционно начинается нуля.
Можно пойти дальше и распаковать этот результат:
Здесь мы извлекли из результата отправленный пакет (под номером 21) и ответ на него.
Но это только один пакет, а как быть, если нужно обработать все пакеты?
В таком случае придется вновь обращаться к циклу for:
Берем и разбиваем каждый элемент списка res на части a и b. Затем обрезаем часть “a”, заливая это всё в список “allsent”.
Аналогично создаем список allrec, только уже оставляем другую часть.
Всё это, конечно, хорошо, но хотелось бы в более удобном виде получить список открытых и закрытых портов.
Еще раз посмотрим на список res, a точнее на элемент res[0], который состоит из двух частей: пакет, который мы отправили res[0][0], и ответ, который получили res[0][1].
В ответе можно обнаружить три части — заголовок IP (res[0][1][0]), заголовок TCP (res[0][1][1]) и собственно сами данные (res[0][1][2]).
Используем цикл for для извлечения каждого элемента res[N] в переменную «а».
Теперь в переменной «a»содержится результат для каждого пакета. Другими словами «а» представляет собой ans[N].
Нам остается только проверить значения a[1][1], которые будут означать res[N][1][1] в заголовке TCP.
Если быть еще более точным, требуется значение 18, которое означает установленные флаги SYN-ACK.
В тех случаях, когда это условие сработает, мы еще выведем порт отправителя из заголовка TCP:
В итоге, получим результат в виде списка открытых портов.
Все вышеозначенные конструкции набираются за один раз, важно так же уделять внимание отступам (обычно это 4 пробела).
Мы только что вручную написали простой сканер портов, не больше и не меньше.
Сниффер и наоборот
В Scapy входит также и небольшой сниффер, за который отвечает функция sniff().
Естественно, с ним можно использовать фильтры (похожие на фильтры tcpdump), за это отвечает параметр filter, так же можно ограничивать количество пакетов с помощью параметра count.
Как всегда вызов help(sniff) выведет вполне подробную информацию по этой функции.
Не следует забывать, что это сильно упрощенный сниффер, и ожидать от него хорошей скорости особо не приходится.
Стандартная комбинация Ctrl+C прервет процесс захвата трафика и выведет результат.
Как и любая неопределенная переменная, результат попадет в «_».
Выполнив метод summary(), можно увидеть статистику по захваченным пакетам:
Вместо захвата трафика из сети, можно прочитать его из заранее сохраненного дампа (pcap файла).
Кроме того, можно и наоборот, записать пойманные пакеты в файл, используя функцию wrpcap():
И завершая тему сниффинга, можно вызвать Wireshark прямо из интерфейса Scapy, для этого можно использовать одноименную функцию wireshark().
Подробно про Wireshark можно в моей предыдущей статье по адресу http://linkmeup.ru/blog/115.html.
Автоматизация
Казалось бы, что уже всё готово, но не тут то было.
При очередной попытке подгрузить ospf модуль:
>>> load_contrib(‘ospf’), получаем всё ту же ошибу «ERROR: No module named contrib.ospf»
Для того, чтобы модуль окончательно заработал, осталось создать скрипт инициализации (пустой файл):
touch /usr/lib/python2.7/dist-packages/scapy/contrib/__init__.py
После этого, уже можно будет создавать пакеты для OSPF.
Создаем трехэтапное TCP-соединение
Для этого нужно будет поймать SYN/ACK ответ, извлечь из него TCP sequence number, увеличить значение на единицу и, собственно, и поместить полученное значение в поле acknowledgement number.
Непростая задача на первый взгляд, но Scapy может справиться и с ней.
Вначале рассмотрим, что нам нужно, для того чтобы всё прошло успешно.
1) Отправить SYN принимающей стороне:
- собрать заголовок IP, не забыть про адрес отправителя и получателя;
- собрать TCP заголовок, в котором нужно будет указать TCP порты отправителя и назначения, установить TCP флаги (SYN бит) и сгенерировать ISN (Initial Sequence Number).
2) Поймать ответный пакет:
- сохранить ответ;
- извлечь из него TCP sequence number и увеличить это значение на единицу.
3) Создать подтверждение (ACK) на полученный ответный пакет:
- собрать заголовок IP, содержащий такие же адреса отправителя и получателя, как в случае SYN пакета;
- собрать TCP заголовок, с такими же номерами портов, как и в SYN сегменте, но уже установить ACK флаг, увеличить значение ISN на единицу и установить acknowledgement в извлеченный и увеличенный, на втором шаге, sequence number.
Для того чтобы стало еще понятней, рассмотрим уже более подробно, с использованием произвольно взятых значений.
К примеру, соединение прошло таким образом:
192.168.10.200 1024 > 192.168.10.50 80 flags=SYN seq=12345
192.168.10.50 80 > 192.168.10.200 1024 flags=SYN, ACK seq=9998 ack=12346
192.168.10.200 1024 > 192.168.10.50 80 flags=ACK seq=12346 ack=9999
Что в итоге нужно было сделать.
1) Отправить SYN принимающей стороне:
- собрать заголовок IP, в котором указать в качестве отправителя 192.168.10.200 и 192.168.10.50 в качестве получателя;
- собрать TCP заголовок с портом источника (source) 1024 и портом назначения (destination) 80. Так же установить SYN флаг и сгенерировать ISN равный 12345.
2) Поймать ответный пакет:
- сохранить ответ;
- извлечь из него TCP sequence number (9998) и увеличить это значение на единицу, получим 9999.
3) Создать подтверждение (ACK) на полученный ответный пакет:
- собрать заголовок IP, в котором указать в качестве отправителя 192.168.10.200 и 192.168.10.50 в качестве получателя;
- собрать TCP заголовок с такими же портами источника и назначения (1024 и 80 соответственно), установить ACK флаг, увеличить ISN на единицу (12346) и установить acknowledgement в увеличенное значение пойманного seq number (9999).
Начнем собирать пакет:
Здесь уже всё должно быть знакомым: пакет собираем из двух частей, инкапсулируя TCP в IP.
Теперь помня о том, что нам нужно будет перехватить ответ, извлечь оттуда sequence number и увеличить на единицу, делаем:
Происходит следующее – функция sr1 отправляет ранее созданный пакет в сеть, а первый пришедший ответ помещается в переменную SYNACK.
А затем, используя конструкцию SYNACK.seq, извлекаем TCP sequence number, увеличиваем его на единицу и сохраняем в переменной my_ack.
Создаем новый заголовок TCP и называем его ACK. В нем устанавливается другой флаг (A — ACK) и увеличивается значение sequence number.
Кроме того, в качестве acknowledgement указывается переменная my_ack.
Затем собранный пакет выбрасывается в сеть командой send (помним, что это L3 команда, которая даже не слушает, что придет в ответ).
Если всё было сделано правильно, то классическое TCP-соединение состоялось.
Осталось только создать TCP сегмент без каких-либо флагов и тоже отправить в сеть.
Как можно увидеть, мы в очередной раз создали экземпляр TCP заголовка (в этот раз, назвав его PUSH), без флагов и со всеми остальными знакомыми уже значениями.
После этого добавили немного данных, используя переменную data, и отправили в сеть, используя ту же функцию send.
И соответственно от получателя должен будет прийти acknowledgement на этот сегмент.
>>> ip=IP(src=«192.168.10.200»,dst=«192.168.10.50»)
>>> SYN=TCP(sport=1024,dport=80,flags=«S»,seq=12345)
>>> packet=ip/SYN
>>> SYNACK=sr1(packet)
>>> my_ack=SYNACK.seq+1
>>> ACK=TCP(sport=1024,dport=80,flags=«A»,seq=12346,ack=my+ack)
>>>send(ip/ACK)
Но здесь есть и несколько подводных камней.
Если посмотреть на этот обмен в Wireshark, можно увидеть, что до того как ушел наш ACK пакет, внезапно был отправлен RST:
Дело в том, что Scapy работает мимо TCP/IP стека ОС. Это означает то, что ОС не подозревает о том, что Scapy отправляет какие-то пакеты.
Соответственно ОС не будет ожидать появления SYN/ACK пакетов. И, следовательно, соединение будет сразу же сброшено.
Очевидно, что это совсем не тот результат, который нам нужен.
Одним из решений такой проблемы будет использование функционала пакетного фильтра, в частности iptables, который сможет блокировать исходящие RST пакеты.
Например, таким образом:
# iptables -A OUTPUT -p tcp -d 192.168.10.50 -s 192.168.10.200 —dport 80 —tcp-flags RST RST -j DROP
Выполнение такой конструкции приведет к тому, что все исходящие пакеты с адресом назначения 192.168.10.50 и с адресом отправителя 192.168.10.200 на 80-й порт, с установленным RST флагом, будут отбрасываться.
Пакеты будут все так же генерироваться силами ОС, но они просто не будут вылетать за ее пределы.
В итоге уже ничего не будет мешать Scapy делать полноценную TCP-сессию:
Продолжаем исследования
Используя Scapy, можно находить хосты в сети, среди указанного множества адресов:
В этом случае мы используем протокол ICMP и применяем знакомый прием по разделению полученных ответов.
По умолчанию, установлен 8-й тип для ICMP, это и есть классический эхо-запрос.
Углубляясь в тему ИБ, попробуем определить версию ОС используя Scapy и nmap.
Итак, рассмотрим что было сделано.
Вначале был подключен внешний модуль, в данном случае nmap.
Затем проверяем, что у нас есть файл (nmap-os-fingerprints) с отпечатками различных ОС.
И запускаем непосредственно определение удаленной операционной системы, за это отвечает функция nmap_fp, где в качестве параметров помимо самой цели, можно еще указать открытый (oport) и закрытый (cport) порты.
Правильно указанные порты помогут сильно улучшить точность определения ОС.
Визуализируем пакеты
Все время мы смотрели на текстовый вывод, местами была псевдографика, но Scapy умеет и выводить некоторые результаты в графическом виде.
Посмотрим, что нам предлагается.
Самое простое — это метод conversations():
При его выполнении, запустится окно ImageMagick, в котором отрисуется схема нашего обмена пакетами, не ахти красиво, но достаточно наглядно.
Это способ, вероятно, лучше всего подойдет, для визуализации дампов с трафиком.
Второй способ заключается в построении 2D графиков, с последующим экспортом их в pdf-файл.
За это уже отвечает функция pdfdump().
Результаты выглядят примерно так:
В данном случае уже вполне неплохо.
Кроме того, функция graph() опять откроет окно ImageMagick, но уже с детальной прорисовкой:
Здесь мы видим результат трассировки, с подробным отображением автономных систем и прочей визуализацией.
И, завершая тему визуализации, а вместе с ней и статью, посмотрим на 3D отображения трассы.
Для этого потребуется VPython и команда trace3D().
Здесь отображена трасса из предыдущего графика.
Но иногда бывают и такие варианты:
В этом примере была проведена трассировка сразу нескольких целей, с использованием нескольких (80, 443) tcp портов.
Левый клик на любом объекте приведет к появлению IP-адреса над ним, а левый клик с зажатой клавишей CTRL – к отображению более подробной информации — портам, как в этом случае.
Эпилог
Итак, мы рассмотрели лишь малую часть утилиты Scapy, но уже это впечатляет.
Возможности, которые предоставляются действительно очень большие.
Статья призвана вызвать у читателя интерес в изучении сетевых протоколов, и не является исчерпывающим руководством по инструменту Scapy.
За использование этой утилиты в каких-либо противоправных целях автор ответственности не несет.
В процессе написания статьи использовались материалы Института SANS и официальная документация проекта.