Содержание статьи
Мы проследим, какие сигналы выдает сам хакер, как их интерпретировать и почему подобные знания важны не меньше, чем умение вовремя заметить попытку взлома.
Enumeration — это искусство сбора информации с целей. Способность узнать достаточно неочевидные вещи. Крупицы информации, которые потом складываются в общую картину и формируют перед атакующим ту самую поверхность атак, над которой ему затем и предстоит работать.
Традиционно enumeration считался хакерским ремеслом. Но можем ли мы применить его в обратную сторону — против хакеров? Встречная разведка атакующего может оказаться весьма полезной в определенных обстоятельствах. И в этой статье мы, подобно Шерлоку Холмсу, составим примерный портрет атакующего, опираясь лишь на особенности его трафика.
В сетевом трафике почти каждой операционной системы скрыто множество секретов. И в глубине сетевых пакетов можно отыскать очень интересные поля, способные раскрыть массу неочевидных сведений об источнике трафика, о которых мало кто догадывается...
Узнаём, что ищет хакер
Любая таргетированная хакерская атака всегда начинается с разведки. И первое, с чем столкнется целевая система при попытке проникновения, — это сканирование сетевых портов. Но само по себе сканирование портов бесполезно без идентификации сервисов, прослушивающих эти самые порты. Ведь именно в этих сервисах хакеру и предстоит искать уязвимости.
С помощью отправки особых сетевых пакетов хакер может вынуждать те или иные службы отвечать соответствующим образом, тем самым выдавая тип службы или протокол.

В этой области все неизменно уже несколько десятилетий, и с этой задачей все еще прекрасно справляется сетевой сканер Nmap, имеющий богатую базу фингерпринтов.

Но что, если мы вооружимся этой же самой базой и будем сверять все приходящие сетевые пакеты и определять, на какой протокол или службу они больше всего похожи? Так мы сможем понять, что пытается найти хакер.
info
Эта статья основана на фрагменте книги «Хакерская самооборона. Приемы обнаружения и предотвращения хакерских атак», которая сейчас готовится к публикации.
Итак, давай напишем простейший TCP- или UDP-листенер, который будет просто слушать указанный порт и молча принимать данные. Базу данных Nmap, по которой мы будем находить соответствие между принятыми данными и тем или иным протоколом, мы можем парсить следующим незамысловатым образом:
defence/proto.py
import socketimport difflibprobes = {}with open("/usr/share/nmap/nmap-service-probes") as f: #https://github.com/boy-hack/nmap-parser for line in f.readlines(): line = line.strip() if line.startswith("Probe "): protocol = line[6:9] if protocol not in ["TCP", "UDP"]: continue probename_start = 10 probename_end = line.index(" ", probename_start) if probename_end - probename_start <= 0: continue probename = line[probename_start:probename_end] probestring_start = line.index("q|", probename_end) + 1 probestring = line[probestring_start:].strip("|") probes[probename] = probestring.encode().decode('unicode-escape').encode()def get_nmap_probe(buf): best_match = 0 probename = None for probe in probes: matcher = difflib.SequenceMatcher(a=probes[probe], b=buf) match = matcher.find_longest_match(0, len(matcher.a), 0, len(matcher.b)) if match.size/len(buf) > best_match: best_match = match.size/len(buf) probename = probe #matcher.a[match.a:match.a+match.size] return probename,best_matchs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)s.bind(("0.0.0.0", int(port)))s.listen(10)while True: c,info = s.accept() try: buf = c.recv(1024) probe,match = get_nmap_probe(packet) except: pass c.close()При этом мы должны понимать, что стопроцентного соответствия между принимаемыми байтами может и не быть. Поэтому искать будем то, что наиболее похоже на тот или иной сетевой пакет, и выводить результат в процентном соотношении. Включим также в код GeoIP и Whois:
from geolite2 import geolite2 #pip3 install maxminddb-geolite2geoip = geolite2.reader()result = geoip.get(IP)country = result['country']['names']['ru']city = result['city']['names']['ru']from ipwhois import IPWhois #pip3 install ipwhoisresult = IPWhois(IP).lookup_whois()netname = result['nets'][0]['name']descr = result['nets'][0]['description']В итоге, если этот листенер кто‑то просканирует, мы увидим все его попытки обнаружения тех или иных протоколов.

Достаточно высокая степень сходства говорит о том, что сканировали нас, скорее всего, с помощью Nmap либо похожим сканером, использующим ту же базу фингерпринтов.
До сих пор мы слушали один порт, но хакеры сканируют множество портов. С помощью следующих правил межсетевого экрана мы можем завернуть все неиспользуемые сетевые порты в этот листенер:
iptables -t nat -I PREROUTING -i eth0 -p tcp --dport 22 -j REDIRECT --to-ports 22 # Исключениеiptables -t nat -A PREROUTING -i eth0 -p tcp -m conntrack --ctstate NEW -j REDIRECT --to-ports 1234
После того как мы изменили порт назначения и перенаправили подключение на листенер, нам нужно восстановить исходный номер порта. Определить исходный номер порта на сокете, что был до редиректа, можно с помощью такой функции:
import structdef get_original_dst(sock): try: sockaddr_in = sock.getsockopt(socket.SOL_IP, 80, 16) (proto, port, a,b,c,d) = struct.unpack("!HHBBBB", sockaddr_in[:8]) dst_ip = "%d.%d.%d.%d" % (a,b,c,d) dst_port = port return (dst_ip, dst_port) except: passПосле чего мы почти в ту же секунду начнем видеть, какие службы и протоколы пытаются найти на нашем сервере уже на других портах.

Это может показаться удивительным — как минуту назад еще закрытые порты теперь стали кем‑то исследоваться. Именно так и выглядит темный трафик в интернете.
Однако большая часть сетевой активности, с которой можно столкнуться, — это не реальные хакеры, а лишь боты — программные роботы, ищущие что‑то в интернете. Возможно, именно они потом приведут уже реальных хакеров, если те найдут на нашем периметре нечто интересное и перспективное для проникновения.
Большая часть хакеров ломает то, что ломается. Иными словами, работают в ширину и ищут уязвимые периметры, которые можно легко взломать. А не наоборот, когда изначально выбирают компанию и потом ищут уязвимости в ней, работая тем самым в глубину. Исключение составляют лишь таргетированные атаки, когда хакер реально заинтересован во взломе конкретной компании. Такие злоумышленники, скорее всего, просканируют все 65 535 портов на всех твоих серверах, так что у тебя еще будет возможность заметить это.
Узнаём ОС атакующего
В предыдущем разделе для наблюдения за трафиком мы использовали листенер. Теперь пришла пора разработать свой миниатюрный сниффер. Сниффер позволит нам видеть уже весь сетевой трафик, который приходит на нашу систему. И к тому же мы сможем видеть не только полезные данные, но и весь сетевой пакет, включая транспортный и сетевой протоколы. С этого момента мы приступаем к анализу строения сетевых пакетов.
Анализ особых полей в TCP- или IP-слоях пакета может рассказать очень многое об источнике трафика. Например, анализируя размеры окна (поле window) в TCP-слое пакета, можно с той или иной уверенностью определить тип и даже версию ОС источника трафика. С этой задачей прекрасно справляется утилита p0f, имеющая хорошую базу фингерпринтов сетевых стеков. Встроить ее функциональность в код сниффера можно библиотекой‑оберткой следующим образом:
from scapy.all import *import scapy_p0f #pip3 install scapy-p0fdef p0f(packet): os = ""; ver = "" try: (_,_,os,ver),_,_ = scapy_p0f.p0f(packet) except: passsniff(iface=iface, prn=p0f, store=0)Чрезвычайно просто!
Однако, чтобы сигнатуры p0f заработали, им требуется полноценная TCP-сессия. Если же хакер производит сканирование TCP-SYN, не открывая полноценное соединение, информации в его TCP/IP-пакетах будет недостаточно. Но поле IP., которое есть в каждом пакете, может позволить определить тип ОС, пусть и на самом базовом уровне: Linux, Windows или Cisco. Дело в том, что у каждого из этих семейств ОС есть свое начальное значение TTL: 64, 128 и 255 соответственно. Так что если мы добавим эту проверку в функцию p0f(, то будем получать тип ОС для любого входящего пакета:
...if not os and IP in packet: if packet[IP].ttl <= 64: os = "Linux" elif 64 < packet[IP].ttl <= 128: os = "Windows" elif 128 < packet[IP].ttl <= 255: os = "Cisco"return os, ver...В тех случаях, когда информации в сетевых пакетах достаточно, p0f определяет тип ОС даже с некоторой версией, в противном случае мы получим лишь семейство ОС.

Версия ОС даст нам начальное представление о том, кто нас атакует: более‑менее профи с Linux или новичок, запустивший Kali на виртуалке под Windows.
Определяем таргетированность атаки
Сетевой стек TCP/IP, который связывает атакующего с его целью, имеет несколько примечательных особенностей. По ним можно составить примерную картину об источнике атаки — откуда и как она исходит. Так, в IP-слое сетевого пакета есть поле Identifier, которое у большинства операционных систем имеет глобальный инкрементальный характер. Иными словами, компьютер хакера, отправляя сетевые пакеты своим целям, может автоматически увеличивать это поле на единицу с каждым пакетом. Анализируя это поле в принимаемых от хакера пакетах, мы сможем видеть, сколько пакетов он отправляет куда‑то еще, кроме нас. Иными словами, мы можем заключить, таргетированная ли атака или веерная на множество хостов.
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
