В локальной сети, обычно, достаточно много машин с операционной системой Windows. И почти у каждой из них включено сетевое обнаружение, что делает ее ресурсы выставленными напоказ всей сети. Есть небольшой трюк, когда вы не хотите, чтобы ваш сетевой ресурс был виден в проводнике, в конце, после его имени нужно поставить знак $. Именно так светит диски в сеть ОС Windows. То есть, к примеру, есть общий ресурс ADMIN$. Если вы зайдете с помощью проводника, то вы ничего не увидите. Но, стоит попробовать обратиться к ресурсу напрямую и у вас попросят пароль. Давайте напишем небольшой скрипт, который использует в своей работе smbclient для сканирования компьютеров в сети на наличие общедоступных ресурсов.
Что потребуется?
Так как мы будем использовать smbclient, по умолчанию он установлен далеко не во всех ОС Linux, а потому, нам либо нужно установить его вручную выполнив команду:
либо не устанавливать изначально, так как мы напишем небольшую функцию, которая будет проверять его наличие и устанавливать в систему для работы.
Понадобиться библиотека requests. С ее помощью мы будем выполнять запросы к сайтам для получения внешнего ip-адреса, а также библиотека ping3, для того, чтобы мы могли пинговать компьютеры. Конечно, в процессе работы данного скрипта установка библиотеки requests не обязательна, так как напрямую не взаимодействует в скрипте ни с одной функцией. Но, так как я написал небольшой модуль в который собрал те функции, которые чаще всего требуются при работе с сетью, такие как получение локального ip, внешнего ip или получение координат по внешнему ip-адресу, установить ее нужно. Пишем в терминале:
Импортировать на данном этапе мы ничего не будем, так как у нас будет три модуля, импорт в них будет немного различаться. Почему три? Это вы поймете в процессе создания скрипта.
Модуль для работы с IP
Создадим модуль для работы с часто требующимися функциями по работе с IP-адресом. Я назвал модуль definition.py.
Выполним в него импорт необходимых для работы библиотек.
Теперь создадим класс IPDefinition и инициализируем необходимые параметры. В самом классе, как таковых функций нет. Он просто служит для удобства вызова функций. Все функции, к которым обращаются переменные при инициализации, находятся вне класса. Поэтому, он будет совсем небольшой. Вот полный его код:
Теперь напишем функции, к которым обращаются переменные при инициализации класса.
Первой функцией будет получение локального IP-адреса. На самом деле, эту функцию я нашел на Stack Overflow. Точного объяснения, к какому именно адресу коннектится сокет я не нашел, но при коннекте, функция получает имя сокета. И именно оно является локальным адресом. Эта функция кросплатформенна, а потому я решил использовать ее, вместо использования ip или ipconfig. Так как, желательно, чтобы модуль работал в любой операционной системе.
Создадим функцию local(). Объявим переменную st и установим протокол, по которому будет коннектится сокет и тип соединения — дейтаграм. Устанавливаем соединение, после чего, получаем имя сокета и возвращаем его из функции. В случае возникновения исключения значению ip присваиваем локальный IP, закрываем сокет и возвращаем адрес.
Следующая функция, которую нужно создать, это функция для получения внешнего, публичного IP-адреса. Создадим функцию public(). С помощью функции get библиотеки requests отправим запрос на адрес api.ipify.org. API этого сайта возвращает в ответ внешний IP, его мы и вернем из функции. В случае ошибки соединения вернем локальный IP в виде 127.0.0.1.
Еще нам понадобиться функция, которая получает IP-адрес роутера или маршрутизатора. Так как она понадобиться в процессе получения общедоступных ресурсов. Создадим функцию router(). Здесь, с помощью функции check_output библиотеки subprocess выполняем команду и получаем ответ, который парсим для получения результата. Для того, чтобы функция была кросплатформенной, для начала определяется операционная система, а затем, в зависимости от этого выполняется команда. Выполняется проверка являются ли полученные данные цифровыми. Если нет, делаем попытку получить IP-адрес с помощью функции сокет, gethostbyname.
И еще одна функция, которая в данном контексте не обязательна, но присутствует в модуле, хотя, ее нужно импортировать напрямую, помимо класса. Так происходит потому, что класс не принимает никаких значений. А данная функция требует, чтобы в нее передали IP-адрес. Служит она для получения координат (широты и долготы) по IP-адресу.
Создадим функцию geo_ip(ip). На вход она получает IP-адрес, для которого нужно получить координаты. Конечно, адрес должен быть внешним, так как по локальному адресу определить координаты вряд ли получиться.
Формируем ссылку на сервис, который возвращает данные по IP в виде JSON.
Выполняем запрос и возвращаем полученные значения.
На самом деле, в JSON прилетают не только координаты, но также и дополнительная информация, вроде адреса. Но в данном контексте она не особо нужна, так как, определение координат по IP в принципе не особо точное занятие.
Вот, для примера, как можно получить координаты в контексте данного модуля для своего IP:
На этом, пока все. Функции в модуле закончились. А значит, нужно приступать к написанию модуля, с помощью которого будут сканироваться общедоступные ресурсы локальной сети.
Модуль для сканирования сети на наличие общедоступных ресурсов
Создадим модуль smb_scan.py. Импортируем в него необходимые библиотеки для работы:
Создадим класс SMBscan и функцию инициализации переменных класса. Класс требует передачи в него IP-адреса сканируемой машины, а также логина и пароля, если таковые известны для доступа к сетевым ресурсам. Если логин и пароль не известны, присваиваем переменной значение «-N». Это опция указывающая на то, что будет попытка осуществления доступа к ресурсам без логина и пароля.
Получение данных об общедоступных дисках компьютера в сети
Создадим функцию класса smb_get(self). С помощью функции check_output, которая позволяет получить данные из выполняемой команды, выполним сканирование ресурсов компьютера. Данная команда состоит из пути к smbclient, опции L(list), которая позволяет просмотреть общедоступные сервисы, ip-адреса компьютера, связки логин-пароль. Логин и пароль передаются в виде опции -U login%pass, так как, если передать только логин, пароль будет запрошен явно в процессе выполнения. Если пароль и логин не переданы, сюда подставляется опция -N, которая указывает, что данные о пароле и логине отсутствуют. И выводим сообщения не относящиеся к делу в null.
После чего разбиваем ответ на строки и парсим полученные значения. Убираем пустые символы, а также строку, которая указывает, какая версия протокола выполняется. Затем снова собираем строки в кучку с помощью join и возвращаем собранные данные из функции.
Сканирование сетевых дисков
Создадим функцию класса smb_access(self). С ее помощью мы будем пробовать сканировать найденные диски на наличие общедоступных ресурсов и возможность к ним доступа без логина и пароля. Выполним для начала ту же команду, что и в предыдущей функции, но с помощью grep отфильтруем только те записи, в которых содержится значение Disk, а также с помощью sed -e удалим все символы, которые идут после его названия. Таким образом, на выходе данной команды мы получим необработанную строку, содержащую имена сетевых дисков, для примера: C$.
Теперь разобьем эту полученные значения построчно, и сформируем список с очищенными от пробелов и символов именами.
Создадим словарь, в который будем добавлять полученные значения и ошибки в результате выполнения команды с помощью os.popen. В данной команде мы передаем путь к smbclient, путь к диску, логин-пароль, если таковые имеются, если нет, значение будет «-N» укажем опцию -c, которая позволяет указать через ; несколько команд: получение списка директории и выход.
Полученные значения разобьем на строки, сформируем список, при формировании которого обрежем лишние символы, и добавим полученные значения в словарь, где ключом будет имя диска.
Пробежимся в цикле по словарю. Сформируем строку, которая будет состоять из полученных значений и переводов каретки для корректного отображения в терминале, заменим полученные строки на более понятные значения. После чего вернем эту строку из функции.
На этом код модуля закончен. Можно приступать к написанию модуля, в котором мы будем обращаться к созданным ранее модулям и получать нужную информацию.
Скрипт для получения данных
Создадим модуль scan.py. Импортируем нужные библиотеки и модули, а также объявим глобальный словарь, в который будем складывать полученные при сканировании IP-адреса.
Пинг сети, проверка наличия машин на адресе
Создадим функцию ping_net(ip), которая на входе получает ip-адрес. С помощью данной функции можно выполнить проверку доступности машины в сети. Так как нам, для наших целей больше информации не требуется, то есть, нет необходимости получать MAC-адрес или NETBIOS-имя компьютера. Выполняем пинг, если ответ получен, проверяем, не равен ли адрес локальному IP, так как получать список ресурсов у своей машины нам нет необходимости, также проверяем, не является ли IP адресом роутера или маршрутизатора. Если нет, добавляем адрес в список. Также обработаем исключение, при котором доступ, то есть пинг адреса запрещен. К примеру, если вы захотите сделать пинг 192.168.1.255, получите ошибку доступа, так как вы попытаетесь пинговать адрес для широковещательной рассылки.
Запуск потоков для выполнения пинга
Создадим функцию thread_func(target), которая на вход получает адрес и маску сети, для примера, в виде 192.168.1.1/24. В цикле выполняем итерацию по списку адресов, который получаем с помощью ip_network. Запускаем потоки, в которых целевой функцией является функция пинга ip-адреса, в которую передаем итерированный ip. Ждем завершения потоков. Обрабатываем ошибку, когда неверно указан адрес или маска.
Проверка доступности 139 порта
Создадим функцию check_port(ip, port=139), в которую передается ip-адрес, а также порт для сканирования. По умолчанию порт 139-й. Это порт сеансовой службы NetBIOS, которая активизирует браузер поиска других компьютеров, службы совместного использования файлов, Net Logon и службу сервера.
Выполнять проверку будем с помощью сокетов. Создаем сокет, в котором указываем, что он будет использовать TCP для работы, выполним коннект по переданному в функцию адресу к порту 139. Если есть ответ, вернем True, нет, вернем False.
Установка smbclient при его отсутствии
Создадим функцию smb_install(), в которой проверим, доступен ли пакет по определенному пути. Если нет, запускаем команду установки пакета, запрашиваем пароль у пользователя.
Запуск полного сканирования сети
Создадим функцию network_scan(ip_addr, user_pass), которая на входе получает диапазон для сканирования в виде, для примера, 192.168.1.1/24, а также связку логин-пароль, если таковые были указаны пользователем. Создаем список, в который будем складывать ip-адреса, у которых открыт порт 139, после чего запускаем функцию пинга сети с помощью потоков, куда и передаем диапазон.
Проверяем глобальный список, есть ли в нем IP-адреса. Если есть, запускаем по ним цикл, и проверяем на каждом адресе, открыт ли 139-й порт. Если открыт, добавляем в созданный список. После чего выводим сообщение о том, сколько найдено компьютеров с отрытым 139-портом, а также, сколько их найдено вообще.
Проверяем список после сканирования 139-го порта. Если он не пуст, пробегаемся по нему циклом и запускаем поочередно функции smb_get и smb_access класса SMBscan(cln, user_pass), в который передаем ip-адрес и связку логин-пароль. После чего, выводим в терминал полученные значения.
Функция пользовательского выбора
Создадим функцию main(). В ней мы будем запрашивать у пользователя желаемое действие на выбор. Доступен: пинг сети, сканирование одного ip-адреса, сканирование всей сети и выход из скрипта.
Для начала запустим функцию проверки, установлен ли smbclient, smb_install(). Просим у пользователя выбрать действие. Если пользователь выбирает пинг сети, просим ввести ip-адрес и маску сети, после чего запускаем сканирование сети в потоках thread_func(ip_mask), проверяем, есть ли значения в глобальном списке, если есть, выводим их в терминал.
Если пользователь выбрал сканирование одного компьютера, запрашиваем ip-адрес, затем запрашиваем логин. Если логин введен, запрашиваем пароль, формируем опции для передачи в команду сканирования общедоступных ресурсов. То есть, U логин%пароль. Если пользователь не ввел ничего, просто передаем пустое значение и вызываем поочередно функции сканирования компьютера на наличие дисков, и на наличие ресурсов на этих дисках, куда передаем ip-адрес, и логин-пароль. После выполнения функций выводим полученные значения в терминал.
Если пользователь выбрал сканирование всей сети, просим ввести его адрес и маску, запрашиваем логин-пароль и запускаем функцию network_scan(ip_mask, user_pass), куда передаем полученные значения.
Если пользователь выбрал выход, завершаем работу скрипта.
Небольшое видео демонстрации работы сканера в Kali Linux:
А на этом, пожалуй, всё. Все модули будут прикреплены во вложении к статье.
Спасибо за внимание. Надеюсь, что данная информация будет вам полезна
Автор Johan Van
источник codeby
Что потребуется?
Так как мы будем использовать smbclient, по умолчанию он установлен далеко не во всех ОС Linux, а потому, нам либо нужно установить его вручную выполнив команду:
sudo apt install smbclientлибо не устанавливать изначально, так как мы напишем небольшую функцию, которая будет проверять его наличие и устанавливать в систему для работы.
Понадобиться библиотека requests. С ее помощью мы будем выполнять запросы к сайтам для получения внешнего ip-адреса, а также библиотека ping3, для того, чтобы мы могли пинговать компьютеры. Конечно, в процессе работы данного скрипта установка библиотеки requests не обязательна, так как напрямую не взаимодействует в скрипте ни с одной функцией. Но, так как я написал небольшой модуль в который собрал те функции, которые чаще всего требуются при работе с сетью, такие как получение локального ip, внешнего ip или получение координат по внешнему ip-адресу, установить ее нужно. Пишем в терминале:
pip install requests ping3Импортировать на данном этапе мы ничего не будем, так как у нас будет три модуля, импорт в них будет немного различаться. Почему три? Это вы поймете в процессе создания скрипта.
Модуль для работы с IP
Создадим модуль для работы с часто требующимися функциями по работе с IP-адресом. Я назвал модуль definition.py.
Выполним в него импорт необходимых для работы библиотек.
Python:
from platform import system
from subprocess import check_output
from socket import gethostbyname, socket, AF_INET, SOCK_DGRAM
from requests import exceptions, get
Теперь создадим класс IPDefinition и инициализируем необходимые параметры. В самом классе, как таковых функций нет. Он просто служит для удобства вызова функций. Все функции, к которым обращаются переменные при инициализации, находятся вне класса. Поэтому, он будет совсем небольшой. Вот полный его код:
Python:
class IPDefinition:
def __init__(self):
"""
Класс для получения IP-адресов.
self.local_ip: локальный IP-адрес. Получает значение из функции.
self.public_ip: внешний IP-адрес. Получает значение из функции.
self.router_ip: IP-адрес роутера или маршрутизатора. Получает значение из функции.
"""
self.local_ip = local()
self.public_ip = public()
self.router_ip = router()
Теперь напишем функции, к которым обращаются переменные при инициализации класса.
Первой функцией будет получение локального IP-адреса. На самом деле, эту функцию я нашел на Stack Overflow. Точного объяснения, к какому именно адресу коннектится сокет я не нашел, но при коннекте, функция получает имя сокета. И именно оно является локальным адресом. Эта функция кросплатформенна, а потому я решил использовать ее, вместо использования ip или ipconfig. Так как, желательно, чтобы модуль работал в любой операционной системе.
Создадим функцию local(). Объявим переменную st и установим протокол, по которому будет коннектится сокет и тип соединения — дейтаграм. Устанавливаем соединение, после чего, получаем имя сокета и возвращаем его из функции. В случае возникновения исключения значению ip присваиваем локальный IP, закрываем сокет и возвращаем адрес.
Python:
def local():
"""
Получаем локальный IP-адрес с помощью коннекта
на адрес 10.255.255.255. В ответ получаем имя
сокета, которое и является адресом.
: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
Следующая функция, которую нужно создать, это функция для получения внешнего, публичного IP-адреса. Создадим функцию public(). С помощью функции get библиотеки requests отправим запрос на адрес api.ipify.org. API этого сайта возвращает в ответ внешний IP, его мы и вернем из функции. В случае ошибки соединения вернем локальный IP в виде 127.0.0.1.
Python:
def public():
"""
Получение внешнего, публичного IP-адреса.
:return: возвращает значение публичного IP-адреса.
"""
try:
return get('https://api.ipify.org/').text
except exceptions.ConnectionError:
return '127.0.0.1'
Еще нам понадобиться функция, которая получает IP-адрес роутера или маршрутизатора. Так как она понадобиться в процессе получения общедоступных ресурсов. Создадим функцию router(). Здесь, с помощью функции check_output библиотеки subprocess выполняем команду и получаем ответ, который парсим для получения результата. Для того, чтобы функция была кросплатформенной, для начала определяется операционная система, а затем, в зависимости от этого выполняется команда. Выполняется проверка являются ли полученные данные цифровыми. Если нет, делаем попытку получить IP-адрес с помощью функции сокет, gethostbyname.
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
И еще одна функция, которая в данном контексте не обязательна, но присутствует в модуле, хотя, ее нужно импортировать напрямую, помимо класса. Так происходит потому, что класс не принимает никаких значений. А данная функция требует, чтобы в нее передали IP-адрес. Служит она для получения координат (широты и долготы) по IP-адресу.
Создадим функцию geo_ip(ip). На вход она получает IP-адрес, для которого нужно получить координаты. Конечно, адрес должен быть внешним, так как по локальному адресу определить координаты вряд ли получиться.
Формируем ссылку на сервис, который возвращает данные по IP в виде JSON.
url = f'[URL]http://ip-api.com/json/[/URL]{ip}'Выполняем запрос и возвращаем полученные значения.
Python:
req = get(url=url).json()
return [req['lat'], req['lon']]
На самом деле, в JSON прилетают не только координаты, но также и дополнительная информация, вроде адреса. Но в данном контексте она не особо нужна, так как, определение координат по IP в принципе не особо точное занятие.
Python:
def geo_ip(ip):
"""
Функция для получения координат по внешнему IP-адресу.
Выполняет обращение к сервису, в который передает адрес.
В ответ получает JSON с координатами и не только (остальные данные не возвращаются из функции).
:param ip: внешний IP-адрес или любой адрес координаты которого нужно узнать.
:return: возвращает широту и долготу по IP.
"""
try:
url = f'http://ip-api.com/json/{ip}'
req = get(url=url).json()
return [req['lat'], req['lon']]
except Exception:
return 'Не удалось получить координаты'
Вот, для примера, как можно получить координаты в контексте данного модуля для своего IP:
geo_ip(IPDefinition().public_ip)На этом, пока все. Функции в модуле закончились. А значит, нужно приступать к написанию модуля, с помощью которого будут сканироваться общедоступные ресурсы локальной сети.
Python:
"""
Модуль объединяет в себе несколько функций для получения различного
рода IP-адресов.
В данном случае это:
- локальный IP;
- IP-роутера(маршрутизатора);
- внешний IP-адрес.
Для работы данной функции нужно установить библиотеку requests:
pip install requests
"""
from platform import system
from subprocess import check_output
from socket import gethostbyname, socket, AF_INET, SOCK_DGRAM
from requests import exceptions, get
class IPDefinition:
def __init__(self):
"""
Класс для получения IP-адресов.
self.local_ip: локальный IP-адрес. Получает значение из функции.
self.public_ip: внешний IP-адрес. Получает значение из функции.
self.router_ip: IP-адрес роутера или маршрутизатора. Получает значение из функции.
"""
self.local_ip = local()
self.public_ip = public()
self.router_ip = router()
def local():
"""
Получаем локальный IP-адрес с помощью коннекта
на адрес 10.255.255.255. В ответ получаем имя
сокета, которое и является адресом.
: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 public():
"""
Получение внешнего, публичного IP-адреса.
:return: возвращает значение публичного IP-адреса.
"""
try:
return get('https://api.ipify.org/').text
except exceptions.ConnectionError:
return '127.0.0.1'
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 geo_ip(ip):
"""
Функция для получения координат по внешнему IP-адресу.
Выполняет обращение к сервису, в который передает адрес.
В ответ получает JSON с координатами и не только (остальные данные не возвращаются из функции).
:param ip: внешний IP-адрес или любой адрес координаты которого нужно узнать.
:return: возвращает широту и долготу по IP.
"""
try:
url = f'http://ip-api.com/json/{ip}'
req = get(url=url).json()
return [req['lat'], req['lon']]
except Exception:
return 'Не удалось получить координаты'
geo_ip(IPDefinition().public_ip)
Модуль для сканирования сети на наличие общедоступных ресурсов
Создадим модуль smb_scan.py. Импортируем в него необходимые библиотеки для работы:
Python:
from os import popen
from shutil import which
from subprocess import check_output, CalledProcessError
Создадим класс SMBscan и функцию инициализации переменных класса. Класс требует передачи в него IP-адреса сканируемой машины, а также логина и пароля, если таковые известны для доступа к сетевым ресурсам. Если логин и пароль не известны, присваиваем переменной значение «-N». Это опция указывающая на то, что будет попытка осуществления доступа к ресурсам без логина и пароля.
Python:
class SMBscan:
def __init__(self, ip, user_pass):
"""
Инициализация класса для сканирования компьютера на наличие
общедоступных директорий и дисков.
:param ip: IP-адрес для сканирования.
:param user_pass: связка логин пароль, которая запрашивается у пользователя и
передается в виде login%pass. Если пользователь не вводит логин-пароль, по умолчанию
устанавливается значение -N, что означает отсутствие пароля.
"""
self.ip = ip
if user_pass == "" or user_pass == " ":
self.user_pass = "-N"
else:
self.user_pass = user_pass
Получение данных об общедоступных дисках компьютера в сети
Создадим функцию класса smb_get(self). С помощью функции check_output, которая позволяет получить данные из выполняемой команды, выполним сканирование ресурсов компьютера. Данная команда состоит из пути к smbclient, опции L(list), которая позволяет просмотреть общедоступные сервисы, ip-адреса компьютера, связки логин-пароль. Логин и пароль передаются в виде опции -U login%pass, так как, если передать только логин, пароль будет запрошен явно в процессе выполнения. Если пароль и логин не переданы, сюда подставляется опция -N, которая указывает, что данные о пароле и логине отсутствуют. И выводим сообщения не относящиеся к делу в null.
После чего разбиваем ответ на строки и парсим полученные значения. Убираем пустые символы, а также строку, которая указывает, какая версия протокола выполняется. Затем снова собираем строки в кучку с помощью join и возвращаем собранные данные из функции.
Python:
def smb_get(self):
"""
Сканирование компьютера с использованием smbclient. Получаем вывод команды с параметрами,
где: -L - опция для просмотра доступных сервисов компьютера.
:return: возвращает список общедоступных ресурсов.
"""
try:
view = check_output(f'{which("smbclient")} -L {self.ip} '
f'{self.user_pass} 2>/dev/null', shell=True).decode().splitlines()
view_smb = "\n".join([x.strip() for x in view if not x.startswith("SMB")])
return view_smb
except CalledProcessError:
return 'Ошибка соединения'
Сканирование сетевых дисков
Создадим функцию класса smb_access(self). С ее помощью мы будем пробовать сканировать найденные диски на наличие общедоступных ресурсов и возможность к ним доступа без логина и пароля. Выполним для начала ту же команду, что и в предыдущей функции, но с помощью grep отфильтруем только те записи, в которых содержится значение Disk, а также с помощью sed -e удалим все символы, которые идут после его названия. Таким образом, на выходе данной команды мы получим необработанную строку, содержащую имена сетевых дисков, для примера: C$.
Python:
disks = check_output(f'{which("smbclient")} -L '
f'{self.ip} {self.user_pass} '
f'2>/dev/null| grep " Disk " | sed -e "s/ Disk .*//"', shell=True).decode()
Теперь разобьем эту полученные значения построчно, и сформируем список с очищенными от пробелов и символов именами.
disks = [d.strip() for d in disks.splitlines()]Создадим словарь, в который будем добавлять полученные значения и ошибки в результате выполнения команды с помощью os.popen. В данной команде мы передаем путь к smbclient, путь к диску, логин-пароль, если таковые имеются, если нет, значение будет «-N» укажем опцию -c, которая позволяет указать через ; несколько команд: получение списка директории и выход.
Python:
disks_list = dict()
for disk in disks:
f = popen(f'{which("smbclient")} //{self.ip}/"{disk}" {self.user_pass} -c "dir;exit" 2>/dev/null').readlines()
d = [x.strip() for x in f]
disks_list.update({disk: d})
Полученные значения разобьем на строки, сформируем список, при формировании которого обрежем лишние символы, и добавим полученные значения в словарь, где ключом будет имя диска.
Пробежимся в цикле по словарю. Сформируем строку, которая будет состоять из полученных значений и переводов каретки для корректного отображения в терминале, заменим полученные строки на более понятные значения. После чего вернем эту строку из функции.
Python:
for key in disks_list:
found = found + "Найден общий ресурс: %s\n%s\n\n" % (key, "\n".join(disks_list[key]))
found = found.replace("NT_STATUS_ACCESS_DENIED", "Общий ресурс виден, но защищен паролем"). \
replace("NT_STATUS_OBJECT_PATH_NOT_FOUND", "Не удается получить доступ к файлу"). \
replace("NT_STATUS_NO_MEDIA_IN_DEVICE", "Диск общий, но нет доступа к носителю"). \
replace("NT_STATUS_DEVICE_DATA_ERROR", "Не удается получить доступ к общему ресурсу")
Python:
def smb_access(self):
"""
Для начала функция получает данные о доступных сервисах, после чего, данные фильтруются
и выбираются только диски, для которых выполняется попытка чтения содержимого.
:return: возвращает список найденных ресурсов.
"""
disks = check_output(f'{which("smbclient")} -L '
f'{self.ip} {self.user_pass} '
f'2>/dev/null| grep " Disk " | sed -e "s/ Disk .*//"', shell=True).decode()
disks = [d.strip() for d in disks.splitlines()]
disks_list = dict()
for disk in disks:
f = popen(f'{which("smbclient")} //{self.ip}/"{disk}" {self.user_pass} -c "dir;exit" 2>/dev/null').readlines()
d = [x.strip() for x in f]
disks_list.update({disk: d})
if disks_list:
found = ''
for key in disks_list:
found = found + "Найден общий ресурс: %s\n%s\n\n" % (key, "\n".join(disks_list[key]))
found = found.replace("NT_STATUS_ACCESS_DENIED", "Общий ресурс виден, но защищен паролем"). \
replace("NT_STATUS_OBJECT_PATH_NOT_FOUND", "Не удается получить доступ к файлу"). \
replace("NT_STATUS_NO_MEDIA_IN_DEVICE", "Диск общий, но нет доступа к носителю"). \
replace("NT_STATUS_DEVICE_DATA_ERROR", "Не удается получить доступ к общему ресурсу")
return "\n" + found
else:
return 'Не могу получить доступ'
На этом код модуля закончен. Можно приступать к написанию модуля, в котором мы будем обращаться к созданным ранее модулям и получать нужную информацию.
Python:
"""
Модуль для сканирования сети
на наличие расшаренных(общедоступных)
директорий и дисков локальной сети.
"""
from os import popen
from shutil import which
from subprocess import check_output, CalledProcessError
class SMBscan:
def __init__(self, ip, user_pass):
"""
Инициализация класса для сканирования компьютера на наличие
общедоступных директорий и дисков.
:param ip: IP-адрес для сканирования.
:param user_pass: связка логин пароль, которая запрашивается у пользователя и
передается в виде login%pass. Если пользователь не вводит логин-пароль, по умолчанию
устанавливается значение -N, что означает отсутствие пароля.
"""
self.ip = ip
if user_pass == "" or user_pass == " ":
self.user_pass = "-N"
else:
self.user_pass = user_pass
def smb_get(self):
"""
Сканирование компьютера с использованием smbclient. Получаем вывод команды с параметрами,
где: -L - опция для просмотра доступных сервисов компьютера.
:return: возвращает список общедоступных ресурсов.
"""
try:
view = check_output(f'{which("smbclient")} -L {self.ip} '
f'{self.user_pass} 2>/dev/null', shell=True).decode().splitlines()
view_smb = "\n".join([x.strip() for x in view if not x.startswith("SMB")])
return view_smb
except CalledProcessError:
return 'Ошибка соединения'
def smb_access(self):
"""
Для начала функция получает данные о доступных сервисах, после чего, данные фильтруются
и выбираются только диски, для которых выполняется попытка чтения содержимого.
:return: возвращает список найденных ресурсов.
"""
disks = check_output(f'{which("smbclient")} -L '
f'{self.ip} {self.user_pass} '
f'2>/dev/null| grep " Disk " | sed -e "s/ Disk .*//"', shell=True).decode()
disks = [d.strip() for d in disks.splitlines()]
disks_list = dict()
for disk in disks:
f = popen(f'{which("smbclient")} //{self.ip}/"{disk}" {self.user_pass} -c "dir;exit" 2>/dev/null').readlines()
d = [x.strip() for x in f]
disks_list.update({disk: d})
if disks_list:
found = ''
for key in disks_list:
found = found + "Найден общий ресурс: %s\n%s\n\n" % (key, "\n".join(disks_list[key]))
found = found.replace("NT_STATUS_ACCESS_DENIED", "Общий ресурс виден, но защищен паролем"). \
replace("NT_STATUS_OBJECT_PATH_NOT_FOUND", "Не удается получить доступ к файлу"). \
replace("NT_STATUS_NO_MEDIA_IN_DEVICE", "Диск общий, но нет доступа к носителю"). \
replace("NT_STATUS_DEVICE_DATA_ERROR", "Не удается получить доступ к общему ресурсу")
return "\n" + found
else:
return 'Не могу получить доступ'
Скрипт для получения данных
Создадим модуль scan.py. Импортируем нужные библиотеки и модули, а также объявим глобальный словарь, в который будем складывать полученные при сканировании IP-адреса.
Python:
from shutil import which
from socket import socket, AF_INET, SOCK_STREAM
from subprocess import Popen, PIPE
from threading import Thread
from ipaddress import ip_network
from ping3 import ping
from definition import IPDefinition
from smb_scan import SMBscan
IP_SET = []
Пинг сети, проверка наличия машин на адресе
Создадим функцию ping_net(ip), которая на входе получает ip-адрес. С помощью данной функции можно выполнить проверку доступности машины в сети. Так как нам, для наших целей больше информации не требуется, то есть, нет необходимости получать MAC-адрес или NETBIOS-имя компьютера. Выполняем пинг, если ответ получен, проверяем, не равен ли адрес локальному IP, так как получать список ресурсов у своей машины нам нет необходимости, также проверяем, не является ли IP адресом роутера или маршрутизатора. Если нет, добавляем адрес в список. Также обработаем исключение, при котором доступ, то есть пинг адреса запрещен. К примеру, если вы захотите сделать пинг 192.168.1.255, получите ошибку доступа, так как вы попытаетесь пинговать адрес для широковещательной рассылки.
Python:
def ping_net(ip):
"""
Проверяет доступность компьютера в сети с помощью
пинга, если доступен, добавляет адрес в глобальный
список, если нет, выход из функции.
:param ip: IP-адрес для пинга компьютеро.
:return: выход из функции.
"""
try:
if ping(ip):
if ip == IPDefinition().local_ip:
return
elif ip == IPDefinition().router_ip:
return
IP_SET.append(ip)
except PermissionError:
return
Запуск потоков для выполнения пинга
Создадим функцию thread_func(target), которая на вход получает адрес и маску сети, для примера, в виде 192.168.1.1/24. В цикле выполняем итерацию по списку адресов, который получаем с помощью ip_network. Запускаем потоки, в которых целевой функцией является функция пинга ip-адреса, в которую передаем итерированный ip. Ждем завершения потоков. Обрабатываем ошибку, когда неверно указан адрес или маска.
Python:
def thread_func(target):
"""
Запуск потоков для пинга адресов в диапазоне, который
итерируется с помощью ip_network. Ожидание завершения потоков.
:param target: IP-адрес с маской сети.
:return: выход из функции.
"""
threads = []
try:
for ip in ip_network(target, False):
t = Thread(target=ping_net, kwargs={'ip': str(ip)})
t.daemon = True
t.start()
threads.append(t)
for num, thread in enumerate(threads):
thread.join()
except ValueError:
print('Неверно указан адрес или маска. Повторите ввод')
return
Проверка доступности 139 порта
Создадим функцию check_port(ip, port=139), в которую передается ip-адрес, а также порт для сканирования. По умолчанию порт 139-й. Это порт сеансовой службы NetBIOS, которая активизирует браузер поиска других компьютеров, службы совместного использования файлов, Net Logon и службу сервера.
Выполнять проверку будем с помощью сокетов. Создаем сокет, в котором указываем, что он будет использовать TCP для работы, выполним коннект по переданному в функцию адресу к порту 139. Если есть ответ, вернем True, нет, вернем False.
Python:
def check_port(ip, port=139):
"""
Проверка порта службы SMB.
:param ip: IP-адрес для проверки порта.
:param port: порт сеансовой службы NetBIOS, которая
активизирует браузер поиска других компьютеров,
службы совместного использования файлов, Net Logon и службу сервера.
По умолчанию 139.
:return: возвращает True, если порт открыт и
False, если порт недоступен.
"""
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(2)
try:
s.connect((ip, int(port)))
s.shutdown(2)
return True
except Exception:
return False
Установка smbclient при его отсутствии
Создадим функцию smb_install(), в которой проверим, доступен ли пакет по определенному пути. Если нет, запускаем команду установки пакета, запрашиваем пароль у пользователя.
Python:
def smb_install():
"""
Проверка наличия smbclient на компьютере.
Если он не установлен в системе, пользователю
будет предложено его установить. Запрашивается пароль
для установки.
"""
if not which('smbclient'):
sudo_password = input("Внимание!\nНе установлен smbclient.\nДля его установки введите пароль sudo: ")
command1 = 'apt install smbclient -y'.split()
p = Popen(['sudo', '-S'] + command1, stdin=PIPE, stderr=PIPE,
universal_newlines=True)
p.communicate(sudo_password + '\n')
Запуск полного сканирования сети
Создадим функцию network_scan(ip_addr, user_pass), которая на входе получает диапазон для сканирования в виде, для примера, 192.168.1.1/24, а также связку логин-пароль, если таковые были указаны пользователем. Создаем список, в который будем складывать ip-адреса, у которых открыт порт 139, после чего запускаем функцию пинга сети с помощью потоков, куда и передаем диапазон.
Python:
set_smb = []
thread_func(ip_addr)
Проверяем глобальный список, есть ли в нем IP-адреса. Если есть, запускаем по ним цикл, и проверяем на каждом адресе, открыт ли 139-й порт. Если открыт, добавляем в созданный список. После чего выводим сообщение о том, сколько найдено компьютеров с отрытым 139-портом, а также, сколько их найдено вообще.
Python:
if len(IP_SET) > 0:
for ip in IP_SET:
if check_port(ip):
set_smb.append(ip)
print(f'\nНайдено компьютеров с SMB: {len(set_smb)}/{len(IP_SET)}')
Проверяем список после сканирования 139-го порта. Если он не пуст, пробегаемся по нему циклом и запускаем поочередно функции smb_get и smb_access класса SMBscan(cln, user_pass), в который передаем ip-адрес и связку логин-пароль. После чего, выводим в терминал полученные значения.
Python:
if len(set_smb) > 0:
for cln in set_smb:
print(f'\n[+] Сканирую: {cln}\n{"-" * 50}')
print(f'{SMBscan(cln, user_pass).smb_get()}')
print(SMBscan(cln, user_pass).smb_access())
else:
print('Сканировать нечего')
Python:
def network_scan(ip_addr, user_pass):
"""
Сканирование сети на наличие в ней компьютеров,
сканирование на каждом из них наличие общедоступных ресурсов.
Вывод полученных данных в терминал.
:param ip_addr: IP-адрес и маска сети.
:param user_pass: логин и пароль пользователя в виде: login%pass
"""
set_smb = []
thread_func(ip_addr)
if len(IP_SET) > 0:
for ip in IP_SET:
if check_port(ip):
set_smb.append(ip)
print(f'\nНайдено компьютеров с SMB: {len(set_smb)}/{len(IP_SET)}')
if len(set_smb) > 0:
for cln in set_smb:
print(f'\n[+] Сканирую: {cln}\n{"-" * 50}')
print(f'{SMBscan(cln, user_pass).smb_get()}')
print(SMBscan(cln, user_pass).smb_access())
else:
print('Сканировать нечего')
else:
print('Компьютеров в сети не обнаружено')
Функция пользовательского выбора
Создадим функцию main(). В ней мы будем запрашивать у пользователя желаемое действие на выбор. Доступен: пинг сети, сканирование одного ip-адреса, сканирование всей сети и выход из скрипта.
Для начала запустим функцию проверки, установлен ли smbclient, smb_install(). Просим у пользователя выбрать действие. Если пользователь выбирает пинг сети, просим ввести ip-адрес и маску сети, после чего запускаем сканирование сети в потоках thread_func(ip_mask), проверяем, есть ли значения в глобальном списке, если есть, выводим их в терминал.
Python:
smb_install()
user_ch = input('\nВыберите действие:\n [1] Пинг сети\n [2] Сканирование одного компьютера\n '
'[3] Сканирование сети\n [4] Выход\n >>> ')
if user_ch == "1":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
thread_func(ip_mask)
if len(IP_SET) > 0:
print('\nНайдены компьютеры:')
for ip in IP_SET:
print(f' {ip}')
else:
print('Компьютеров не найдено')
Если пользователь выбрал сканирование одного компьютера, запрашиваем ip-адрес, затем запрашиваем логин. Если логин введен, запрашиваем пароль, формируем опции для передачи в команду сканирования общедоступных ресурсов. То есть, U логин%пароль. Если пользователь не ввел ничего, просто передаем пустое значение и вызываем поочередно функции сканирования компьютера на наличие дисков, и на наличие ресурсов на этих дисках, куда передаем ip-адрес, и логин-пароль. После выполнения функций выводим полученные значения в терминал.
Python:
elif user_ch == "2":
ip = input('Введите IP адрес: ')
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
if check_port(ip):
print(SMBscan(ip, user_pass).smb_get())
print(SMBscan(ip, user_pass).smb_access())
Если пользователь выбрал сканирование всей сети, просим ввести его адрес и маску, запрашиваем логин-пароль и запускаем функцию network_scan(ip_mask, user_pass), куда передаем полученные значения.
Python:
elif user_ch == "3":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
network_scan(ip_mask, user_pass)
Если пользователь выбрал выход, завершаем работу скрипта.
Python:
elif user_ch == "4":
print('Good by...')
exit(0)
Python:
def main():
"""
Проверка пользовательского выбора. В зависимости
от выбора выполняется пинг сети, сканирование адреса или
сканирование всей сети.
"""
smb_install()
user_ch = input('\nВыберите действие:\n [1] Пинг сети\n [2] Сканирование одного компьютера\n '
'[3] Сканирование сети\n [4] Выход\n >>> ')
if user_ch == "1":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
thread_func(ip_mask)
if len(IP_SET) > 0:
print('\nНайдены компьютеры:')
for ip in IP_SET:
print(f' {ip}')
else:
print('Компьютеров не найдено')
elif user_ch == "2":
ip = input('Введите IP адрес: ')
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
if check_port(ip):
print(SMBscan(ip, user_pass).smb_get())
print(SMBscan(ip, user_pass).smb_access())
elif user_ch == "3":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
network_scan(ip_mask, user_pass)
elif user_ch == "4":
print('Good by...')
exit(0)
Python:
"""
Скрипт для пинга сети, проверки наличия в ней компьютеров
и сканирование каждого из них на наличие общедоступных ресурсов.
Для работы требует установки библиотеки ping3: pip install ping3.
Также, для работы скрипта на компьютере необходимо наличие
установленного пакета smbclient. Если данного пакета не установлено,
пользователю предлагается его установить.
Также в скрипт импортируются модули для работы с IP-адерсами, то есть
для определения локального, внешнего адреса, а также адреса роутера-маршрутизатора и
модуль для работы с smbclient и сканирования сети с его помощью.
"""
from shutil import which
from socket import socket, AF_INET, SOCK_STREAM
from subprocess import Popen, PIPE
from threading import Thread
from ipaddress import ip_network
from ping3 import ping
from definition import IPDefinition
from smb_scan import SMBscan
IP_SET = []
def ping_net(ip):
"""
Проверяет доступность компьютера в сети с помощью
пинга, если доступен, добавляет адрес в глобальный
список, если нет, выход из функции.
:param ip: IP-адрес для пинга компьютеро.
:return: выход из функции.
"""
try:
if ping(ip):
if ip == IPDefinition().local_ip:
return
elif ip == IPDefinition().router_ip:
return
IP_SET.append(ip)
except PermissionError:
return
def thread_func(target):
"""
Запуск потоков для пинга адресов в диапазоне, который
итерируется с помощью ip_network. Ожидание завершения потоков.
:param target: IP-адрес с маской сети.
:return: выход из функции.
"""
threads = []
try:
for ip in ip_network(target, False):
t = Thread(target=ping_net, kwargs={'ip': str(ip)})
t.daemon = True
t.start()
threads.append(t)
for num, thread in enumerate(threads):
thread.join()
except ValueError:
print('Неверно указан адрес или маска. Повторите ввод')
return
def check_port(ip, port=139):
"""
Проверка порта службы SMB.
:param ip: IP-адрес для проверки порта.
:param port: порт сеансовой службы NetBIOS, которая
активизирует браузер поиска других компьютеров,
службы совместного использования файлов, Net Logon и службу сервера.
По умолчанию 139.
:return: возвращает True, если порт открыт и
False, если порт недоступен.
"""
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(2)
try:
s.connect((ip, int(port)))
s.shutdown(2)
return True
except Exception:
return False
def smb_install():
"""
Проверка наличия smbclient на компьютере.
Если он не установлен в системе, пользователю
будет предложено его установить. Запрашивается пароль
для установки.
"""
if not which('smbclient'):
sudo_password = input("Внимание!\nНе установлен smbclient.\nДля его установки введите пароль sudo: ")
command1 = 'apt install smbclient -y'.split()
p = Popen(['sudo', '-S'] + command1, stdin=PIPE, stderr=PIPE,
universal_newlines=True)
p.communicate(sudo_password + '\n')
def network_scan(ip_addr, user_pass):
"""
Сканирование сети на наличие в ней компьютеров,
сканирование на каждом из них наличие общедоступных ресурсов.
Вывод полученных данных в терминал.
:param ip_addr: IP-адрес и маска сети.
:param user_pass: логин и пароль пользователя в виде: login%pass
"""
set_smb = []
thread_func(ip_addr)
if len(IP_SET) > 0:
for ip in IP_SET:
if check_port(ip):
set_smb.append(ip)
print(f'\nНайдено компьютеров с SMB: {len(set_smb)}/{len(IP_SET)}')
if len(set_smb) > 0:
for cln in set_smb:
print(f'\n[+] Сканирую: {cln}\n{"-" * 50}')
print(f'{SMBscan(cln, user_pass).smb_get()}')
print(SMBscan(cln, user_pass).smb_access())
else:
print('Сканировать нечего')
else:
print('Компьютеров в сети не обнаружено')
def main():
"""
Проверка пользовательского выбора. В зависимости
от выбора выполняется пинг сети, сканирование адреса или
сканирование всей сети.
"""
smb_install()
user_ch = input('\nВыберите действие:\n [1] Пинг сети\n [2] Сканирование одного компьютера\n '
'[3] Сканирование сети\n [4] Выход\n >>> ')
if user_ch == "1":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
thread_func(ip_mask)
if len(IP_SET) > 0:
print('\nНайдены компьютеры:')
for ip in IP_SET:
print(f' {ip}')
else:
print('Компьютеров не найдено')
elif user_ch == "2":
ip = input('Введите IP адрес: ')
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
if check_port(ip):
print(SMBscan(ip, user_pass).smb_get())
print(SMBscan(ip, user_pass).smb_access())
elif user_ch == "3":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
network_scan(ip_mask, user_pass)
elif user_ch == "4":
print('Good by...')
exit(0)
if __name__ == "__main__":
main()
Небольшое видео демонстрации работы сканера в Kali Linux:
А на этом, пожалуй, всё. Все модули будут прикреплены во вложении к статье.
Спасибо за внимание. Надеюсь, что данная информация будет вам полезна
Автор Johan Van
источник codeby