![](https://habrastorage.org/getpro/habr/upload_files/d3c/4e0/c37/d3c4e0c377ec0796d9c604fedb22cc1d.png)
Статья расскажет, как можно использовать Scapy для создания пакетов UDP и TCP протокола, так же попробуем реализовать взаимодействие по сети, отправив короткое сообщение с использованием каждого из протоколов.
Простые протоколы
TCP и UDP - это самые распространенные протоколы. По сути протокол - это то, что описывает правила работы систем и формат передаваемых данных. Для упрощения сетевые данные обозначаются как пакеты, а информация в них делится на служебную информацию и те данные, которые нужно передать.
На страницах wiki можно найти следующие картинки, которые как раз показывают концепцию разделения передаваемых данных на служебную информацию и полезную нагрузку.
Для TCP:
![](https://habrastorage.org/getpro/habr/upload_files/912/962/218/912962218c31bddc44d659011cbf50af.png)
Выше представлен общий вид пакета для адресации по IPv4. Как видно из картинки, полей предостаточно и их заполнение частично лежит на приложении, которое передает данные, частично на операционной системе.
Для UDP:
![](https://habrastorage.org/getpro/habr/upload_files/c0d/1a6/1ce/c0d1a61ceb9621b1c3426f1a15252278.png)
Структура протокола немного проще, но тем не менее есть, над чем нужно поработать, прежде чем отправлять данные в сеть.
Протоколы TCP и UDP для модели TCP/IP являются транспортными, то есть они используются для задач маршрутизации данных и их основная задача - создание соединения или просто доставка данных для конкретного приложения, которое открывает специальную структуру - сокет (совокупность IP адреса и номера порта). Для каждого из этих протоколов существуют свои правила для настройки соединения. Рассмотрим процесс для каждого их них.
Начнем с UDP протокола. Это самый простой протокол, который не требует дополнительных или предварительных действий для передачи данных. Не предоставляет никаких механизмов для контроля соединения, если данные по какой-то причине не будут доставлены, то единственный способ, чтобы данные все-таки доставить, это переслать их заново.
Попробуем реализовать это взаимодействие. В качестве тестового стенда будем использовать следующий набор виртуальных машин:
Windows 10 - система для получения и визуализации данных
Kali Linux - система, которая будет отправлять сообщения
В качестве первого этапа запустим scapy. Сделать это достаточно просто, scapy - это набор классов, который интегрирован в интерактивный шелл языка программирования Python. В Kali Linux он установлен по умолчанию, чтобы им воспользоваться достаточно в терминале набрать sudo scapy
. Sudo здесь необходимо для доступа к сетевому интерфейсу напрямую, scapy самостоятельно работает с пакетами, поэтому ему нужно больше прав:
![](https://habrastorage.org/getpro/habr/upload_files/89e/f2f/028/89ef2f0289a9688fdcfb6a81f2497034.png)
Просто так отправлять данные не получится, поэтому нужно сначала открыть порты для взаимодействия. Так как наша целевая система Windows, то сделать это можно с использованием, например, netcat. Netcat можно найти так же на Kali Linux, нужно только его передать в Windows. Сделать это можно так:
cd /usr/share/windows-resources/binaries/ && sudo python3 -m http.server 9090
Теперь, чтобы получить доступ к файлу для Windows, нужно либо скачать файл через Браузер, либо через powershell. Открыть UDP порт можно вот так:
nc.exe -u -lvvp 8585
![](https://habrastorage.org/getpro/habr/upload_files/169/ce3/ce3/169ce3ce3fd5b74d97ac0c7a3957002d.png)
Порт открыт, теперь готовим пакет для отправки. Scapy в отличии от других библиотек сразу позволяет отправлять данные, используя известный протокол, то есть даже не нужно заботиться о заполнении всех необходимых для отправки информации данных. Так же очень просто понять, какие поля можно изменять для использования. Пример создания пакета для UDP:
ip = IP(dst="192.168.1.110")
udp = UDP()
udp.dport=8585
data = "HELLO FROM UDP!"
packet = ip/udp/Raw(data)
send(packet)
В результате получаем следующие данные на системе Kali Linux:
![](https://habrastorage.org/getpro/habr/upload_files/7ee/48b/126/7ee48b126adf80088dc289b788e9ef28.png)
В системе Windows:
![](https://habrastorage.org/getpro/habr/upload_files/dd9/808/03f/dd980803f53dd12802362b6677d4ee24.png)
Попробуем так же отправить данные через TCP протокол. Стоит уточнить, что этот протокол в отличии от UDP требует предварительной настройки соединения. Для этого нужно пройти процесс 3-х рукопожатий. Это последовательность запросов, которую нужно выполнить перед тем, как отправить хоть какие-то данные. Наглядно это выглядит вот так:
![](https://habrastorage.org/getpro/habr/upload_files/710/ff4/08e/710ff408ed22e9fb48f7b2ae8732760a.png)
Это взаимодействие просто так не реализовать отправкой данных. Так как протокол умеет настраивать и контролировать содение, а scapy не уведомляет ОС об открывающемся взаимодействии, то соответственно все этапы с картинки придется выполнять самостоятельно.
Так же стоит помнить, что на поведение соединения могут влиять некоторые поля. Поля, с которыми придется работать:
flags - флаги состояния соединеня
sequence - идентификатор, который используется ОС для отправки данных
Среди перечисленного самым большим камнем преткновения является sequence. Этот идентификатор генерируется рандомно ОС или устройством и, чтобы его угадать или подобрать, нужно либо слушать трафик, либо озаботиться о предварительном заполнении. Мы будем идти именно 2-м путем - создадим собственные идентификаторы для sequence.
Так как для некоторых операционных систем просчет sequence может отличаться от обычного инкремента счетчика, то операционная система может просто сбрасывать или переповторять много раз одни и те же пакеты. Для ОС Windows это поведение означает, что это будет переотправка RST пакета. Чтобы этого избежать, нужно добавить вот такое правило в файерволл на Kali Linux:
sudo iptables -A OUTPUT -p tcp -d 192.168.1.110 -s 192.168.1.115 --dport 9090 --tcp-flags RST RST -j DROP
Теперь можно произвести сборку пакетов и отправку в целевую систему. На целевой системе открываем порт:
nc.exe -lvvp 9090
Собираем пакеты:
from scapy.all import *
ip=IP(dst='192.168.1.110')
SYN=TCP(sport=1030, dport=9090, flags='S', seq=10)
SYNACK=sr1(ip/SYN)
my_ack = SYNACK.seq + 1
ACK=TCP(sport=1030, dport=9090, flags='A', seq=11, ack=my_ack)
send(ip/ACK)
payload = 'SEND TCP'
PUSH=TCP(sport=1030,dport=9090, flags='PA', seq=11, ack=my_ack)
send(ip/PUSH/payload)
Так как процесс многоступенчатый, то стоит записать все команды в один файл. Запускаем и получаем вот такой результат на Kali Linux:
![](https://habrastorage.org/getpro/habr/upload_files/d7e/b96/2ac/d7eb962ac7ad5014050b5241f7874a07.png)
И вот такой на Windows:
![](https://habrastorage.org/getpro/habr/upload_files/079/cfe/e08/079cfee08f84945b861766007c1666e6.png)
Таким простым способом можно изучать, как себя ведет ОС при обработке пакетов, и разбираться в основных проблемах, возникающих при передаче данных через TCP и UDP.
Данная статья была подготовлена в преддверии старта курса Network Engineer. 20 января пройдет demoday курса, на который приглашаются все желающие. Регистрация на demoday доступна по ссылке.