Статья Обнаружение атаки ARP-spoofing с помощью Scapy в Python

Как можно защититься от атаки MITM ARP-spoofing? С одной стороны, если ваши маршрутизаторы не имеют защиты от данного вида атак, то практически никак. Потому, что даже обнаружение данного вида атаки довольно сложно с той точки зрения, что атакованная машина, а равно как и ее пользователь не будут даже подозревать о том, что что-то происходит. Но, обнаружить данную атаку вполне возможно. И в этом нам снова поможет Python и библиотека scapy.

0102.jpg


Что понадобится?

Данный скрипт должен работать как в Windows, так и в Linux. Для установки необходимых библиотек для Linux, а в данном случае нужна библиотека scapy, воспользуемся командой:

pip install --pre scapy[basic]

Для Windows понадобиться выполнить немного больше установок. Для начала установим scapy, точно такой же командой как и в Linux. Затем, для отображения всплывающего уведомления нужно будет установить библиотеку win10toast. Для этого в командной строке выполните команду:

pip install win10toast

Теперь, чтобы была возможность работать с библиотекой scapy, нужно установить Npcap, который можно загрузить по этой ссылке. После того, как все будет установлено, можно импортировать нужные библиотеки и приступать к написанию кода. Вот что нужно будет импортировать в основном блоке импорта:

Python:
from os import system
from platform import system as psys

from scapy.all import sniff

Все остальное будет импортироваться по мере необходимости, непосредственно в функциях.


Создаем обработчик

Для начала создадим словарь, в который будем помещать нашу собственную ARP-таблицу. Это необходимо для того, чтобы выполнять сравнение данных, которые получены в пакете со значениями таблицы.

Давайте создадим функцию packet_sniff(packet), которая на входе будет принимать пакеты переданные сниффером, сравнивать с ARP-таблицей, которую мы создали в словаре ip_mac_dict.

Получаем ip и mac адреса из пакета.

Python:
ip_sourse = packet['ARP'].psrc
mac_source = packet['Ether'].src

Прогоняем по ключам словаря. И если полученное значение не совпадает, значит начинаем бить тревогу. Также получим ip адрес, откуда и пришел поддельный пакет.

Python:
    if mac_source in ip_mac_dict.keys():
        if ip_mac_dict[mac_source] != ip_sourse:
            try:
                ip_reference = ip_mac_dict[mac_source]
            except:
                ip_reference = "unknown"

А теперь бьем тревогу. Для начала проверяем, какая ОС у нас установлена. Так как вывод всплывающего сообщения в разных ОС отличается. Потому, если у нас Windows, импортируем библиотеку win10toast и создаем объект ToastNotifier. А затем выводим сообщение на экран. Параметры, которые нужно указать в выводе сообщения: Заголовок, Тело сообщения, и в данном случае duration, время в секундах, которое будет показываться данное сообщение. Тут надо больше поэкспериментировать. Но я пока остановился на 4 секундах. Так как если поставить меньше, то сообщения начинают сыпаться с интервалом секунды в две. Больше — есть вероятность пропустить сообщения. Но, тем не менее, этого достаточно для того, чтобы обнаружить атаку.

Python:
            if psys() == "Windows":
                print(f'\n[+] Обнаружена возможная ARP-атака\n- Возможно, что машина с адресом\n- {ip_reference} '
                      f'притворяется {ip_sourse}\n')
                from win10toast import ToastNotifier
                toast = ToastNotifier()
                toast.show_toast('Обнаружена возможная ARP-атака', f'Машина {ip_reference} притворяется {ip_sourse}',
                                 duration=4)

Так же и для Linux. Только тут с выводом сообщения попроще.

Python:
            elif psys() == "Linux":
                print(f'\n[+] Обнаружена возможная ARP-атака\n- Возможно, что машина с адресом\n- {ip_reference} '
                      f'притворяется {ip_sourse}\n')
                command = f'''notify-send "Обнаружена возможная ARP-атака" "Машина {ip_reference} притворяется {ip_sourse}"'''
                system(command)

Python:
def packet_sniff(packet):
    ip_sourse = packet['ARP'].psrc
    mac_source = packet['Ether'].src
    if mac_source in ip_mac_dict.keys():
        if ip_mac_dict[mac_source] != ip_sourse:
            try:
                ip_reference = ip_mac_dict[mac_source]
            except:
                ip_reference = "unknown"

            if psys() == "Windows":
                print(f'\n[+] Обнаружена возможная ARP-атака\n- Возможно, что машина с адресом\n- {ip_reference} '
                      f'притворяется {ip_sourse}\n')
                from win10toast import ToastNotifier
                toast = ToastNotifier()
                toast.show_toast('Обнаружена возможная ARP-атака', f'Машина {ip_reference} притворяется {ip_sourse}',
                                 duration=4)
            elif psys() == "Linux":
                print(f'\n[+] Обнаружена возможная ARP-атака\n- Возможно, что машина с адресом\n- {ip_reference} '
                      f'притворяется {ip_sourse}\n')
                command = f'''notify-send "Обнаружена возможная ARP-атака" "Машина {ip_reference} притворяется {ip_sourse}"'''
                system(command)
    else:
        ip_mac_dict[mac_source] = ip_sourse


Перехватываем пакеты

А теперь нужно запустить перехватчик пакетов, который мы импортировали из библиотеки scapy, sniff. Сделаем мы это в функции main(). А также проведем еще несколько необходимых проверок.

Итак, при старте скрипта, для начала проверяем, является ли рабочая операционная система Linux. Если да, проверяем с какими правами запущен скрипт. Если это не суперпользователь, то выводим сообщение и прерываем работу скрипта. Если же суперпользователь на месте, то выводим сообщение о том, что анализ пакетов запущен и запускаем функцию sniff.

sniff(count=0, filter="arp", store=0, prn=packet_sniff)

Полный код функции main() будет чуть ниже. А пока немного пояснений по параметрам, с которыми запущена функция. count в значении 0 указывает на то, что нужно анализировать пакеты непрерывно. Данный параметр указывает функции на то, сколько пакетов подлежат анализу. А значит, если мы укажем число отличное от 0, то работа данной функции в ключе обнаружения атак не будет иметь смысла. filter — здесь указывается тип пакета для перехвата. В данном случае нас интересуют именно arp-пакеты. store указывает на количество пакетов, которое нам нужно сохранять. Здесь же указан 0, что означает, что память на сохранение пакетов мы тратить не будем. Так как они будут просто анализироваться. И наконец параметр prn указывает на функцию, которая будет вызываться при получении пакета, для последующей его передачи и обработки в этой функции.

Python:
def main():
    if psys() == "Linux":
        from os import getuid
        if not getuid() == 0:
            print('\n[+] Запустите скрипт с правами суперпользователя!\n')
            return
        else:
            print(f'[+] Анализ ARP-пакетов... \n')
            sniff(count=0, filter="arp", store=0, prn=packet_sniff)
    elif psys() == "Windows":
        print(f'[+] Анализ ARP-пакетов... \n')
        sniff(count=0, filter="arp", store=0, prn=packet_sniff)

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

001.png

Теперь перенесем скрипт в Kali, для примера и запустим там. А также запустим спуфер на машину с ней. И вот также посыпались сообщения:

002.png

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

Python:
from os import system
from platform import system as psys

from scapy.all import sniff

ip_mac_dict = {}


def packet_sniff(packet):
    ip_sourse = packet['ARP'].psrc
    mac_source = packet['Ether'].src
    if mac_source in ip_mac_dict.keys():
        if ip_mac_dict[mac_source] != ip_sourse:
            try:
                ip_reference = ip_mac_dict[mac_source]
            except:
                ip_reference = "unknown"

            if psys() == "Windows":
                print(f'\n[+] Обнаружена возможная ARP-атака\n- Возможно, что машина с адресом\n- {ip_reference} '
                      f'притворяется {ip_sourse}\n')
                from win10toast import ToastNotifier
                toast = ToastNotifier()
                toast.show_toast('Обнаружена возможная ARP-атака', f'Машина {ip_reference} притворяется {ip_sourse}',
                                 duration=4)
            elif psys() == "Linux":
                print(f'\n[+] Обнаружена возможная ARP-атака\n- Возможно, что машина с адресом\n- {ip_reference} '
                      f'притворяется {ip_sourse}\n')
                command = f'''notify-send "Обнаружена возможная ARP-атака" "Машина {ip_reference} притворяется {ip_sourse}"'''
                system(command)
    else:
        ip_mac_dict[mac_source] = ip_sourse


def main():
    if psys() == "Linux":
        from os import getuid
        if not getuid() == 0:
            print('\n[+] Запустите скрипт с правами суперпользователя!\n')
            return
        else:
            print(f'[+] Анализ ARP-пакетов... \n')
            sniff(count=0, filter="arp", store=0, prn=packet_sniff)
    elif psys() == "Windows":
        print(f'[+] Анализ ARP-пакетов... \n')
        sniff(count=0, filter="arp", store=0, prn=packet_sniff)


if __name__ == "__main__":
    main()


Спасибо за внимание. Надеюсь, что данная информация будет кому-нибудь полезна
 
Мы в соцсетях:

1 августа стартует курс «Основы программирования на Python» от команды The Codeby

Курс будет начинаться с полного нуля, то есть начальные знания по Python не нужны. Длительность обучения 2 месяца. Учащиеся получат методички, видео лекции и домашние задания. Много практики. Постоянная обратная связь с кураторами, которые помогут с решением возникших проблем.

Запись на курс до 10 августа. Подробнее ...