• Открыта запись на вторую часть курса по анонимности и безопасности в сети интернет "Paranoid II" от команды codeby. Анонимные роутеры, Подъём, настройка и администрирование Tor-ноды, Работа с железом ПК, Удаление аппаратных закладок, Минимизация рисков, Авторские разработки и многое другое. Подробнее ...

Конкурс [ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.

explorer

explorer

Red Team
05.08.2018
672
1 458
Статья для участия в конкурсе Тестирование Веб-Приложений на проникновение
Приветствую тебя читатель!

Сегодня мы окунёмся в настоящую эволюцию, и напишем целых три программы, от простого к более сложному.

[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.



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

У разных портов бывает разное назначение. Приведу буквально несколько примеров:

21 — FTP (File Transfer Protocol) Протокол передачи файлов. FTP позволяет подключаться к серверам, просматривать содержимое каталогов и загружать файлы с сервера или на сервер.
23 — TELNET (TELecommunication NETwork) Позволяет управлять Операционной Системой удаленно.
53 – DNS (Domain Name System) Показывает установлен ли DNS. Может использоваться для так называемого DNS Spoofing, то есть подменой объекта DNS.

Первая ступень – черепаха

Приступим:

Первым делом подключаем модуль socket для работы с сокетами (интерфейс для обеспечения обмена данными между процессами). И модуль netaddr для работы с IP-адресами.

Python:
import socket
from netaddr import *
Далее создаём функцию сканирования портов, в которой создаётся сокет и выставляется таймаут. Мы будем пытаться приконнектиться к порту, и в случае соединения, пишем что порт открыт. Открытое соединение обязательно закрываем. Если соединение не удалось, то ничего не пишем, оператор-заглушка pass пропустит все ошибки.

Python:
def port_scan(port, host):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(0.5)
    try:
        connection = s.connect((host, port))
        print(host, 'Port :', port, "is open.")
        connection.close()
    except:
        pass
Потом делаем ввод диапазонов IP-адресов, с помощью split определяем разделитель, в данном случае дефис. То есть ввод осуществляется так 87.248.98.0-87.248.98.255 Также назначаем начало и конец диапазона. Делаем ввод нужного порта.

Python:
ipStart, ipEnd = input("Enter IP-IP: ").split("-")
iprange = IPRange(ipStart, ipEnd)

port = int(input('Enter port: '))
Ну и напоследок накидаем цикл перебора IP-адресов.

Python:
for ip in iprange:
    host = str(ip)
    port_scan(port, host)

input()
Скрипт готов. Давайте его протестируем. Вводим диапазон 87.248.98.0-87.248.98.255 и порт 443.

[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


Сканер работает, но посмотрите на скорость – 127 секунд понадобилось, чтобы просканировать 256 позиций. Это ужасно медленно – настоящая черепаха! И немудрено, ведь он однопоточный.

Python:
import socket
from netaddr import *


def port_scan(port, host):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(0.5)
    try:
        connection = s.connect((host, port))
        print(host, 'Port :', port, "is open.")
        connection.close()
    except:
        pass


ipStart, ipEnd = input("Enter IP-IP: ").split("-")
iprange = IPRange(ipStart, ipEnd)

port = int(input('Enter port: '))


for ip in iprange:
    host = str(ip)
    port_scan(port, host)

input()

[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


Вторая ступень – арабский скакун

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

Поскакали! :)

Для этого мы дополнительно импортируем модуль threading, необходимый для создания многопоточности.

Python:
import threading
import socket
from netaddr import *
Теперь изменим цикл, и добавим туда создание и запуск потока.

Python:
for ip in iprange:
    host = str(ip)
    t = threading.Thread(target=port_scan, args=(port, host))
    t.start()
Ну что же, недолго думая, начнём тестирование. Возьмём тот же диапазон и попробуем разные порты, которые точно открыты в данном диапазоне.

[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


Й-я-х-у-у!!! Фантастика!!! Всего три строчки добавлены в код, и программу просто не узнать. Скорость отличная, 8-9 сотых секунды на 256 хостов. Настоящий жеребец!

[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


Ну а теперь ложка дёгтя. Выставим-ка диапазон побольше 87.248.95.0-87.248.98.255 Здесь уже 1024 адреса. Запускаем программу, и … получаем ошибку – не удаётся запустить новый поток. Похоже наш арабский скакун подвернул себе ногу :(

[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


А так всё хорошо начиналось… Программа получилась очень шустрая, но больше двух диапазонов(512 адресов), у неё не получается осилить. Произошёл конфликт потоков. Почему так произошло? Программа запускает потоки быстрее, чем они успевают завершаться.

На самом деле этот вопрос решается использованием замков или семафоров для блокировки потоков. Но мы на этот раз воспользуемся совершенно другой возможностью, которая есть в python – это мультипроцессинг.

Python:
import threading
import socket
from netaddr import *


def port_scan(port, host):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(0.5)
    try:
        connection = s.connect((host, port))
        print(host, 'Port :', port, "is open.")
        connection.close()
    except:
        pass


print('-' * 36)
port = int(input('Enter port: '))
ipStart, ipEnd = input("Enter IP-IP: ").split("-")
iprange = IPRange(ipStart, ipEnd)
print('-' * 36)


for ip in iprange:
    host = str(ip)
    t = threading.Thread(target=port_scan, args=(port, host))
    t.start()

input()

Третья ступень – зверь

Ну что же друзья, вот мы и добрались до самого интересного. Начнём с того, чем же отличается мультипроцессинг от потоков. Потоки создаются в одном главном потоке GIL (Global Interpreter Lock). То есть все потоки работают внутри одного главного потока.

А модуль multiprocessing позволяет нам запускать множество процессов – сколько процессов, столько GIL'ов. Круто, да? Кроме того, имеется замечательный класс Pool умеющий выполнять функции параллельно. То есть исключается ситуация, когда несколько потоков пытаются обратиться одновременно к одному объекту. Для модуля multiprocessing также можно применять semaphore и lock, как и в threading. Но у нас будет чистый эксперимент без использования замков.

Возьмёмся за дело – вместо threading импортируем Pool. И немного изменим функцию port_scan.

Python:
import socket
from multiprocessing import Pool
from netaddr import *


def port_scan(arg):
    host, port = arg

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(0.5)
    try:
        s.connect((host, port))
        print(host, 'Port :', port, "is open.")
        s.close()

        return port, True
    except (socket.timeout, socket.error):
        return port, False
Добавим вместо ввода порта количество процессов. А портов нарисуем целый список, чего уж тут вошкаться с одним портом ;)

Python:
if __name__ == '__main__':

    print('-' * 38)
    ipStart, ipEnd = input("Enter IP-IP: ").split("-")
    iprange = IPRange(ipStart, ipEnd)
    number = int(input('Number of processes: '))
    print('-' * 38)

    ports = [43, 80, 109, 110, 115, 118, 119, 143,
            194, 220, 443, 540, 585, 591, 1112, 1433, 1443, 3128, 3197,
            3306, 4000, 4333, 5100, 5432, 6669, 8000, 8080, 9014, 9200]
Ну и в завершение нахимичим с циклами for, вложив их в друг друга. Первый цикл останется без изменений, в нём мы прокрутим ip. Второй цикл создаёт пул процессов с аргументами порт и хост. И третий, завершающий цикл, запустит калейдоскоп портов. Завершает тройку циклов вывод результатов.

Python:
pool = Pool(processes=number)

    for ip in iprange:
        host = str(ip)
        for port, status in pool.imap_unordered(port_scan, [(host, port) for port in ports]):
            pass
Так, уже чешутся руки потестить :) Не будем мелочиться, и забубеним вот такенный диапазон 87.248.85.0-87.248.98.255, но сколько же потоков сделать? Пусть будет 400! Ну вот, выставил от балды, а сколько же там операций понадобится провернуть программе? Немного математики не помешает.
14*256*34 = 121 856

Расшифрую – 14 диапазонов по 256 хостов и 34 порта.
Ну ничего себе!!! А не подавится?! Нечего гадать, старт! Первые секунды ничего не происходило, оно и понятно, в это время программа запускала 400 процессов. А потом понеслось…

[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


Ё-оу!!! Программа даже не поморщилась, проглотила и пережевала в своей пасти 121 856 операций. Настоящий зверь!!! Ушло времени 30.5 минут (1834.28 секунд).

[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


Давайте сделаем синтетический подсчёт - производительность операций в секунду:

1] Черепаха

256/127.28 = 2.01

2] Арабский скакун

256/0.09 = 2844.44

3] Зверь

121 856/1834.28 = 66.43

Что мы видим – черепаха самая медленная, но она стабильная, и её скорость всегда будет примерно одинаковой.
Арабский скакун – просто огонь, лучшее решение для проверки 256 хостов.
Зверь – всеядный и бесстрашный, работает с огромными диапазонами ip, и большим количеством процессов, одновременно тестируя множество портов.

Ну а теперь ещё о звере. Мои наблюдения следующие:

- Если выставить 1 порт и маленький диапазон на 256 хостов, то скорость идентична черепахе. Меньше задач – меньше производительность.
- Пропуски открытых портов замечены не были.
- Разумеется при разном количестве процессов, результаты могут отличаться в разы.
- Количество процессов ограничено исключительно производительностью машины. Даже на моём древнем компе, 1000 запущенных процессов прекрасно переваривалось.

Однако не стоит думать, что чем больше процессов, тем лучше. После любого пика идёт падение, так что нужно выбирать золотую середину. Часто 90-100 процессов давали лучшие результаты. В общем нужно подбирать оптимальный параметр под своё железо.

Условия тестирования вкратце:

Windows 7, оперативной памяти 4 Гб, процессор Dual-Core E5400, видеокарта GeForce GT 630, скорость интернета очень низкая.

[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


Послесловие:

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

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

P.P.S Сначала было интересно наблюдать как появляются всё новые списки закрытых портов. Но нет смысла выводить ненужную информацию в консоль, поэтому выводятся только гуды.
После запуска зверя тестирование портов идёт до тех пор, пока не появится надпись Enter to exit

Python:
import socket
from multiprocessing import Pool
from netaddr import *
import time


def port_scan(arg):
    host, port = arg

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(0.5)
    try:
        s.connect((host, port))
        print(host, 'Port :', port, "is open.")
        s.close()

        return port, True
    except (socket.timeout, socket.error):
        return port, False


if __name__ == '__main__':

    print('-' * 38)
    ipStart, ipEnd = input("Enter IP-IP: ").split("-")
    iprange = IPRange(ipStart, ipEnd)
    number = int(input('Number of processes: '))
    print('-' * 38)

    ports = [43, 80, 109, 110, 115, 118, 119, 143,
            194, 220, 443, 540, 585, 591, 1112, 1433, 1443, 3128, 3197,
            3306, 4000, 4333, 5100, 5432, 6669, 8000, 8080, 9014, 9200]
    pool = Pool(processes=number)

    start_time = time.clock()
    for ip in iprange:
        host = str(ip)
        for port in pool.imap_unordered(port_scan, [(host, port) for port in ports]):
            pass

    print('\nScanning Completed in:', "%.3f" % (time.clock() - start_time), "seconds")
    input('Enter to exit')
 
Последнее редактирование:
explorer

explorer

Red Team
05.08.2018
672
1 458
Тут у меня спрашивали про оконный интерфейс. Решил накидать оболочку.

[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


[ Пишем Hack-Tools ] - многопоточный сканер IP-диапазонов. Эволюция.


Только это черепаха :) То есть один диапазон сканирует две минуты. Ну и здесь ещё результаты выводятся после окончания сканирования, а не сразу. Короче учебный материал для тех, кто хочет GUI освоить.

Python:
import socket
from netaddr import *
from tkinter import *


icon = '''
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
'''

root = Tk()
root.resizable(width=False, height=False)
root.title("Scanport IP Turtle")
root.geometry("269x300")
calculated_text = Text(root, height=10, width=34)

address_entry = Entry(width=20, justify=CENTER)
endip_entry = Entry(width=20, justify=CENTER)
port_entry = Entry(width=20, justify=CENTER)

address_label = Label(text=" Enter ipStart :")
endip_label = Label(text=" Enter ipEnd :")
port_label = Label(text=" Enter port :")

address_label.grid(row=0, column=0, padx=10, sticky="w")
endip_label.grid(row=1, column=0, padx=10, sticky="w")
port_label.grid(row=2, column=0, padx=10, sticky="w")

address_entry.grid(row=0, column=1, pady=5)
endip_entry.grid(row=1, column=1, pady=5)
port_entry.grid(row=2, column=1, pady=5)

address_entry.insert(0, "87.248.98.0")
endip_entry.insert(0, "87.248.98.255")
port_entry.insert(0, "80")


def port_scan(port, host):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(0.5)

    try:
        connection = s.connect((host, port))
        p = ' ', host, 'Port:', port, 'open'
        calculated_text.insert(1.0,' '.join(map(str, p)))
        calculated_text.insert(1.0,'\n')
        connection.close()
    except:
        pass


ipStart = address_entry.get()
ipEnd = endip_entry.get()
iprange = IPRange(ipStart, ipEnd)
port = int(port_entry.get())


def scan():
    for ip in iprange:
        host = str(ip)
        port_scan(port, host)


scrollb = Scrollbar(root, command=calculated_text.yview)
scrollb.grid(row=4, column=2, sticky=N+S)

calculated_text.grid(row=4, column=0, sticky='nswe', columnspan=3)
calculated_text.configure(yscrollcommand=scrollb.set)


display_button = Button(text=" Scanport ", command=scan)
display_button.grid(row=3, column=1, padx=10, pady=5, sticky="n")

img = PhotoImage(data=icon)
root.tk.call('wm', 'iconphoto', root._w, img)
root.mainloop()
 
shArky

shArky

Well-known member
07.01.2017
58
41
Дальше - больше. Можно и запись в файл вести, а не в консоль. Жалко, что я не на питоне пишу, хотя даже я разобрался в исходном коде.
 
explorer

explorer

Red Team
05.08.2018
672
1 458
Дальше - больше. Можно и запись в файл вести, а не в консоль. Жалко, что я не на питоне пишу, хотя даже я разобрался в исходном коде.
Я уже неспешно начал писать Pro-версию, запись в файл будет. Одной из главных фишек будет охват большого количества вариантов сканирования - типа:

* Диапазон IP - лист портов
* Диапазон IP - диапазон портов
* Лист IP - лист портов
и так далее всего 12 вариантов. Не нашёл в сети ничего похожего, 3-4 варианта максимум.

А чтобы пользователь не путался, всё будет вложено в друг друга, в общем для самого неподготовленного юзера будет простота использования.
 
  • Нравится
Реакции: shArky и Pirnazar
lmike

lmike

нет, пердело совершенство
Lotus team
27.08.2008
7 256
439
писал на java с nio по оракловому примеру, многопоточная прога...
на линухах упевала максимум открытых сокетов забить...
цель - поиск хостов в сетках, к которым комп подключен (автоматом определяет маски и интерфейсы с IPV4), с таймаутом по установлению соединения на сокет и общий таймаут по ожиданию всех потоков, запись в лог файл
CIDR ограничивал на 20
код для java 1.8
НО основным будет, в любом случае, детальное прощупывание хоста, т.е. ответ хоста c адресом/портом, по сокету - это ниочем
 
explorer

explorer

Red Team
05.08.2018
672
1 458
писал на java с nio по оракловому примеру, многопоточная прога...
на линухах упевала максимум открытых сокетов забить...
цель - поиск хостов в сетках, к которым комп подключен (автоматом определяет маски и интерфейсы с IPV4), с таймаутом по установлению соединения на сокет и общий таймаут по ожиданию всех потоков, запись в лог файл
CIDR ограничивал на 20
код для java 1.8
НО основным будет, в любом случае, детальное прощупывание хоста, т.е. ответ хоста c адресом/портом, по сокету - это ниочем
Мясо всегда наращивается на скилет ) Сначала стулья... :)
 
Мы в соцсетях:  ТелеграмВконтактеДзенФейсбукТвиттерЮтуб