• [ Регистрация ]Открытая и бесплатная
  • Tg admin@ALPHV_Admin (обязательно подтверждение в ЛС форума)

Статья Обратный enumeration. Изучаем атакующих по их трафику

stihl

bot
Moderator
Регистрация
09.02.2012
Сообщения
1,517
Розыгрыши
0
Реакции
888
Deposit
0.228 BTC
stihl не предоставил(а) никакой дополнительной информации.
В арсенале безопасников — множество инструментов для анализа сетевой активности и выявления атак, но иногда можно обойтись без них и получить не менее впечатляющие результаты. В этой статье мы разберем, как по одному лишь сетевому трафику восстановить портрет атакующего: понять, откуда он пришел, какой техникой пользуется и как ведет разведку.
Мы проследим, какие сигналы выдает сам хакер, как их интерпретировать и почему подобные знания важны не меньше, чем умение вовремя заметить попытку взлома.

Enumeration — это искусство сбора информации с целей. Способность узнать достаточно неочевидные вещи. Крупицы информации, которые потом складываются в общую картину и формируют перед атакующим ту самую поверхность атак, над которой ему затем и предстоит работать.


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

В сетевом трафике почти каждой операционной системы скрыто множество секретов. И в глубине сетевых пакетов можно отыскать очень интересные поля, способные раскрыть массу неочевидных сведений об источнике трафика, о которых мало кто догадывается...


Узнаём, что ищет хакер​

Любая таргетированная хакерская атака всегда начинается с разведки. И первое, с чем столкнется целевая система при попытке проникновения, — это сканирование сетевых портов. Но само по себе сканирование портов бесполезно без идентификации сервисов, прослушивающих эти самые порты. Ведь именно в этих сервисах хакеру и предстоит искать уязвимости.

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

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

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

info​

Эта статья основана на фрагменте книги «Для просмотра ссылки Войди или Зарегистрируйся», которая сейчас готовится к публикации.
Итак, давай напишем простейший TCP- или UDP-листенер, который будет просто слушать указанный порт и молча принимать данные. Базу данных Nmap, по которой мы будем находить соответствие между принятыми данными и тем или иным протоколом, мы можем парсить следующим незамысловатым образом:

defence/proto.py​

import socket
import difflib

probes = {}
with open("/usr/share/nmap/nmap-service-probes") as f:
#Для просмотра ссылки Войди или Зарегистрируйся
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_match

s = 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-geolite2
geoip = geolite2.reader()
result = geoip.get(IP)
country = result['country']['names']['ru']
city = result['city']['names']['ru']

from ipwhois import IPWhois #pip3 install ipwhois
result = 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 struct

def 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-p0f

def p0f(packet):
os = ""; ver = ""
try:
(,,os,ver),, = scapy_p0f.p0f(packet)
except:
pass

sniff(iface=iface, prn=p0f, store=0)
Чрезвычайно просто!

Однако, чтобы сигнатуры p0f заработали, им требуется полноценная TCP-сессия. Если же хакер производит сканирование TCP-SYN, не открывая полноценное соединение, информации в его TCP/IP-пакетах будет недостаточно. Но поле IP.ttl, которое есть в каждом пакете, может позволить определить тип ОС, пусть и на самом базовом уровне: 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, которое у большинства операционных систем имеет глобальный инкрементальный характер. Иными словами, компьютер хакера, отправляя сетевые пакеты своим целям, может автоматически увеличивать это поле на единицу с каждым пакетом. Анализируя это поле в принимаемых от хакера пакетах, мы сможем видеть, сколько пакетов он отправляет куда‑то еще, кроме нас. Иными словами, мы можем заключить, таргетированная ли атака или веерная на множество хостов.

Узнать, насколько изменилось поле IP.id в каждом принимаемом пакете, достаточно легко: важно лишь сохранять предыдущий услышанный пакет и производить вычитание:

defence/ip_id.py​

from scapy.all import *

delta_ids = {}
def get_delta_id(packet):
delta = 0
src_ip = packet[IP].src
if delta_ids.get(src_ip):
delta = abs(packet[IP].id - delta_ids[src_ip])
delta_ids[src_ip] = packet[IP].id
return delta

def analyze(packet):
if IP in packet:
if packet[IP].dst == own_addr: # only incoming
ip = packet[IP].src
delta_id = get_delta_id(packet)
...

own_addr = get_if_addr(iface)
sniff(iface=iface, prn=analyze, filter=filter, store=0)
Для большей информативности в код сниффера мы можем включить уже известные нам приемы с идентификацией ОС, а также GeoIP- и Whois-резолвы. То, что мы можем увидеть, показано ниже.

Для просмотра ссылки Войди или Зарегистрируйся
Мы видим здесь пример, где первый источник трафика имеет слабый инкремент, свидетельствующий, что практически никуда, кроме нашего узла, он пакеты не посылает. Это говорит о вероятно таргетированной атаке, то есть он атакует в текущий момент только нас. Второй же узел, помимо нас, успевает отправить от нескольких сотен до тысячи пакетов — это говорит о вероятной веерной атаке сразу на множество узлов. Но это было бы более справедливо для Linux-машины, мы же видим, что источник атаки — узел Windows. Более вероятно, что хакер в фоне слушает музыку либо его винда традиционно качает обновления.

info​

Стоит отметить, что подобное поведение ОС плавно уходит в прошлое. Новые версии Windows и Linux все чаще имеют локально инкрементируемый IP.id либо же вовсе случайный.

Вычисляем источник атаки​

В IP-слое каждого сетевого пакета есть поле TTL, которое уменьшается на единицу каждый раз, когда пакет проходит через сетевое устройство (коммутатор). К счастью, мы не связаны напрямую с хакером, нас разделяют десятки сетевых устройств, расположенных в разных уголках планеты. Таким образом, анализируя значение этого поля в принимаемых от хакера пакетах, мы как минимум можем заключить, как далеко он от нас находится. Такой прием лежит в основе трассировки (команды tracert, traceroute).

Но еще одним хитрым приемом мы можем вычислить, где именно расположен хакер: на узле с белым IP (например, выделенный сервер) либо где‑то в глубине локальной сети за NAT или домашним роутером (обычная рабочая станция). Все, что нужно, чтобы это узнать, — выполнить простой ping на источник трафика и сравнить разницу IP.ttl у входящего и исходящего пакетов. Если пакет по направлению к нам прошел больший путь (его TTL уменьшился больше), значит, видимый нами IP-адрес источника не фактический адрес хакера, а лишь NAT- или VPN-шлюз. Если же дистанции туда и обратно полностью совпали, значит, полученный IP-адрес — это истинный источник трафика.

Следующий скрипт делает всю эту «магию». Он отправляет на каждый источник входящего трафика один ping-запрос, позволяющий понять расстояние до него в хопах. Далее это расстояние сверяется с фактическим у входящих пакетов.

defence/nat.py​

from scapy.all import *

def get_distance(ttl):
if 255 >= ttl and ttl > 128:
return 255 - ttl
elif 128 >= ttl and ttl > 64:
return 128 - ttl
elif 64 >= ttl:
return 64 - ttl

delta_ttl_cache = {}
def get_delta_ttl(packet):
src_ip = packet[IP].src
if src_ip in delta_ttl_cache:
return delta_ttl_cache[src_ip]
distance_in = get_distance(packet[IP].ttl)
try:
distance_out = get_distance( sr1(IP(dst=src_ip)/ICMP()/b"probeprobeprobe", timeout=1)[IP].ttl )
delta_ttl_cache[src_ip] = distance_in-distance_out
return distance_in-distance_out
except Exception as e:
return 0

def analyze(packet):
if IP in packet:
if packet[IP].dst == addr: # incoming
ip = packet[IP].src
distance = get_distance(packet[IP].ttl)
delta_ttl = get_delta_ttl(packet)
...

sniff(iface=iface, prn=analyze, filter=filter, store=0)
Теперь мы видим, насколько далеко или близко находится от нас каждый источник трафика и сколько сетевых устройств нас разделяет. Еще можно увидеть, что часть трафика исходит из‑за NAT, причем кое‑где источник расположен достаточно глубоко.

Для просмотра ссылки Войди или Зарегистрируйся
Глубина расстояния за NAT может свидетельствовать о размере локальной сети, в которой расположено устройство, посылающее нам трафик. Нередко такие источники являются не хакерами, а их жертвами — компьютерами, зараженными вирусами и пытающимися заразить другие машины. Так что тут мы можем по имени сети увидеть те самые компании, что когда‑то были успешно атакованы.


Идентифицируем ПК атакующих​

В протоколе TCP скрыто одно очень интересное поле — timestamp, по которому можно рассчитать время, когда была загружена ОС. И что еще интереснее, эту опцию ОС зачастую указывает сама, даже если ее об этом явно не просят. Такая информация встречается не только при исходящих подключениях, но и при входящих. То есть большинство тех узлов, что посылают нам трафик и как‑то пытаются атаковать, бессознательно разглашают в TCP-пакетах свои uptime.

Не на всех типах ОС это поле фактически совпадает с временем загрузки, но важно другое — оно постоянное и достаточно уникально для каждого компьютера. А значит, оно, подобно уникальному хешу, способно выделить трафик произвольного ПК из общей массы, вне зависимости от его IP!

Чтобы получить эту информацию, не нужно ничего делать — нужно лишь слушать:

defence/uptime.py​

#!/usr/bin/python3
from scapy.all import *
from geolite2 import geolite2 #pip3 install maxminddb-geolite2
from ipwhois import IPWhois #pip3 install ipwhois
import scapy_p0f #pip3 install scapy-p0f
from netaddr import IPNetwork
import datetime
import time
from sys import argv

iface = argv[1]
filter = argv[2] if len(argv) > 2 else ""
addr = get_if_addr(iface)

grey_A = IPNetwork("10.0.0.0/8")
grey_B = IPNetwork("172.16.0.0/12")
grey_C = IPNetwork("192.168.0.0/16")
def is_grey(ip):
return ip in grey_A or ip in grey_B or ip in grey_C

geoip = geolite2.reader()
geoip_cache = {}
def geoip_lookup(ip):
if ip in geoip_cache:
return geoip_cache[ip]
LANG = 'ru'
country = ""; city = ""
result = geoip.get(ip)
if result:
country = result['country']['names'][LANG] if 'country' in result else ''
city = result['city']['names'].get(LANG) if 'city' in result else ''
geoip_cache[ip] = {"country": country or "", "city": city or ""}
return {"country": country or "", "city": city or ""}

whois_cache = {}
def whois_lookup(ip):
if ip in whois_cache:
return whois_cache[ip]
result = IPWhois(ip).lookup_whois()
netname = result['nets'][0]['name']
descr = result['nets'][0]['description']
whois_cache[ip] = {"netname": netname, "descr": descr}
return {"netname": netname, "descr": descr}

def p0f(packet):
os = ""; ver = ""
try:
(,,os,ver),, = scapy_p0f.p0f(packet)
except:
pass
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

uptimes = {}
def parse(packet):
global uptimes
if IP in packet and packet[IP].dst == addr and TCP in packet:
#print(packet[IP].src, packet[TCP].options)
for option in packet[TCP].options:
if option[0] == 'Timestamp':
boot_timestamp = option[1][0] / 1000 #HZ
boot_time = datetime.datetime.utcfromtimestamp(time.time() - boot_timestamp).strftime('%Y-%m-%d %H:%M')
ip = packet[IP].src
if not ip in uptimes: uptimes[ip] = []
if not boot_time in uptimes[ip]:
os,ver = p0f(packet)
if not is_grey(ip):
geoip = geoip_lookup(ip)
whois = whois_lookup(ip)
else:
geoip = {"country": "", "city": "",}
whois = {"netname": "intranet", "descr": ""}
print("{os} {country} {city} {netname} {src}: {uptime}".format(
os=f"{os}{ver}",
country=geoip["country"],
city=geoip["city"],
netname=whois["netname"],
src=ip,
uptime=boot_time
))
uptimes[ip].append(boot_time)
break

sniff(iface=iface, prn=parse, filter=filter, store=0)
Теперь в трафике мы сможем видеть уникальные черты большинства ПК. И с этой информацией мы можем, например, различать IP-адрес и фактический источник атаки. То есть понять, реально этот узел вредоносен или это лишь шлюз. Для этого нужно сравнить uptime у входящего подключения с uptime, который мы запросим у соответствующего IP.

Для просмотра ссылки Войди или Зарегистрируйся
Здесь видно, что uptime полностью совпал. Это означает, что активность исходит непосредственно с этого IP. Учитывая, что сеть явно провайдерская, это не может быть выделенный сервер, следовательно, рассматриваемый узел — это либо ноутбук на Linux, в который напрямую воткнули интернет‑кабель и начали атаковать с него, либо это зараженный роутер.

В другом случае ситуация иная — uptime у входящего подключения не совпал с фактическим uptime узла.

Для просмотра ссылки Войди или Зарегистрируйся
Это означает, что рассматриваемый узел не является фактическим источником атаки — он только шлюз и пересылает через себя чьи‑то пакеты. Имя сети указывает на то, что это обычный хостинг. Учитывая, что фингерпринты различаются, хакер использует этот сервер в качестве VPN, а не прокси, так как сервер пересылает чужие пакеты целиком. Кстати, определять NAT мы научились также другим способом в предыдущем разделе.

Еще мы можем увидеть, сколько фактических источников трафика находится за IP-адресом. Помониторив какое‑то время трафик с этого узла, можно заметить минимум три разных uptime, приходящих с него.

Для просмотра ссылки Войди или Зарегистрируйся
Иными словами, этим VPN пользуется минимум три машины или, возможно, три человека.

Следующая возможность — понять, что те или иные разные IP-адреса — это фактически один и тот же компьютер. Наконец, помониторив трафик более продолжительное время, мы можем заметить, что uptime одной из машин за VPN-сервером внезапно совпал с uptime одной из машин, чей трафик пришел из провайдерской домашней сети.

Для просмотра ссылки Войди или Зарегистрируйся
Только что мы сдеанонили атакующего, который по неосторожности допустил утечку сетевых пакетов вне VPN. Несмотря на то что никакие видимые компрометирующие данные хакер явно не посылал, всего одного пакета хватило, чтобы сопоставить его VPN-активность с домашним IP.

И это хороший пример, почему proxy более анонимен, нежели VPN. Хоть VPN и удобнее для хакера, он не делает развязки по сетевому стеку, позволяя проводить такой фингерпринт. А учитывая вопрос удобства и потенциальных сложностей со сканированием портов, более вероятно, что хакер будет использовать VPN, подверженный таким утечкам, чем proxy.


Заключение​

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

В итоге мы смогли добыть о хакере следующую информацию:

  • его географическое расположение и провайдера (или хостинг);
  • версию или тип его операционной системы;
  • веерно или таргетированно он атакует;
  • является ли IP-адрес реальным источником атаки или только шлюзом;
  • сколько фактически атакующих скрыто за IP;
  • какие фактические IP-адреса использовал атакующий.
Знай мы хоть в десять раз больше информации, это вряд ли помогло бы нам в борьбе с хакером. Ведь хакер физически может быть на противоположном конце земли, куда у нас нет никаких рычагов воздействия. Не сильно поможет нам и блокировка его по IP — очень целеустремленный хакер будет менять IP-адрес столько раз, сколько ему потребуется.

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

Полный код скриптов из этой статьи можно найти Для просмотра ссылки Войди или Зарегистрируйся.
 
Activity
So far there's no one here
Сверху Снизу