×
Главная   »   Статьи   »   Протокол TCP: SYN-, FIN-, NULL-, ACK-, XMAS-сканирования и выявление признаков последнего
Протокол TCP: SYN-, FIN-, NULL-, ACK-, XMAS-сканирования и выявление признаков последнего
Метки:      ,   ,   ,   ,   ,   ,   ,   

В этой статье продолжим изучение протоколов, разберемся как осуществляется SYN-, FIN-, NULL-, XMAS-, ACK-сканирования и закрепим весь теоретический материал на практике. После того как научимся сканировать, реализуем анализатор, который будет выявлять XMAS-сканирование.

Автор статьи никого не призывает к правонарушениям и отказывается нести ответственность за ваши действия. Вся информация предоставлена исключительно в ознакомительных целях. Все действия происходят на виртуальных машинах и внутри локальной сети автора. Спасибо!

В этой статье продолжим изучение протоколов, разберемся как осуществляется SYN-, FIN-, NULL-, XMAS-, ACK-сканирования и закрепим весь теоретический материал на практике. После того как научимся сканировать, реализуем анализатор, который будет выявлять XMAS-сканирование.

Из этой статьи вы не только узнаете про все основные виды сканирования целевой машины и научитесь их с нуля разрабатывать на Python, но и начнете более осознано использовать флаги -sS, -sN, -sF, -sX, -sA самой используемой сетевой утилиты nmap.

Объяснить, зачем вам в этом так подробно разбираться я не смогу лучше, чем автор документации по nmap в первом абзаце. Вот этот фрагмент: «Как новичок в автомобильном деле, я могу часами биться в попытках использовать свои элементарные инструменты (молоток, клейкая лента, гаечный ключ и т.д.) для решения какой-либо проблемы. Когда все мои попытки с крахом проваливаются, и я буксирую свою развалюху к настоящему механику, он неизменно достает из большой коробки с интрументами какую-нибудь штуковину, и сразу складывается впечатление, что решение проблемы не требует много усилий. Искусство сканирования портов очень на это похоже. Эксперты понимают дюжины различных приемов сканирования портов и выбирают для конкретной задачи подходящий (или комбинацию из нескольких). Неопытные пользователи и script kiddies, пытаются решить все задачи с помощью используемого по умолчанию SYN сканирования. Т.к. Nmap является бесплатной, то единственным барьером на пути к овладению техникой сканирования портов является знание. Это все же лучше, чем в мире автомобилей, где, когда вам наконец-то удается определить, что вам необходимо какое-либо устройство, вам еще надо будет заплатить за него тысячу долларов.».

Скучная теория SYN-сканирования

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

Если порт на целевой машине открыт, с него придет пакет SYN-ACK. Это нам нужно знать, чтобы определить состояние порта, поэтому позже к этому вернемся. После чего сканер отвечает пакетом RST и тем самым закрывает соединение. Схема для наглядности:

Реализуем SYN-сканер

Для написания простого сканера будем использовать Python с модулем scapy, поэтому импортируем все что нам нужно:

import sys
from scapy.all import IP, ICMP, TCP, sr1

Протоколы IP и TCP, думаю понятно, для чего нам нужны. Протокол ICMP пригодится, чтобы проверить хост, а функция sr1() нужна для отправки и приема только одного пакета. Теперь напишем функцию icmp_check(), которая принимает IP-адрес и возвращает булевое значение, означающее его состояние. Если True, то хост подключен к сети:

def icmp_check(ip):
    icmp_packet = IP(dst=ip) / ICMP()
    response_packet = sr1(icmp_packet, timeout=10, verbose=0)
    return response_packet != None

Разберемся с кодом. Сначала формируем ICMP пакет используя оператор / для объединения информации разных слоев и изменения значений по умолчанию. Далее отправляем пакет и получаем ответ. Если ответ есть, то хост подключен к сети. Кстати, verbose=0 мы указываем, чтобы в консоль не отправлялась служебная информация, которая захламляет вывод. Теперь реализуем функцию syn_scan(), которая отправляет пакет с флагом SYN.

def syn_scan(ip, port):
    syn_request = IP(dst=ip) / TCP(dport=port, flags='S') 
    syn_response = sr1(syn_request, timeout=10, verbose=0)
    return syn_response

Принцип тот же, только мы переопределяем в коде флаг пакета TCP на S, что означает установку SYN флага у TCP пакета. Кстати, название полей можно посмотреть в документации класса scapy.layers.inet.TCP. Теперь напишем оставшуюся часть программы:

if __name__ == '__main__':
    ip = sys.argv[1]
    port_start = 1
    port_end = 23

    if icmp_check(ip):
        for p in range(port_start, port_end + 1):
            syn_response = syn_scan(ip, p)
            # print(syn_response.show())
            if str(type(syn_response)) != "<class 'NoneType'>":
                if syn_response.getlayer('TCP').flags == 0x12:
                    print(f'Port {p} open')
                else:
                    print(f'Port {p} closed')
            else:
                print(f'Port {p} closed')

IP-адрес мы получаем из первого аргумента, а диапазон портов для проверки пока зададим вручную. Далее проверяем, подключен ли хост к сети и сканируем каждый порт при помощи нашей функции syn_scan() и сохраняет ответ в переменную syn_response. Чтобы посмотреть сами пакеты, можете используйте функцию show():

syn_response.show()

Нас интересует только флаг TCP-пакета, значение которого можно получить так:

syn_response.getlayer('TCP').flags

Итак, почему мы сравниваем флаг со значением 0x12. Как оно получилось? Если установлен лишь флаг SYN, то флаг у TCP-пакета будет 0x02. Если только флаг ACK, то значение будет 0x10. Если оба, то 0x12. Вот на форуме про это спрашивали, можете посмотреть более подробные объяснения. Запускаем наш сканер и смотрим результат:

python3 syn_scanner.py 192.168.1.101

Для наглядности я запустил Wireshark и просканировал один открытый порт. Сначала машины обменялись ARP-пакетами, про которые мы подробно говорили в статье про ARP-спуфинг. Далее происходит обмен ICMP-пакетами, чтобы убедиться о наличии подключения хоста к сети. После чего отправляем TCP-пакет с флагом SYN и получаем ответ SYN, ACK. В конце разрываем соединение.

Если порт закрыт, то после отправки TCP-пакета с флагом SYN мы получим в ответ RST, ASK:

Скучная теория FIN-сканирования (у NULL-, XMAS- алгоритм идентичный)

Некоторые серверы отслеживают SYN-сканирование портов и разрывают соединение для защиты от сканирования. Согласно RFC 793 «TRANSMISSION CONTROL PROTOCOL», на поступивший пакет с флагом FIN на закрытый порт сервер должен ответить пакетом с флагом RST. Если на открытый порт, то он должен быть проигнорирован. RFC – это рекомендация и не все операционные системы ей придерживаются.

Реализуем FIN-сканер

Перенесем часть кода SYN-сканера в функцию syn_scan(), добавим второй аргумент, который позволит выбирать тип сканирования и напишем функцию fin_scan(). В итоге у нас получился следующий код:

...
def fin_scan(ip, port):
    fin_request = IP(dst=ip) / TCP(dport=port, flags='F') 
    fin_response = sr1(fin_request, timeout=3, verbose=0)

    if str(type(fin_response)) == "<class 'NoneType'>":
        return f'Port {p} open or filtered'
    else:
        if fin_response.haslayer('TCP'):
            if fin_response.getlayer('TCP').flags == 0x14:
                return f'Port {p} closed'
        elif fin_response.haslayer('ICMP'):
            if(int(fin_response.getlayer('ICMP').type) == 3 
                and int(fin_response.getlayer('ICMP').code) 
                in [1, 2, 3, 9, 10, 13]):
                return f'Port {p} filtered'

if __name__ == '__main__':
    ip = sys.argv[1]
    # s - SYN, f - FYN, x - XMAS
    mode = sys.argv[2]
    port_start = 1
    port_end = 23

    if icmp_check(ip):
        for p in range(port_start, port_end + 1):
            if mode == '-s':
                print(syn_scan(ip, p))
            elif mode == '-f':
                print(fin_scan(ip, p))

Он далек от изящества и емкости, но выполняет свои функции. Давайте запустим его:

python3 ports_scanner.py 192.168.1.101 –f

Как видите, при таком сканировании нельзя определить наверняка, открыт порт или он фильтруется. Если в ответ приходит ICMP ошибка (тип 3, код 1, 2, 3, 9, 10 или 13), то порт фильтруется. По такой же схеме работают NULL- и XMAS-сканирования, а отличаются лишь флагами TCP-пакета.

NULL- и XMAS-сканирования

Думаю, нет смысла подробно останавливаться на NULL- и XMAS-сканированиях, так как они работают, как и FIN-сканирование, только в первом случае поле с флагом остается пустым, а при XMAS-сканировании флаг TCP-пакета должен быть равен FPU (PSH, FIN, URG).

ACK-сканирование

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

Реализация ACK-сканирования на Python

Теперь реализуем этот тип сканирования на Python. Напишем функцию ack_scan(). В ней будет все тоже самое, что мы уже ранее обсуждали, только в этот раз проверяем TCP пакет на наличие флага R, который равен 0x4, чтобы определить не фильтруемый порт. Далее представлен весь код сканера, который мы вместе написали:

import sys
from scapy.all import IP, ICMP, TCP, sr1

def icmp_check(ip):
    icmp_packet = IP(dst=ip) / ICMP()
    response_packet = sr1(icmp_packet, timeout=10, verbose=0)
    return response_packet != None

def send_packet(ip, port, flag):
    request = IP(dst=ip) / TCP(dport=port, flags=flag) 
    return sr1(request, timeout=3, verbose=0) 

def check_icmp_error(r):
    if r.haslayer('ICMP'):
        if(int(r.getlayer('ICMP').type) == 3 
            and int(r.getlayer('ICMP').code) 
            in [1, 2, 3, 9, 10, 13]):
            return True
    return False

def syn_scan(ip, port):
    response = send_packet(ip, port, 'S')

    if str(type(response)) != "<class 'NoneType'>":
        # 0x12 = SA
        if response.getlayer('TCP').flags == 0x12:
            return f'Port {p} open'
        else:
            return f'Port {p} closed'
    else:
        return f'Port {p} closed'

def fin_null_xmas_scan(ip, port, flag):
    response = send_packet(ip, port, flag)

    if str(type(response)) == "<class 'NoneType'>":
        return f'Port {p} open or filtered'
    else:
        if response.haslayer('TCP'):
            # 0x14 = RA
            if response.getlayer('TCP').flags == 0x14:
                return f'Port {p} closed'
        elif check_icmp_error(response):
            return f'Port {p} filtered'


def ack_scan(ip, port):
    response = send_packet(ip, port, 'A')

    if str(type(response)) == "<class 'NoneType'>":
        return f'Port {p} filtered'
    else:
        if response.haslayer('TCP'):
            # 0x4 = R
            if(response.getlayer(TCP).flags == 0x4):
                return f'Port {p} unfiltered'
        elif check_icmp_error(response):
            return f'Port {p} filtered'


if __name__ == '__main__':
    ip = sys.argv[1]

    # s - SYN, f - FYN, n - NULL, x - XMAS, a - ACK
    mode = sys.argv[2]

    port_start = int(sys.argv[3])
    port_end = int(sys.argv[4])

    if icmp_check(ip):
        for p in range(port_start, port_end + 1):
            result_scan_port = ''

            if mode == '-s':
                result_scan_port = syn_scan(ip, p)
            elif mode == '-f':
                result_scan_port = fin_null_xmas_scan(ip, p, 'F')
            elif mode == '-n':
                result_scan_port = fin_null_xmas_scan(ip, p, '')
            elif mode == '-x':
                result_scan_port = fin_null_xmas_scan(ip, p, 'FPU')
            elif mode == '-a':
                result_scan_port = ack_scan(ip, p)

            print(result_scan_port)

Для запуска скрипта откройте терминал и укажите нужные параметры (-f — вид сканирования, 1 — начальный порт, 23 — конечный порт):

python3 ports_scanner.py 192.168.1.101 –f 1 23

Выявление признаков XMAS-сканирования

В статье про перехват трафика при помощи ARP-спуфинга мы уже поднимали тему анализа пакетов. Воспользуемся нашими знаниями и постараемся обнаружить XMAS-сканирование нашей системы.

Как вы узнали из этой статьи, XMAS-сканирование выдает себя из-за наличия в пакете TCP флагов PSH, FIN, URG. Так-то мы и определим «вероятное» XMAS-сканирование нашего хоста.

import os
from scapy.all import sniff

def process(packet):
    # print(packet.show())
    ip_dst = packet['IP'].dst
    flags = packet['TCP'].flags

    if ip_dst == host:
        if flags == 'FPU':
            print(f'Perhaps XMAS port {packet["IP"].dport} \
            scanning from the host {packet["IP"].src}:{packet["IP"].sport}')

host = '192.168.1.100'

sniff(count=0, filter='tcp', store=0, prn=process)

Все что нам нужно, это отфильтровать только TCP пакеты и при нахождении в них флага FPU, написать в консоль о возможном XMAS-сканировании. Теперь запустите скрипт и попробуйте просканировать с другого компьютера при помощи утилиты nmap:

nmap -sX 192.168.1.100

Выводы

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

1346 просмотров
07.03.2023
Автор