Как написать сканер портов на Python

Сканер портов

Существует множество различных ска­неров пор­тов, но нас­тоящий тру хакер дол­жен знать, как работа­ют подобные инструменты. Так что в этой статье я покажу, как написать сканер портов на Python.

Еще по теме: Сканер уязвимостей из Nmap с помощью Vulscan

Взаимодействие устройств в сети

Для взаимодействие подключенных к сети устройств  исполь­зуют­ся сокеты, которые легче изоб­разить ввиде тру­бы. Где концы трубы — это и есть наши сокеты. Один выход распологается на одном компе, дру­гой — на дру­гом, а приложения помещают или читают данные из этой «тру­бы».

Есть два вида сокетов:

  • TCP-сокет гаран­тиру­ет, что пакеты будут без потерь.
  • UDP-сокеты работа­ют на ско­рость, но с возможной потерей пакетов.

Кроме этого TCP можно сказать незащищенный, а UDP защишенный, но это не имеет значения в данном случае.

При передачи видеопотока испульзуется UDP. Если с каналом звязи будут какие-то временные проблемы, прос­то потеря­ются пакеты, а оставши­еся будут переданы в срок, и все рав­но будет хоть какая-то кар­тинка .

Как написать сканер портов на Python на сокетах

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

Для этого примера будут использовать Python 3.10 и мой любимый PyCharm.

Соз­дадим приложение и добавим основные модули:

Офи­циаль­ная докумен­тации поможет лучше разобраться с модулем socket.

За­пом­инаем вре­мя запус­ка приложения — в будущем это понадобится для определения вре­мени ска­ниро­вания.

Сохранят пары из названий сервисов и пор­тов можно пря­мо в коде. Если будет желании можете улучшить данный метод, до фай­лов JSON.

Пре­обра­зовываем передан­ный аргу­мент в IP-адрес. Для это­го подсунем пер­вый аргу­мент коман­дной стро­ки ска­нера портов фун­кции socket.gethostbyname(string) — как бонус получим раз­решение DNS, если переда­н не IP-адрес, а домен­ное имя.

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

В завершении сох­раним вре­мя окон­чания отобразим время ска­ниро­вания.

Те­перь, для проверки  работы ска­нера портов, открываем тер­минал, переходим в пап­ку со ска­нером и выпол­няем коман­ду:

Ес­ли вы работаете на Linux, исполь­зуйте коман­ду python3, что­бы ненароком не нар­вать­ся на старую версию Python.

Разумеется, вмес­то IP-адреса мож­но ука­зать любой хост.

Да­вайте пос­мотрим, как это выг­лядит со сто­роны сер­вера. Для это­го заюзаем Netcat. Ска­чиваем и запус­каем его таким обра­зом:

Имейте ввиду. Антивирь будет ругаться на Netcat, так как его час­то юзают хакеры для поднятия шелла на взломанных машинах.

В сосед­нем тер­минале запус­каем ска­нер портов:

В другом в окне с Netcat про­изой­дет это.

Сканер портов на Python
Netcat среагировал на ска­ниро­вание

На скрине вид­но, что было уста­нов­лено пол­ноцен­ное соеди­нение, скрытое сканирование не получит­ся.

Так­же давайте теперь попробуем прос­канировать сер­вер scanme.nmap.org:
Сканер портов Python
Ска­нер портов выявил два откры­тых пор­та — 22 и 80. Но вам придет идет прос­каниро­вать данный хост с помощью Nmap, вы увидите большее количество откры­тых пор­тов.
Сканер портов Питон
По­чему так происходит? Популярный Nmap про­ходит по боль­шему спис­ку пор­тов, чем наш ска­нер портов на Python, и, поэтому, находит боль­ше. Если добавить эти пор­ты в наш ска­нер, он тоже их отыщет.

По­нят­но, что такой ска­нер вряд ли при­меним в реаль­ных усло­виях, кро­ме сов­сем уж экзо­тичес­ких: нап­ример, ког­да ска­ниро­вать надо с машины, на которую невоз­можно дос­тавить пол­ноцен­ный ска­нер, но на которой уже есть Python.

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

Оптимизация сканера портов на Python

Что­бы сде­лать наш ска­нер более эффектив­ным и скрыт­ным, нуж­но не уста­нав­ливать соеди­нение, ведь это тре­бует целых три пакета (тре­хэтап­ное рукопо­жатие) плюс допол­нитель­ное вре­мя, что­бы зак­рыть соеди­нение. Но как узнать, открыт ли порт, не пыта­ясь под­клю­чить­ся?

Ока­зыва­ется, метод есть. Зак­люча­ется он в том, что необя­затель­но завер­шать все три эта­па рукопо­жатия — тог­да соеди­нение не будет уста­нов­лено, а зна­чит, не нуж­но будет и зак­рывать его.

Узнать, открыт ли порт, мож­но в отве­те на пер­вый же пакет, отправ­ленный сер­веру, так что здо­рово сэконо­мите вре­мя, если искусс­твен­но делать непол­ное рукопо­жатие.

Бонусом получа­ем скрыт­ность: если соеди­нение не уста­нов­лено, то прог­раммы на целевом хосте и не узна­ют, что их кто‑то ска­ниру­ет.

Как работа­ет тре­хэтап­ное рукопо­жатие? Сна­чала ини­циатор соеди­нения отправ­ляет дру­гой машине на инте­ресу­ющий порт пакет с фла­гом SYN. Если порт открыт, дол­жен прий­ти ответ с фла­гами SYN и ACK. Если порт зак­рыт, поведе­ние может быть раз­ным, но в нор­маль­ном слу­чае в отве­те дол­жен быть флаг RST.

Ес­ли ини­циатор еще не раз­думал откры­вать соеди­нение, он дол­жен отпра­вить еще один пакет, но уже толь­ко с фла­гом ACK, пос­ле чего соеди­нение будет счи­тать­ся уста­нов­ленным.

Клю­чевой момент тут в том, что отправ­лять финаль­ный ACK совер­шенно не обя­затель­но, так как узна­ем ста­тус пор­та мы уже пос­ле вто­рого пакета, а тре­тий заберет у нас дра­гоцен­ное вре­мя и лишит скрыт­ности.

Что­бы зак­рыть соеди­нение, нуж­но отпра­вить пакет с фла­гами FIN и ACK. Эта информа­ция нам сегод­ня не понадо­бит­ся и нуж­на толь­ко для более пол­ного понима­ния, что про­исхо­дит с TCP-соеди­нени­ем на раз­ных эта­пах его жиз­ни.

SYN-сканер на Python

Соз­даем новый скрипт на Python и импорти­руем нуж­ные модули:

Ес­ли вы получили ошиб­ку вида:

Зна­чит, нуж­но доус­тановить модуль scapy из PyPI:

В этот раз перед ска­ниро­вани­ем неп­лохо бы про­верить, дос­тупна ли целевая машина. Для это­го мож­но отпра­вить ICMP Echo Request (в прос­тонародье — пинг):

Те­перь напишем саму фун­кцию ска­ниро­вания. Она будет обхо­дить все пор­ты, слать SYN-пакеты и ожи­дать отве­та.

Порт будет счи­тать­ся откры­тым, если в ответном пакете уста­нов­лены фла­ги SYN и ACK (или хотя бы один из них).

Что­бы узнать, какой сер­вис соот­ветс­тву­ет какому пор­ту, в этот раз мы не будем изоб­ретать свой спи­сок, а вос­поль­зуем­ся готовым, который пре­дос­тавля­ет сам Scapy. Мож­но исполь­зовать фун­кцию resp_packet.show(), в выводе которой ука­зыва­ется сер­вис, соот­ветс­тву­ющий целево­му пор­ту.

Ос­талось все­го ничего: скор­мить фун­кции спи­сок пор­тов для про­вер­ки и реали­зовать ввод информа­ции о цели. Этим и зай­мет­ся фун­кция main. В этот раз сде­лаем инте­рак­тивный ввод адре­са цели и пред­варитель­ную про­вер­ку дос­тупнос­ти цели перед ска­ниро­вани­ем.

В мас­сив ports нуж­но записать пор­ты, которые вы собира­етесь ска­ниро­вать. Необя­затель­но переби­рать все: даже Nmap по умол­чанию ска­ниру­ет толь­ко топ-1000 самых популяр­ных, а осталь­ные усколь­зают и от него, если явно не зат­ребовать пол­ное ска­ниро­вание ( -p-).

На пос­ледней ите­рации цик­ла про­исхо­дит ошиб­ка (мас­сив закан­чива­ется), а зна­чит, мож­но исполь­зовать это событие, что­бы сооб­щить о завер­шении работы.

Пос­коль­ку мы теперь зап­рашива­ем адрес в самом скрип­те, его мож­но запус­кать без вся­ких парамет­ров.

Ес­ли сно­ва открыть Netcat и прос­каниро­вать его, то ска­нер покажет откры­тый порт, но никако­го под­клю­чения не слу­чит­ся. Про­вер­ка на скрыт­ность прой­дена!

При­мер работы SYN-ска­нера
При­мер работы SYN-ска­нера

Пол­ный код смот­рите в ре­пози­тории на GitHub. Там есть и доработ­ки, которые не были рас­смот­рены в статье, нап­ример гра­фичес­кий интерфейс на PyQt.

Те­перь давайте прос­каним какой‑нибудь пуб­личный сер­вер (тот же scanme.nmap.org) и с помощью Wireshark пос­мотрим, какие пакеты ходят. Запус­тим Wireshark, а затем и ска­ниро­вание.

Что­бы не запутать­ся и не уто­нуть в потоке информа­ции, который изверга­ет Wireshark, давайте при­меним филь­тр. Дос­таточ­но будет показать пакеты, которые отправ­лены или получе­ны от хос­та с IP 45.33.32.156.

Пос­ле при­мене­ния филь­тра окно Wireshark выг­лядит так.

Ре­зуль­таты филь­тра­ции
Ре­зуль­таты филь­тра­ции

Пер­вым в спис­ке видим ICMP-пакет (пинг), который отправ­лялся для про­вер­ки дос­тупнос­ти. Даль­ше идут пакеты с фла­гами RST/ACK (зак­рытые пор­ты) и SYN/ACK (откры­тые пор­ты).

Заключение

В статье рас­смот­рели два метода ска­ниро­вания: с уста­нов­лени­ем соеди­нения и без него. За рам­ками статьи оста­лось ска­ниро­вание пор­тов UDP, так как оно нам­ного слож­нее и нуж­но реже. Тем не менее Scapy поз­волит реали­зовать и его, если вам это вдруг понадо­бит­ся.

Не забывайте, что подоб­ные прог­раммы — это в пер­вую оче­редь опыт, а не замена готовым инс­тру­мен­там. Имен­но зна­ние того, как все устро­ено внут­ри, дела­ет вас нас­тоящим тру хакером!

РЕКОМЕНДУЕМ:

Дима (Kozhuh)

Эксперт в кибербезопасности. Работал в ведущих компаниях занимающихся защитой и аналитикой компьютерных угроз.

Добавить комментарий

  1. Christopher

    Hello . I need minute your attention.

    Ответить
  2. Стас

    Круто

    Ответить