Главная

Wednesday, 20 May 2020

Основы конструирования сетевых пакетов в Scapy.

Всем привет.

Сегодня будет сетевая вкусняшка по имени Scapy. Несмотря на то что оригинал статьи был опубликован в журнале "Системный администратор" №10 (107) еще в далеком 2011-м году Бражуком Андреем сама Scapy популярности не утратила.

Его статья является введением в многофункциональный расширяемый генератор сетевых пакетов Scapy, работающий в интерпретируемой среде языка программирования Python.

Сфера применения генераторов сетевых пакетов достаточно обширна: они могут быть полезны как средства первичной диагностики сети, при решении нестандартных задач автоматизации мониторинга серверов и сервисов, тестирования и анализа защищенности. Хороший генератор можно с успехом использовать для наглядной демонстрации при изучении сетевых взаимодействий, а также при создании и отладке новых протоколов. В общем, подобный инструмент востребован программистом и системным администратором, специалистом по информационной безопасности и просто энтузиастом, интересующимся сетями.

Примером многофункционального расширяемого генератора пакетов и является рассматриваемая в статье утилита Scapy. По сути, Scapy предназначена для конструирования, отправки, перехвата и анализа сетевых пакетов, а также для манипулирования единицами передачи протоколов транспортного уровня (UDP-датаграммами и TCP-сегментами) и сообщениями прикладных протоколов [1].

Средой выполнения данной утилиты выступает интерпретатор языка программирования Python, что позволяет использовать ее как в интерактивном режиме, так и в виде модуля (подключаемой библиотеки) в скриптах, написанных на Python. Фактически, с помощью Scapy можно воспроизвести функции любой сетевой утилиты, начиная от простейших ICMP-команд типа ping и traceroute и заканчивая реализациями сложных алгоритмов поиска уязвимостей безопасности [2]. При чем, по заверениям автора этого открытого проекта, то, для чего на языке программирования C потребуется написать сто строк кода, в Scapy делается буквально двумя строками.

Немного дегтя

Прежде чем, как говорится, с головой погружаться в захватывающий мир возможностей Scapy, следует учесть несколько моментов.

Во-первых, эта утилита наиболее полезна для решения нестандартных проблем, требующих дополнительной обработки полученных результатов, т. е. когда от инструмента требуется гибкость. Не надо забывать, что для типичных задач диагностики создано множество средств с куда более классическим интерфейсом, например, таких как сканер портов Nmap или утилита перехвата сетевого трафика tcpdump и т. д.

Во-вторых, Scapy неразлучна с языком программирования Python. И для всего, что сложнее, чем примеры, описанные в руководстве пользователя, желательно хотя бы понимание базовых конструкции и подходов, характерных для Python. Правда, с позиций человека, имеющего некоторое представлении о процессе программирования, этот язык выглядит ни чуть не сложнее других, и при наличии желания и хорошего руководства [3] вполне «постигаем». Лишь забавный дискомфорт (субъективно) вызывает замена ставших привычными разделителей синтаксических конструкций (всевозможных скобок, ключевых слов и т. д.) на отступы от начала строки.

В-третьих, если имеет место личная несовместимость с Python, то, разумеется, Scapy не единственный в этом мире генератор пакетов [4] и можно подобрать средство или сетевую библиотеку, привязанную к предпочитаемой технологии или языку программирования.

В-четвертых, учитывая "интерпретируемость" и многоуровневую абстракцию среды программирования, Scapy не претендует на рекордные показатели производительности. В частности, данный аспект нужно иметь ввиду при реализации тестирования производительности сети.

В-пятых, Scapy в большей степени ориентирована на протоколы, не требующие установления соединения (об этом еще будет сказано ниже).

В-шестых, Scapy является типичным представителем так называемых проектов одного разработчика, со всеми их недостатками и достоинствами.


Особенности интерактивного режима

Утилита Scapy предназначена для Unix-подобных систем, но есть также возможность ее использования под Windows. Последнюю версию программы можно найти на сайте проекта [5]. В Ubuntu/Mint Linux версия 2.x утилиты (речь здесь именно о ней) оказалась в стандартных репозиториях, поэтому установка никаких затруднений не вызвала. Среда программирования Python в системе была уже инсталлирована, а вот программных пакетов Gnuplot и PyX не нашлось. И при старте Scapy оповестила о невозможности рисования графиков и создания PDF-файлов. Если графики нужны, то указанные приложения также можно установить из стандартных репозиториев. Со всеми зависимостями поддержка графики в Scapy уменьшит свободное место на файловой системе примерно на сотню мегабайт.


Запускать утилиту нужно в сессии суперпользователя или с использованием механизма sudo, т. к. отправка произвольных пакетов и их перехват на уровне «сырого» устройства требует привилегии администратора:

$ sudo scapy
WARNING: No route found for IPv6 destination :: (no default route?)
Welcome to Scapy (2.1.0)
>>>

Это и есть интерактивный режим работы, в рамках которого можно определять объекты (пакеты, группы пакетов), выполнять команды, отправлять и перехватывать пакеты по сети, ожидать ответа от удаленных систем и т. д. Выход из интерактивного режима - команда quit().

Для просмотра списка всех доступных функции нужно использовать команду lsc(). Команда ls() отображает весьма внушительный список объектов, реализующих сообщения различных протоколов (ARP, BOOTP, DHCP, DNS, IEEE 802.11, IEEE 802.1q, Ethernet, ICMP, IP, NTP, PPP, PPPoE, Radius, SNMP, STP, TCP TFTP, UDP и т.д., а также различные протоколы для IP версии 6). По любой функции или объекту можно получить справку, используя команду help с указанием в скобках имени объекта, например help(sniff) или help(IP). Выход из справки — клавиша q.

Как известно, процесс сетевого взаимодействия является многоуровневым, и любые данные, предназначенные для отправки в сеть, снабжаются в заданном порядке заголовками протоколов. Эта концепция положена и в основу Scapy: пакет представляет собой совокупность уровней/слоев (заголовков протоколов), каждый из которых включает специфичные для него поля. С точки зрения программирования, описания слоя с полями и возможными операциями над ними — это классы, а экземпляры классов - объекты.
Создадим составной объект a, представляющий собой ICMP-запрос:
>>> a=IP(src="192.168.56.1",dst="192.168.56.2")/ICMP()/"icmpdata"

Уровни составных объектов разделяются символом «/». В определении каждого слоя в скобках через запятую можно указать необходимые значения полей. Строка определяет «сырые» (raw) данные протокола. Те значения полей, которые не определены пользователем, выставляются автоматически, исходя из значений по умолчанию или вычисляются если это необходимо. Для того, чтобы посмотреть, как будет выглядеть пакет с заполненными полями, можно воспользоваться методом объекта show2. Команда ls с указанием в скобках имени уровня отображает все поля и значения по умолчанию, например ls(ICMP).

Значение любого поля заголовка доступно как для записи, так и для чтения:
>>> a.ttl=10
>>> a.ttl
10

Разумеется, с уровнями пакета и целиком с пакетом можно проводить все допустимые объектно-ориентированной концепцией Python преобразования: присваивания, копирования, вызова методов, наследования и т. д.

Отправка и получение пакетов

Для безусловной отправки пакета на сетевом уровне используется функция send(). Для отправки пакета, сконструированного в предыдущем пункте нужно просто написать:
>>> send (a)

При использовании функции send(), Scapy автоматически добавляет заголовок канального уровня, обращаясь к системным возможностям: определяется исходящий сетевой интерфейс, при помощи ARP-запроса разрешается MAC-адрес узла назначения и т. п. (ниже приведен фрагмент дампа сетевого трафика):
15:47:31.212029 0a:00:27:00:00:00 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), 
length 42: Request who-has 192.168.56.2 tell 192.168.56.1, length 28
15:47:31.248953 08:00:27:53:1e:3e > 0a:00:27:00:00:00, ethertype ARP (0x0806), 
length 42: Reply 192.168.56.2 is-at 08:00:27:53:1e:3e, length 28
15:47:31.296903 0a:00:27:00:00:00 > 08:00:27:53:1e:3e, ethertype IPv4 (0x0800), 
length 50: 192.168.56.1 > 192.168.56.2: ICMP echo request, id 0, seq 0, length 16
15:47:31.297875 08:00:27:53:1e:3e > 0a:00:27:00:00:00, ethertype IPv4 (0x0800), 
length 50: 192.168.56.2 > 192.168.56.1: ICMP echo reply, id 0, seq 0, length 16

В Scapy можно также использовать безусловную функцию отправки на канальном уровне sendp(). Параметр iface sendp() позволяет выбирать исходящий интерфейс. При этом, пользователю нужно создать уровень Ethernet в пакете:
>>> sendp (Ether()/a,iface="vboxnet0")

Практический интерес представляют функции для отправки пакетов и ожидания ответов на них: sr() - на сетевом уровне, srp() - на канальном уровне.

Например, для сканирования подсети так называемым ARP-пингом (отправкой ARP-запросов и анализом полученных ARP-откликов) можно использовать следующую конструкцию (обратите внимание, отправляется не один пакет, а каждому узлу подсети по пакету):
>>> ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.56.0/24"),
timeout=2,iface="vboxnet0")

Функция srp() возвращает два списка: первый (ans) содержит пары отправленных и полученных пакетов, второй (unans) — пакеты, на которые не было ответов. Для того, чтобы получить доступ, например, к первому списку можно использовать такое выражение (отображен и результат):
>>> for snd,rcv in ans:
...       print rcv.summary()
... 
Ether / ARP is at 08:00:27:53:1e:3e says 192.168.56.2
Ether / ARP is at 08:00:27:a6:21:79 says 192.168.56.100 / Padding

Цикл for последовательно перебирает все пары и на каждой итерации, в переменной snd содержится отправленный пакет, а в переменной rcv - полученный.

В документации в этом примере для вывода результата используется такая конструкция:
>>> ans.summary(lambda (s,r): r.sprintf("%Ether.src% %ARP.psrc%") )

При более подробном рассмотрении «диковатое» для непривычного глаза выражение с ключевым словом lambda оказалась замечательной возможностью Python объявлять на лету однострочные функции, а «s» и «r» - все те же отправленный и полученный пакеты соответственно. Кстати, ARP-пинг в Scapy присутствует также и в виде стандартной функции arping().

Перехват сетевых пакетов

При использовании большего количества элементов программирования работать в интерактивном режиме становится неудобно. В этом случае можно воспользоваться возможностью использования функций и объектов Scapy в скриптах на Python, например как в листинге:
#!/usr/bin/python

from scapy.all import *
import sys

if len(sys.argv) != 3:
    print "Usage: ",sys.argv[0],"  "
    sys.exit(1)
ipa=sys.argv[1]
iface=sys.argv[2]

def psv_output(pkt):
    if pkt.haslayer(IP):
       return pkt.command()

def myfilter(pkt):
    if pkt.haslayer(IP):
       return ((pkt[IP].dst == ipa) or (pkt[IP].src == ipa))

sniff(prn=psv_output, iface=iface, lfilter=myfilter, store=0)

Функция sniff() предназначена для перехвата сетевого трафика. Она имеет интересный параметр prn, который дает возможность отображать заданный текст (в данном случае значением prn является функция psv_output, просто печатающая содержимое полей пакета). Еще одна опция lfilter позволяет задавать фильтр пакетов в виде логической функции (в данном случае myfilter). В документации описывается также параметр filter, который позволяет задавать фильтры в стиле Tcpdump, но в тестовой конфигурации он почему-то не работал. Также в функциях пришлось добавить проверку на наличие IP-уровня, так как первый же перехваченный ARP-пакет, ввиду отсутствия в нем данного уровня, приводил к программному исключению в работе скрипта.

О протоколах, ориентированных на соединение

Scapy базируется на простой модели «Запрос-ответ», в то же время ориентированные на соединение протоколы, в частности TCP, не слишком удобны для реализации в рамках этой модели, в связи с наличием процедуры установления и завершения соединения, управления скоростью передачи и т.д. [6].

Для работы с TCP непосредственно в Scapy есть две возможности. Первая заключается в использовании встроенного объекта StreamSocket на основе реализации стека TCP/IP ядра операционной системы. Вторая возможность связана с использованием TCP-клиента модуля Automaton [7], хотя функциональность в этом случае существенно ограничена. Кроме того, есть несколько сторонних реализаций TCP на основе Scapy.

Несколько слов о субпроектах

На сайте проекта также есть информация о нескольких поддерживаемых разработчиками Scapy проектах, использующих возможности данной утилиты.

Например, веб-приложение Scapytain [8] позволяет сохранять и воспроизводить пакеты, организовать на их основе комплексные тестовые сценарии. Правда, в настоящее время эта утилита находится в стадии бета-тестирования. Модульному тестированию посвящен еще один субпроект UTScapy [9]. В отличие от Scapytain, данная программа работает в режиме командной строки. Отдельный (исследовательский) интерес также представляет утилита Wifitrap [10], демонстрирующая концепцию взаимодействия по сетям Wi-Fi на основе перехвата и внедрения трафика.

Послесловие

Данная статья является введением в Scapy. Здесь не описаны многие возможности этого в некотором смысле уникального инструмента, в частности, работа с дампами сетевого трафика, встроенные сетевые функции, вывод результатов в виде таблиц, управление собственными таблицами маршрутизации и т. д. Много полезной информации по Scapy можно найти на сайте проекта, а также во встроенной справочной подсистеме.

Следует отметить, что первое знакомство со Scapy выявило прямую зависимость эффективности использования Scapy от знаний конкретных протоколов и особенностей сетевых взаимодействий: чем больше Вы знаете о сетях, тем проще освоиться со Scapy; чем глубже Вы постигаете Scapy, тем больше узнаете о том, как работают сети.

Ссылки
1.Welcome to Scapy’s documentation! - http://www.secdev.org/projects/scapy/doc/
2.Степан "Step" Ильин. Работа со скальпелем. Разбираемся с утилитой Scapy / Хакер № 06/09 (126) - http://www.xakep.ru/magazine/xa/126/028/1.asp 
3.The Python Tutorial - http://docs.python.org/tutorial/ 
4.Traffic generators - http://wiki.wireshark.org/Tools#Traffic_generators 
5.http://www.secdev.org/projects/scapy/ 
6.TCP and Scapy - http://trac.secdev.org/scapy/wiki/TCP 
7.Using Automata - http://trac.secdev.org/scapy/wiki/Automata 
8.http://www.secdev.org/projects/scapytain/ 
9.http://www.secdev.org/projects/UTscapy/ 
10.http://sid.rstack.org/static/articles/w/i/f/Wifitap_EN_9613.html 

А через недельку будет практика Scapy: в активной диагностике приложений с протоколом DHCP. Увидимся.

2 comments:

  1. Пишем tcp=TCP()
    читаем tcp.display()
    читаем подробно ls(tcp)
    собираем packet=ip/tcp
    посылаем send(packet)
    ...
    меняем tcp.port=22
    собираем packet=ip/tcp
    send(packet)
    ...
    пишем ip=IP()
    посылаем send(ip)
    читаем ls(ARP)
    читаем arp=ARP()
    собираем packet=ip/arp
    send(packet)

    ReplyDelete
  2. pip install scapy
    pip install PyX
    pip install matplotlib
    pip install IPy

    +
    Npcap


    Ipython.
    For interactive sessions using ipython can be great advantage. Install using pip3 or from your package manager

    Graphviz.
    For some visualizations, e.g. traceroute graph, dot is required on the PATH

    Matplotlib.
    Required for interactive plot/graph viewing.

    Networkx.
    Conversations can be converted to Networkx graph if library is present.

    PyX.
    To create PostScript, PDF and SVG files.

    LaTeX.
    To create PostScript and PDF files.ipython. For interactive sessions using ipython can be great advantage. Install using pip3 or from your package manager

    ReplyDelete

А что вы думаете по этому поводу?